Optimize code; memory-leaks, race-condition

Fix bugs like StackOverflow, Thread-Leak, or Race-Condition. Optimize code generally
This commit is contained in:
TDSTOS
2026-04-13 02:42:24 +02:00
parent f9dba5b10d
commit b526b85262
10 changed files with 99 additions and 88 deletions

View File

@@ -212,6 +212,7 @@ class SpeedHG : JavaPlugin() {
override fun onDisable()
{
podiumManager.cleanup()
gameManager.shutdown()
if ( ::lobbyAnnouncer.isInitialized ) lobbyAnnouncer.stop()
if ( ::perkManager.isInitialized ) perkManager.shutdown()
if ( ::tablistManager.isInitialized ) tablistManager.shutdown()

View File

@@ -151,7 +151,9 @@ class StatsManager(private val plugin: SpeedHG) {
} catch ( e: Exception ) {
plugin.logger.severe( "[StatsManager] Fehler beim Speichern für $uuid: ${e.message}" )
} finally {
cache.remove(uuid)
// Nur entfernen wenn der Cache-Eintrag noch unser ursprüngliches Objekt ist
// (verhindert, dass ein Reconnect-Eintrag gelöscht wird)
cache.remove( uuid, stats )
dirtyFlags.remove( uuid )
}
}

View File

@@ -60,40 +60,26 @@ class TornadoDisaster : NaturalDisaster() {
return eligibleBiomes.any { biome.contains( it ) }
}
override fun trigger(
plugin: SpeedHG,
player: Player
) {
override fun trigger( plugin: SpeedHG, player: Player )
{
val center = player.location.clone()
val world = center.world ?: return
val coroutineScope = CoroutineScope( Dispatchers.Default + SupervisorJob() )
var tick = 0
var angle = 0.0
var mainTask: BukkitTask? = null
mainTask = Bukkit.getScheduler().runTaskTimer( plugin, { ->
Bukkit.getScheduler().runTaskTimer( plugin, { ->
tick++
if ( tick > DURATION_TICKS )
{
mainTask?.cancel()
coroutineScope.cancel()
return@runTaskTimer
}
if ( tick > DURATION_TICKS ) return@runTaskTimer // Bukkit cancelt via cancel()
val currentAngle = angle
angle += ROTATION_SPEED
coroutineScope.launch {
val positions = ArrayList<Triple<Double, Double, Double>>( MAX_HEIGHT * SPOKES )
for ( layer in 0..MAX_HEIGHT )
{
val layerRadius = (( MAX_HEIGHT - layer ).toDouble() / MAX_HEIGHT )
val layerRadius = ( MAX_HEIGHT - layer ).toDouble() / MAX_HEIGHT
for ( spoke in 0 until SPOKES )
{
val a = currentAngle + ( spoke * ( Math.PI * 2.0 / SPOKES ))
val a = angle + ( spoke * ( Math.PI * 2.0 / SPOKES ))
positions += Triple(
center.x + cos( a ) * layerRadius,
center.y + layer.toDouble(),
@@ -101,14 +87,10 @@ class TornadoDisaster : NaturalDisaster() {
)
}
}
angle += ROTATION_SPEED
Bukkit.getScheduler().runTask( plugin ) { ->
positions.forEach { ( x, y, z ) ->
world.spawnParticle(
Particle.CAMPFIRE_COSY_SMOKE,
x, y, z,
1, 0.0, 0.0, 0.0, 0.0
)
world.spawnParticle( Particle.CAMPFIRE_COSY_SMOKE, x, y, z, 1, 0.0, 0.0, 0.0, 0.0 )
}
if ( tick % 20 == 0 )
@@ -119,21 +101,17 @@ class TornadoDisaster : NaturalDisaster() {
plugin.gameManager.alivePlayers
.mapNotNull { Bukkit.getPlayer( it ) }
.filter { it.location.distance( center ) in 0.5..PULL_RADIUS }
.forEach { nearby ->
val dist = nearby.location.distance( center )
if ( dist !in 0.5..PULL_RADIUS ) return@forEach
val strength = ( 1.0 - dist / PULL_RADIUS ) * 0.35
val toCenter: Vector = center.toVector()
val toCenter = center.toVector()
.subtract( nearby.location.toVector() )
.normalize()
.multiply( strength )
toCenter.y = strength * 0.45
nearby.velocity = nearby.velocity.add( toCenter )
}
}
}
}, 0L, 1L )
}

View File

@@ -21,6 +21,7 @@ import org.bukkit.potion.PotionEffect
import org.bukkit.potion.PotionEffectType
import org.bukkit.scheduler.BukkitTask
import java.util.*
import java.util.concurrent.ConcurrentHashMap
import kotlin.random.Random
class GameManager(
@@ -32,7 +33,7 @@ class GameManager(
var timer = 0
val alivePlayers = mutableSetOf<UUID>()
val alivePlayers: MutableSet<UUID> = ConcurrentHashMap.newKeySet()
private var gameTask: BukkitTask? = null
@@ -72,6 +73,13 @@ class GameManager(
}, 10L )
}
fun shutdown()
{
recraftManager.shutdown()
gameTask?.cancel()
gameTask = null
}
private var lobbyIdleCount: Int = 0
private fun gameLoop()
@@ -442,7 +450,7 @@ class GameManager(
val x = Random.nextDouble( -maxRadius, maxRadius )
val z = Random.nextDouble( -maxRadius, maxRadius )
val y = world.getHighestBlockYAt( x.toInt(), z.toInt() ) + 1.0
loc = Location( world, x, y, z )
loc = Location( world, x, y + 1.0, z )
val block = loc.block.type
val below = loc.subtract( 0.0, 1.0, 0.0 ).block.type
@@ -452,7 +460,7 @@ class GameManager(
attempts++
} while ( !safe && attempts < 20 )
player.teleport(loc.add( 0.0, 1.0, 0.0 ))
player.teleport( loc )
}
private fun updateCompass()

View File

@@ -11,8 +11,10 @@ import org.bukkit.GameMode
import org.bukkit.Sound
import org.bukkit.entity.Player
import org.bukkit.event.EventHandler
import org.bukkit.event.EventPriority
import org.bukkit.event.Listener
import org.bukkit.event.entity.EntityDamageByEntityEvent
import org.bukkit.event.player.PlayerQuitEvent
import org.bukkit.potion.PotionEffect
import org.bukkit.potion.PotionEffectType
import java.util.*
@@ -62,6 +64,15 @@ class AntiRunningManager(
}
}
@EventHandler( priority = EventPriority.MONITOR )
fun onQuit(
event: PlayerQuitEvent
) {
lastCombatAction.remove( event.player.uniqueId )
// BossBar entfernen falls aktiv (verhindert Ghost-BossBar nach Reconnect)
event.player.hideBossBar( warningBar )
}
private fun checkPlayers()
{
if (plugin.gameManager.currentState != GameState.INGAME)

View File

@@ -7,11 +7,14 @@ import org.bukkit.Material
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
import org.bukkit.scheduler.BukkitRunnable
import org.bukkit.scheduler.BukkitTask
class RecraftManager(
private val plugin: SpeedHG
) {
private var task: BukkitTask? = null
private val beforeFeast = plugin.config.getBoolean( "game.recraftNerf.beforeFeast", true )
private val recraftNerfEnabled = plugin.config.getBoolean( "game.recraftNerf.enabled", false )
private val maxRecraftAmount = plugin.config.getInt( "game.recraftNerf.maxAmount", 64 )
@@ -21,7 +24,7 @@ class RecraftManager(
if ( !recraftNerfEnabled )
return
object : BukkitRunnable() {
task = object : BukkitRunnable() {
override fun run()
{
@@ -29,6 +32,7 @@ class RecraftManager(
plugin.gameManager.feastManager.hasSpawned )
{
this.cancel()
task = null
return
}
@@ -51,6 +55,12 @@ class RecraftManager(
}.runTaskTimer( plugin, 10L, 10L )
}
fun shutdown()
{
task?.cancel()
task = null
}
private class Recraft {
private val recraftMaterials = listOf(

View File

@@ -276,7 +276,7 @@ class ArmorerKit : Kit()
// Shared no-active placeholder
// =========================================================================
inner class NoActive(
private class NoActive(
playstyle: Playstyle
) : ActiveAbility( playstyle )
{

View File

@@ -181,7 +181,7 @@ class GladiatorKit : Kit()
player.location.clone().add( 0.0, 64.0, 0.0 ),
capturedRadius,
capturedHeight + 2
)
) ?: return AbilityResult.ConditionNotMet( "No space found for gladiator arena" )
val center = BukkitAdapter.adapt( player.world, gladiatorRegion.center )
WorldEditUtils.createCylinder( player.world, center, capturedRadius - 1, true, 1, Material.WHITE_STAINED_GLASS )
@@ -228,11 +228,13 @@ class GladiatorKit : Kit()
private fun getGladiatorLocation(
location: Location,
radius: Int,
height: Int
): Region
height: Int,
attempt: Int = 0
): Region?
{
val random = Random()
if ( attempt >= 20 ) return null // Kein Platz gefunden → Ability abbrechen
val random = Random()
val region = CylinderRegion(
BukkitAdapter.adapt( location.world ),
BukkitAdapter.asBlockVector( location ),
@@ -248,7 +250,8 @@ class GladiatorKit : Kit()
if ( random.nextBoolean() ) -10.0 else 10.0
),
radius,
height
height,
attempt + 1 // ← Versuch mitzählen
)
else region
}

View File

@@ -67,7 +67,7 @@ import kotlin.math.sin
* [lastHitEnemy]-Fenster und berechnet eine Position 1,8 Blöcke hinter dem Feind.
*
* ### Smoke-Mechanismus
* Ein BukkitTask spawnt alle [smokRefreshTicks] einen Partikelring. Jeder Feind
* Ein BukkitTask spawnt alle [smokeRefreshTicks] einen Partikelring. Jeder Feind
* im Ring erhält Blindness I + Slowness I, die regelmäßig erneuert werden.
*/
class NinjaKit : Kit() {

View File

@@ -40,11 +40,9 @@ class TeamSelectionListener(
}
@EventHandler( priority = EventPriority.MONITOR )
fun onQuit(
event: PlayerQuitEvent
) {
val state = plugin.gameManager.currentState
if ( state == GameState.LOBBY || state == GameState.STARTING )
fun onQuit( event: PlayerQuitEvent )
{
// Immer aus dem Team-Tracking entfernen, unabhängig vom GameState
plugin.presetTeamManager.leave( event.player )
}