Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ import com.xpdustry.imperium.discord.misc.MessageCreate
import com.xpdustry.imperium.discord.misc.await
import com.xpdustry.imperium.discord.service.DiscordService
import java.awt.Color
import java.awt.image.BufferedImage
import java.io.IOException
import java.net.URI
import javax.imageio.ImageIO
import kotlin.io.path.createTempFile
import kotlin.io.path.deleteExisting
import kotlinx.coroutines.future.await
Expand Down Expand Up @@ -96,21 +99,13 @@ class MapSubmitCommand(instances: InstanceManager) : ImperiumApplication.Listene
val tempFile = createTempFile()
map.proxy.downloadToPath(tempFile).await()
val metaResult = content.parseMap(tempFile.toFile())
val previewResult = content.renderMap(tempFile.toFile())
if (metaResult.isFailure) {
val ex = metaResult.exceptionOrNull()!!
logger.error("Invalid map file", ex)
reply.sendMessage("Invalid map file: " + ex.message).await()
return
}
if (previewResult.isFailure) {
val ex = previewResult.exceptionOrNull()!!
logger.error("Invalid map file", ex)
reply.sendMessage("Invalid map file: " + ex.message).await()
return
}
val meta = metaResult.getOrThrow()
val preview = previewResult.getOrThrow()

if (meta.width > MindustryMap.MAX_MAP_SIDE_SIZE || meta.height > MindustryMap.MAX_MAP_SIDE_SIZE) {
reply
Expand All @@ -121,6 +116,12 @@ class MapSubmitCommand(instances: InstanceManager) : ImperiumApplication.Listene
return
}

val previewResult = content.renderMap(tempFile.toFile())
if (previewResult.isFailure) {
logger.error("Failed to render map preview", previewResult.exceptionOrNull())
}
val preview = previewResult.getOrDefault(FALLBACK_PREVIEW)

val channel =
discord.getMainServer().getTextChannelById(config.discord.channels.maps)
?: throw IllegalStateException("Map submission channel not found")
Expand All @@ -138,7 +139,7 @@ class MapSubmitCommand(instances: InstanceManager) : ImperiumApplication.Listene
field("Name", meta.name.stripMindustryColors(), false)
field("Author", meta.author.stripMindustryColors(), false)
field("Description", meta.description.stripMindustryColors(), false)
field("Size", "${preview.width} x ${preview.height}", false)
field("Size", "${meta.width} x ${meta.height}", false)
if (notes != null) {
field("Notes", notes, false)
}
Expand Down Expand Up @@ -292,5 +293,11 @@ class MapSubmitCommand(instances: InstanceManager) : ImperiumApplication.Listene
private const val MAP_ACCEPT_BUTTON = "map-submission-accept:2"
private const val MAP_REJECT_BUTTON = "map-submission-reject:2"
private val logger by LoggerDelegate()
private val FALLBACK_PREVIEW: BufferedImage by lazy {
MapSubmitCommand::class
.java
.getResourceAsStream("/com/xpdustry/imperium/discord/map_preview_error.png")
?.use { ImageIO.read(it) } ?: throw IOException("Failed to load fallback map preview image")
}
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -168,16 +168,23 @@ class AdminRequestListener(instances: InstanceManager) : ImperiumApplication.Lis
}
}

Vars.net.handleServer(AdminRequestCallPacket::class.java, ::interceptAdminRequest)
Vars.net.handleServer(AdminRequestCallPacket::class.java) { con, packet ->
ImperiumScope.MAIN.launch {
// TODO We REALLY need a proper local account cache...
val playerRank = runCatching { getUserRank(con.player) }.getOrNull() ?: Rank.EVERYONE
runMindustryThread { interceptAdminRequest(con, packet, playerRank) }
}
}
}

