Skip to content

Commit

Permalink
chore: Fix cringe map metadata system
Browse files Browse the repository at this point in the history
  • Loading branch information
phinner committed Dec 4, 2023
1 parent 2f9ba08 commit 36a46a5
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import com.xpdustry.imperium.mindustry.config.ConventionListener
import com.xpdustry.imperium.mindustry.game.GameListener
import com.xpdustry.imperium.mindustry.game.ImperiumLogicListener
import com.xpdustry.imperium.mindustry.history.HistoryCommand
import com.xpdustry.imperium.mindustry.misc.ImperiumMetadataChunkReader
import com.xpdustry.imperium.mindustry.misc.getMindustryVersion
import com.xpdustry.imperium.mindustry.security.AdminRequestListener
import com.xpdustry.imperium.mindustry.security.AntiEvadeListener
Expand All @@ -67,12 +68,14 @@ import fr.xpdustry.distributor.api.plugin.AbstractMindustryPlugin
import java.util.Locale
import kotlin.system.exitProcess
import kotlinx.coroutines.runBlocking
import mindustry.io.SaveVersion

class ImperiumPlugin : AbstractMindustryPlugin() {
private val application = MindustryImperiumApplication(this)

override fun onInit() {
logger.info("Imperium plugin initialized!")
SaveVersion.addCustomChunk("imperium", ImperiumMetadataChunkReader)

val source =
LocalizationSourceRegistry.create(application.instances.get<ImperiumConfig>().language)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ import com.xpdustry.imperium.common.inject.get
import com.xpdustry.imperium.common.misc.LoggerDelegate
import com.xpdustry.imperium.common.version.MindustryVersion
import com.xpdustry.imperium.mindustry.misc.getMindustryVersion
import com.xpdustry.imperium.mindustry.misc.playtime
import com.xpdustry.imperium.mindustry.misc.runMindustryThread
import com.xpdustry.imperium.mindustry.misc.snowflake
import com.xpdustry.imperium.mindustry.misc.start
import fr.xpdustry.distributor.api.event.EventHandler
import fr.xpdustry.distributor.api.util.Priority
import java.time.Instant
import kotlin.time.Duration
import kotlin.time.Duration.Companion.ZERO
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.delay
Expand All @@ -40,7 +42,6 @@ import mindustry.Vars
import mindustry.core.GameState
import mindustry.game.EventType
import mindustry.io.SaveIO
import mindustry.maps.Map
import mindustry.net.Administration

class GameListener(instances: InstanceManager) : ImperiumApplication.Listener {
Expand All @@ -52,7 +53,7 @@ class GameListener(instances: InstanceManager) : ImperiumApplication.Listener {
ImperiumScope.MAIN.launch {
while (isActive) {
delay(5.seconds)
if (Vars.state.state != GameState.State.playing) continue
if (Vars.state.state != GameState.State.playing || Vars.state.gameOver) continue
runMindustryThread { Vars.state.map.playtime += 5.seconds }
}
}
Expand Down Expand Up @@ -115,17 +116,7 @@ class GameListener(instances: InstanceManager) : ImperiumApplication.Listener {
buildingsDestroyed = stats.buildingsDestroyed,
winner = event.winner.id.toUByte())
}
Vars.state.map.playtime = ZERO
Vars.state.map.start = null
}

private var Map.start: Instant?
get() = tags.get("imperium-map-start")?.toLongOrNull()?.let(Instant::ofEpochMilli)
set(value) {
tags.put("imperium-map-start", value?.toEpochMilli()?.toString())
}

private var Map.playtime: Duration
get() = tags.get("imperium-map-playtime")?.toLongOrNull()?.seconds ?: Duration.ZERO
set(value) {
tags.put("imperium-map-playtime", value.inWholeSeconds.toString())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Imperium, the software collection powering the Xpdustry network.
* Copyright (C) 2023 Xpdustry
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.xpdustry.imperium.mindustry.misc

import com.xpdustry.imperium.common.misc.LoggerDelegate
import com.xpdustry.imperium.common.snowflake.Snowflake
import java.io.DataInput
import java.io.DataOutput
import java.time.Instant
import kotlin.time.Duration
import kotlin.time.Duration.Companion.ZERO
import kotlin.time.Duration.Companion.seconds
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.json.long
import kotlinx.serialization.json.put
import mindustry.Vars
import mindustry.io.SaveFileReader.CustomChunk
import mindustry.maps.Map

private const val MAP_SNOWFLAKE = "imperium-map-snowflake"
var Map.snowflake: Snowflake?
get() = tags.get(MAP_SNOWFLAKE)?.toLongOrNull()
set(value) {
tags.put(MAP_SNOWFLAKE, value.toString())
}

private const val MAP_START = "imperium-map-start"
var Map.start: Instant?
get() = tags.get(MAP_START)?.toLongOrNull()?.let(Instant::ofEpochMilli)
set(value) {
tags.put(MAP_START, value?.toEpochMilli().toString())
}

private const val MAP_PLAYTIME = "imperium-map-playtime"
var Map.playtime: Duration
get() = tags.get(MAP_PLAYTIME)?.toLongOrNull()?.seconds ?: ZERO
set(value) {
tags.put(MAP_PLAYTIME, value.inWholeSeconds.toString())
}

object ImperiumMetadataChunkReader : CustomChunk {

private val logger by LoggerDelegate()

override fun write(stream: DataOutput) {
val json = buildJsonObject {
Vars.state.map.snowflake?.let { put(MAP_SNOWFLAKE, it) }
Vars.state.map.start?.toEpochMilli()?.let { put(MAP_START, it) }
put(MAP_PLAYTIME, Vars.state.map.playtime.inWholeSeconds.toString())
}
stream.writeUTF(json.toString())
logger.debug("Written imperium metadata: {}", json.toString())
}

override fun read(stream: DataInput) {
val json = Json.parseToJsonElement(stream.readUTF()).jsonObject
Vars.state.map.snowflake = json[MAP_SNOWFLAKE]?.jsonPrimitive?.long
Vars.state.map.start = json[MAP_START]?.jsonPrimitive?.long?.let(Instant::ofEpochMilli)
Vars.state.map.playtime = json[MAP_PLAYTIME]?.jsonPrimitive?.long?.seconds ?: ZERO
logger.debug("Read imperium metadata: {}", json.toString())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ package com.xpdustry.imperium.mindustry.misc

import com.xpdustry.imperium.common.misc.toInetAddress
import com.xpdustry.imperium.common.network.Discovery
import com.xpdustry.imperium.common.snowflake.Snowflake
import com.xpdustry.imperium.common.version.MindustryVersion
import java.net.InetAddress
import mindustry.Vars
Expand All @@ -28,12 +27,6 @@ import mindustry.core.Version
import mindustry.game.Gamemode
import mindustry.net.Administration

var mindustry.maps.Map.snowflake: Snowflake?
get() = tags.get("imperium-map-id")?.toLongOrNull()
set(value) {
tags.put("imperium-map-id", value.toString())
}

fun getMindustryServerInfo(): Discovery.Data.Mindustry =
Discovery.Data.Mindustry(
Administration.Config.serverName.string(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,47 +56,46 @@ class MapListener(instances: InstanceManager) : ImperiumApplication.Listener {
if (config.gamemode in it.gamemodes || it.gamemodes.isEmpty()) reloadMaps()
}

reloadMaps()
ImperiumScope.MAIN.launch { reloadMaps() }
}

@Command(["reloadmaps"])
@ServerSide
private fun onMapReloadCommand() {
private suspend fun onMapReloadCommand() {
reloadMaps()
}

private fun reloadMaps() =
ImperiumScope.MAIN.launch {
val old = runMindustryThread {
val before = Vars.maps.all().map { it.name().stripMindustryColors() }.toMutableSet()
Vars.maps.reload()
return@runMindustryThread before
}
private suspend fun reloadMaps() {
val old = runMindustryThread {
val before = Vars.maps.all().map { it.name().stripMindustryColors() }.toMutableSet()
Vars.maps.reload()
return@runMindustryThread before
}

val pool =
maps.findAllMapsByGamemode(config.gamemode).mapNotNull {
try {
downloadMapFromPool(it)
} catch (e: Exception) {
logger.error(
"Failed to load map from server pool, falling back to local maps.", e)
null
}
val pool =
maps.findAllMapsByGamemode(config.gamemode).mapNotNull {
try {
downloadMapFromPool(it)
} catch (e: Exception) {
logger.error(
"Failed to load map from server pool, falling back to local maps.", e)
null
}
if (pool.isEmpty()) {
logger.warn("No maps found in server pool, falling back to local maps.")
}
if (pool.isEmpty()) {
logger.warn("No maps found in server pool, falling back to local maps.")
}

runMindustryThread {
Vars.maps.all().addAll(pool)
val now = Vars.maps.all().map { it.name().stripMindustryColors() }.toMutableSet()
logger.info(
"Reloaded {} maps (added={}, removed={}).",
now.size,
(now - old).size,
(old - now).size)
}
runMindustryThread {
Vars.maps.all().addAll(pool)
val now = Vars.maps.all().map { it.name().stripMindustryColors() }.toMutableSet()
logger.info(
"Reloaded {} maps (added={}, removed={}).",
now.size,
(now - old).size,
(old - now).size)
}
}

private suspend fun downloadMapFromPool(map: MindustryMap): Map {
val file = cache.resolve("${map.snowflake}_${map.lastUpdate.toEpochMilli()}.msav")
Expand Down

0 comments on commit 36a46a5

Please sign in to comment.