private fun interceptAdminRequest(con: NetConnection, packet: AdminRequestCallPacket) {
private fun interceptAdminRequest(con: NetConnection, packet: AdminRequestCallPacket, playerRank: Rank) {
if (con.player == null) {
logger.warn("Received admin request from non-existent player (uuid: {}, ip: {})", con.uuid, con.address)
return
}

if (!con.player.admin()) {
// Allow undercover staff to use the admin menu
if (playerRank < Rank.OVERSEER || !con.player.admin()) {
logger.warn(
"{} ({}) attempted to perform an admin action without permission",
con.player.plainName(),
Expand All @@ -195,7 +202,10 @@ class AdminRequestListener(instances: InstanceManager) : ImperiumApplication.Lis
return
}

if (packet.other.admin() && (packet.action != AdminAction.switchTeam && packet.action != AdminAction.wave)) {
if (
(packet.other.admin() && playerRank < Rank.ADMIN) &&
(packet.action != AdminAction.switchTeam && packet.action != AdminAction.wave)
) {
logger.warn(
"{} ({}) attempted to perform an admin action on the admin {} ({})",
con.player.plainName(),
Expand Down Expand Up @@ -270,7 +280,7 @@ class AdminRequestListener(instances: InstanceManager) : ImperiumApplication.Lis
Call.infoMessage(requester.con, "Player not found.")
return@launch
}
val canSeeInfo = (accounts.selectBySession(requester.sessionKey)?.rank ?: Rank.EVERYONE) >= Rank.ADMIN
val canSeeInfo = getUserRank(requester) >= Rank.ADMIN
val historic = users.findNamesAndAddressesById(user.id)
Call.traceInfo(
requester.con,
Expand Down Expand Up @@ -318,6 +328,11 @@ class AdminRequestListener(instances: InstanceManager) : ImperiumApplication.Lis
}
}

private suspend fun getUserRank(requester: Player): Rank {
val account = accounts.selectBySession(requester.sessionKey) ?: return Rank.EVERYONE
return account.rank
}

companion object {
private val logger by LoggerDelegate()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,26 @@
*/
package com.xpdustry.imperium.mindustry.security

import arc.math.geom.Point2
import com.xpdustry.distributor.api.Distributor
import com.xpdustry.distributor.api.annotation.EventHandler
import com.xpdustry.distributor.api.annotation.TaskHandler
import com.xpdustry.distributor.api.command.CommandSender
import com.xpdustry.distributor.api.plugin.MindustryPlugin
import com.xpdustry.distributor.api.scheduler.MindustryTimeUnit
import com.xpdustry.imperium.common.application.ImperiumApplication
import com.xpdustry.imperium.common.command.ImperiumCommand
import com.xpdustry.imperium.common.config.ImperiumConfig
import com.xpdustry.imperium.common.inject.get
import com.xpdustry.imperium.mindustry.command.annotation.ClientSide
import com.xpdustry.imperium.mindustry.command.annotation.ServerSide
import com.xpdustry.imperium.mindustry.misc.Entities
import com.xpdustry.imperium.mindustry.misc.PlayerMap
import com.xpdustry.imperium.mindustry.misc.asAudience
import com.xpdustry.imperium.mindustry.translation.player_afk
import com.xpdustry.imperium.mindustry.translation.is_player_afk
import com.xpdustry.imperium.mindustry.translation.player_afk_announcement
import com.xpdustry.imperium.mindustry.translation.player_afk_kick
import java.time.Duration
import java.time.Instant
import kotlin.time.toJavaDuration
import kotlin.time.toKotlinDuration
import mindustry.Vars
import mindustry.game.EventType
Expand All @@ -44,10 +49,11 @@ interface AfkManager {
class AfkListener(private val config: ImperiumConfig, plugin: MindustryPlugin) :
AfkManager, ImperiumApplication.Listener {
private val lastActivity = PlayerMap<Instant>(plugin)
private val lastPosition = PlayerMap<Int>(plugin)
private val notified = PlayerMap<Unit>(plugin)

override fun onImperiumInit() {
// TODO: TapEvent is not covered here but it is a valid way to show a player is there however how to detect if
// its just a miss click? Do TapEvents occur if the window isnt focused?
Vars.netServer.admins.addActionFilter { action ->
onDisturbingThePeace(action.player)
true
Expand All @@ -66,39 +72,59 @@ class AfkListener(private val config: ImperiumConfig, plugin: MindustryPlugin) :

@TaskHandler(interval = 15, unit = MindustryTimeUnit.SECONDS)
fun onPlayerAfkUpdate() {
Entities.getPlayers().forEach { player ->
val new = Point2.pack(player.tileX(), player.tileY())
val old = lastPosition.set(player, new)
if (old != new) {
onDisturbingThePeace(player)
}

val duration = getAfkDuration(player).toKotlinDuration()
when {
duration >= config.mindustry.afkDelay -> {
if (notified.set(player, Unit) != Unit) player.asAudience.sendMessage(player_afk(enabled = true))
}
duration >= config.mindustry.afkKickDelay -> {
player.asAudience.kick(player_afk_kick(), Duration.ZERO)
}
}
for (player in Entities.getPlayers()) {
checkAfkStatus(player)
}
}

override fun isPlayerAfk(player: Player): Boolean {
return getAfkDuration(player).toKotlinDuration() >= config.mindustry.afkDelay
}

private fun checkAfkStatus(player: Player) {
val duration = getAfkDuration(player).toKotlinDuration()

when {
duration >= config.mindustry.afkKickDelay -> {
player.asAudience.kick(player_afk_kick(), Duration.ZERO)
}
duration >= config.mindustry.afkDelay -> {
if (notified.set(player, Unit) != Unit) {
Distributor.get()
.audienceProvider
.players
.sendMessage(player_afk_announcement(true, player.plainName()))
}
}
}
}

private fun getAfkDuration(player: Player): Duration {
return lastActivity[player]?.let { Duration.between(it, Instant.now()) } ?: Duration.ZERO
}

// PERSONA!!
private fun onDisturbingThePeace(player: Player) {
if (isPlayerAfk(player)) {
player.asAudience.sendMessage(player_afk(enabled = false))
Distributor.get().audienceProvider.players.sendMessage(player_afk_announcement(false, player.plainName()))
}
notified.remove(player)
lastActivity[player] = Instant.now()
}

@ImperiumCommand(["afk"])
@ClientSide
@ServerSide
fun isAfkCommand(sender: CommandSender, target: Player? = null) {
if (target == null) {
if (sender.isPlayer) {
val afkInstant = Instant.now().minus((config.mindustry.afkDelay).toJavaDuration())
lastActivity[sender.player] = afkInstant
checkAfkStatus(sender.player)
} else sender.error("Console must specify a player. You can't be afk as console.")
} else {
val isAfk = isPlayerAfk(target)
sender.reply(is_player_afk(isAfk, target.plainName()))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,18 @@ private val CYAN_PREFIX = text(">>> ", CYAN)

fun cyan_prefix(component: Component): Component = components(CYAN_PREFIX, space(), component)

fun player_afk(enabled: Boolean): Component =
components(WHITE, text(">>> ", CYAN), translatable("imperium.player.afk.${if (enabled) "enabled" else "disabled"}"))

fun player_afk_kick(): Component = components(WHITE, translatable("imperium.player.afk.kicked", SCARLET))

fun is_player_afk(boolean: Boolean, player: String): Component =
components(
WHITE,
text(">>> ", CYAN),
translatable("imperium.player.afk.$boolean", TranslationArguments.array(text(player, ORANGE))),
)

fun player_afk_announcement(boolean: Boolean, player: String): Component =
components(
WHITE,
text(">>> ", CYAN),
translatable("imperium.player.afk.announcement.$boolean", TranslationArguments.array(text(player, ORANGE))),
)
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,8 @@ imperium.notification.login=You are logged in as {0}.
imperium.killall.force.success.base=You killed {0} unit(s)
imperium.killall.force.success.type=of type {0}
imperium.killall.force.success.team=from the {0} team.
imperium.player.afk.enabled=You have been marked as AFK
imperium.player.afk.disabled=You are no longer marked as AFK
imperium.player.afk.kicked=You have been kicked for being AFK for too long.
imperium.player.afk.kicked=You have been kicked for being AFK for too long.
imperium.player.afk.true={0} is AFK. They are not counted in votes!
imperium.player.afk.false={0} is not AFK. They are counted in votes.
imperium.player.afk.announcement.true={0} has been marked as AFK!
imperium.player.afk.announcement.false={0} is no longer AFK!
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,6 @@ imperium.messages.warning=Attention
imperium.messages.warning.bad_word=Votre message contenait des gros mots ({1}). Abstenez-vous d'utiliser ce genre de langage, ou vous risquez d'être {0}.
imperium.no=Non
imperium.notification.login=Vous êtes connecté en tant que {0}.
imperium.player.afk.disabled=Vous n'êtes plus marqué comme AFK
imperium.player.afk.enabled=Vous avez été marqué comme AFK
imperium.player.afk.kicked=Vous avez été expulsé pour avoir été AFK trop longtemps.
imperium.report.reason.cheating=Tricherie
imperium.report.reason.griefing=Griefing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,6 @@ imperium.messages.warning=Внимание
imperium.messages.warning.bad_word=Ваше сообщение содержит нехорошие слова ({1}). Воздержитесь от использования таких слов, иначе вы можете быть {0}.
imperium.no=Нет
imperium.notification.login=Вы вошли в систему под именем {0}.
imperium.player.afk.disabled=Вы более не помечены как "Отошёл"
imperium.player.afk.enabled=Вы получили пометку "Отошёл"
imperium.player.afk.kicked=Вы были исключены т.к. слишком долго находились с пометкой "Отошёл".
imperium.report.reason.cheating=Читерство
imperium.report.reason.griefing=Гриффинг
Expand Down
Loading