1
0
Fork 0

refactor: world management

This commit is contained in:
ineanto 2024-07-08 21:20:16 +02:00
parent 5bfdaacefc
commit 6e796febe4
Signed by: ineanto
GPG key ID: E511F9CAA2F9CE84
16 changed files with 152 additions and 232 deletions

2
.idea/kotlinc.xml generated
View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinJpsPluginSettings">
<option name="version" value="1.9.24" />
<option name="version" value="2.0.0" />
</component>
</project>

2
.idea/misc.xml generated
View file

@ -13,7 +13,7 @@
<option name="enabled" value="true" />
<option name="wasEnabledAtLeastOnce" value="true" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="18" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="graalvm-ce-21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

View file

@ -1,32 +1,34 @@
plugins {
kotlin("jvm") version "1.9.24"
kotlin("jvm") version "2.0.0"
id("com.github.johnrengelman.shadow") version "7.1.2"
id("io.papermc.paperweight.userdev") version "1.7.1"
id("xyz.jpenilla.run-paper") version "2.3.0"
}
group = "xyz.ineanto.dragon"
version = "1.0-SNAPSHOT"
version = "1.1"
repositories {
mavenLocal()
gradlePluginPortal()
maven("https://repo.papermc.io/repository/maven-public/")
maven("https://oss.sonatype.org/content/repositories/snapshots")
maven("https://oss.sonatype.org/content/repositories/central")
maven("https://maven.enginehub.org/repo")
maven("https://repo.xenondevs.xyz/releases")
}
dependencies {
// Spigot
paperweight.paperDevBundle("1.20.6-R0.1-SNAPSHOT")
// WorldEdit
compileOnly("com.sk89q.worldedit:worldedit-core:7.3.0-SNAPSHOT")
compileOnly("com.sk89q.worldedit:worldedit-bukkit:7.3.0-SNAPSHOT")
paperweight.paperDevBundle("1.21-R0.1-SNAPSHOT")
// Game Libraries
implementation("xyz.xenondevs.invui:invui:1.33")
implementation("ru.brikster:glyphs-api:1.1.0")
implementation("ru.brikster:glyphs-resources:1.1.0")
implementation("team.unnamed:creative-api:1.7.2")
// Serializer for Minecraft format (ZIP / Folder)
implementation("team.unnamed:creative-serializer-minecraft:1.7.2")
// Stdlib
implementation("org.jetbrains.kotlin:kotlin-stdlib:1.9.24")
// InvUI
implementation("xyz.xenondevs.invui:invui:1.30")
implementation("org.jetbrains.kotlin:kotlin-stdlib:2.0.0")
// Commons IO
implementation("commons-io:commons-io:2.11.0")
}
@ -39,4 +41,8 @@ tasks {
assemble {
dependsOn(reobfJar)
}
runServer {
minecraftVersion("1.21")
}
}

View file

@ -6,7 +6,6 @@ import net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.NamedTextColor
import net.kyori.adventure.text.format.TextDecoration
import net.kyori.adventure.text.minimessage.MiniMessage
import org.apache.commons.io.FileUtils
import org.bukkit.*
import org.bukkit.attribute.Attribute
import org.bukkit.plugin.java.JavaPlugin
@ -24,9 +23,7 @@ import xyz.ineanto.dragon.tasks.GracePeriodTask
import xyz.ineanto.dragon.tasks.TimerTask
import xyz.ineanto.dragon.teams.Team
import xyz.ineanto.dragon.teams.TeamManager
import xyz.ineanto.dragon.world.PreviousWorldUtils
import xyz.ineanto.dragon.world.WorldManager
import java.io.File
import java.time.Duration
import java.time.Instant
import java.util.*
@ -62,10 +59,6 @@ class RunnerDragon : JavaPlugin() {
var GRACE_PERIOD_TASK: BukkitTask? = null
lateinit var SPAWN: Location
lateinit var WORLD: World
lateinit var WORLD_END: World
lateinit var WORLD_NETHER: World
lateinit var LOBBY_SCHEMATIC: SchematicLoader
}
@ -78,42 +71,6 @@ class RunnerDragon : JavaPlugin() {
override fun onEnable() {
instance = this
val previousWorldFolder = File("runnerdragon")
val previousEndFolder = File("world_the_end")
val previousNetherFolder = File("world_nether")
if (previousWorldFolder.exists()) {
logger.warning("Ancien monde détecté! Suppression...")
FileUtils.deleteDirectory(previousWorldFolder)
}
if (previousEndFolder.exists()) {
logger.warning("Ancien End détecté! Suppression...")
PreviousWorldUtils.resetEnd()
}
if (previousNetherFolder.exists()) {
logger.warning("Ancien Nether détecté! Suppression...")
PreviousWorldUtils.resetNether()
}
val creator = WorldCreator("runnerdragon")
creator.environment(World.Environment.NORMAL)
creator.generateStructures(true)
creator.type(WorldType.NORMAL)
val creatorTheEnd = WorldCreator("world_the_end")
creatorTheEnd.environment(World.Environment.THE_END)
val creatorNether = WorldCreator("world_nether")
creatorNether.environment(World.Environment.NETHER)
logger.info("Création des nouveaux mondes...")
WORLD = Bukkit.createWorld(creator)!!
WORLD_END = Bukkit.createWorld(creatorTheEnd)!!
WORLD_NETHER = Bukkit.createWorld(creatorNether)!!
worldManager = WorldManager(WORLD, WORLD_END, WORLD_NETHER)
playerManager = PlayerManager()
teamManager = TeamManager(this)
@ -129,16 +86,16 @@ class RunnerDragon : JavaPlugin() {
server.pluginManager.registerEvents(EntityDamageListener(), this)
getCommand("rd")!!.setExecutor(MainCommand())
if (switchState(GameState.WAITING)) {
logger.info("Runner Dragon chargé avec succès.")
} else {
logger.severe("Runner Dragon a rencontré une erreur de chargement.")
}
worldManager = WorldManager(this)
worldManager.init()
switchState(GameState.WAITING)
logger.info("Runner Dragon chargé avec succès.")
}
fun switchState(state: GameState): Boolean {
fun switchState(state: GameState) {
STATE = state
return when (state) {
when (state) {
GameState.WAITING -> {
val resetComponent: Component = Component
.text()
@ -149,18 +106,23 @@ class RunnerDragon : JavaPlugin() {
Bukkit.broadcast(resetComponent)
logger.info("(Re)Démarrage du jeu...")
// Task cancelling
Bukkit.getScheduler().cancelTasks(this)
TIMER_TASK?.taskId?.let { Bukkit.getScheduler().cancelTask(it) }
GRACE_PERIOD_TASK?.taskId?.let { Bukkit.getScheduler().cancelTask(it) }
TIME = 0
DEAD_COWS = 0
worldManager.applyRules()
logger.info("Génération du spawn...")
LOBBY_SCHEMATIC = SchematicLoader(File("./lobby1.schem"), WORLD)
val schematicSpawn = LOBBY_SCHEMATIC.spawnSchematicAt(0, SPAWN_HEIGHT, 0)
SPAWN = Location(WORLD, 0.0, SPAWN_HEIGHT.plus(2).toDouble(), 0.0, -180f, 0f)
Bukkit.unloadWorld("world", true)
//LOBBY_SCHEMATIC = SchematicLoader(File("./lobby1.schem"), WORLD)
//val schematicSpawn = LOBBY_SCHEMATIC.spawnSchematicAt(0, SPAWN_HEIGHT, 0)
WorldManager.LIMBO_SPAWN = Location(
WorldManager.LIMBO_WORLD,
0.0,
SPAWN_HEIGHT.plus(2).toDouble(),
0.0, -180f, 0f
)
logger.info("Mise en place du système d'équipe...")
teamManager.clean()
@ -171,8 +133,8 @@ class RunnerDragon : JavaPlugin() {
scoreboardTeams.create()
Bukkit.getOnlinePlayers().forEach {
it.teleport(SPAWN)
it.setBedSpawnLocation(SPAWN, true)
it.teleport(WorldManager.LIMBO_SPAWN)
it.respawnLocation = WorldManager.LIMBO_SPAWN
it.inventory.clear()
it.clearActivePotionEffects()
it.health = it.getAttribute(Attribute.GENERIC_MAX_HEALTH)!!.value
@ -185,12 +147,11 @@ class RunnerDragon : JavaPlugin() {
playerManager.getPlayers().forEach { (_, player) ->
player.setTeam(Team.DEFAULT_TEAM)
player.player.gameMode = GameMode.ADVENTURE
player.bukkitPlayer.gameMode = GameMode.ADVENTURE
player.updateDisplayName()
player.updateHeaderFooter()
player.setTeamItem(true)
}
return schematicSpawn
}
GameState.LAUNCH -> {
@ -202,14 +163,13 @@ class RunnerDragon : JavaPlugin() {
.build()
Bukkit.broadcast(launchComponent)
LOBBY_SCHEMATIC.removeSchematic()
SPAWN = WORLD.spawnLocation
//LOBBY_SCHEMATIC.removeSchematic()
TIMER_TASK = TimerTask().runTaskTimer(instance, 0L, 20L)
GRACE_PERIOD_TASK = GracePeriodTask().runTaskTimer(instance, 0L, 20L)
playerManager.getPlayers().forEach { (_, player) ->
player.impulse101()
}
true
}
GameState.GAME -> {
@ -222,12 +182,9 @@ class RunnerDragon : JavaPlugin() {
Bukkit.broadcast(pvpComponent)
GRACE_PERIOD_TASK?.taskId?.let { Bukkit.getScheduler().cancelTask(it) }
true
}
else -> {
true
}
else -> {}
}
}
}

View file

@ -8,6 +8,7 @@ import xyz.ineanto.dragon.RunnerDragon
import xyz.ineanto.dragon.enumContains
import xyz.ineanto.dragon.player.DragonPlayer
import xyz.ineanto.dragon.tasks.StartGameTask
import xyz.ineanto.dragon.world.WorldManager
class GameSubCommand {
fun run(sender: DragonPlayer, args: Array<String>) {
@ -17,9 +18,9 @@ class GameSubCommand {
}
when (args[0]) {
"end" -> {
"dim" -> {
Bukkit.getOnlinePlayers().forEach {
it.teleport(RunnerDragon.WORLD_END.spawnLocation)
it.teleport(WorldManager.GAME_WORLD_THE_END.spawnLocation)
}
}

View file

@ -10,6 +10,7 @@ import org.bukkit.event.player.PlayerPortalEvent
import org.bukkit.event.player.PlayerTeleportEvent
import xyz.ineanto.dragon.GameState
import xyz.ineanto.dragon.RunnerDragon
import xyz.ineanto.dragon.world.WorldManager
class DimensionEntranceListener : Listener {
@EventHandler
@ -35,7 +36,7 @@ class DimensionEntranceListener : Listener {
}
PlayerTeleportEvent.TeleportCause.END_PORTAL -> {
event.player.teleport(RunnerDragon.WORLD_END.spawnLocation)
event.player.teleport(WorldManager.GAME_WORLD_THE_END.spawnLocation)
if (RunnerDragon.END_PORTAL_ENTERED.not()) {
RunnerDragon.END_PORTAL_ENTERED = true

View file

@ -18,14 +18,6 @@ class PlayerJoinListener : Listener {
// TODO (Ineanto, 6/26/23): Set nick
/**
* NickAPI.resetNick(player)
* NickAPI.resetUniqueId(player)
* NickAPI.resetGameProfileName(player)
* NickAPI.resetSkin(player)
* NickAPI.setUniqueId(player, player.uniqueId)
*/
event.joinMessage(
Component.text("(", NamedTextColor.WHITE)
.append(Component.text("+", NamedTextColor.GREEN))
@ -37,12 +29,12 @@ class PlayerJoinListener : Listener {
if (RunnerDragon.STATE == GameState.WAITING) {
dragonPlayer.sendMessage(Component.text("Bienvenue !", NamedTextColor.WHITE))
dragonPlayer.instantiatePlayer()
dragonPlayer.player.gameMode = GameMode.ADVENTURE
dragonPlayer.bukkitPlayer.gameMode = GameMode.ADVENTURE
dragonPlayer.setTeamItem(true)
}
if (dragonPlayer.getTeam() == null) {
dragonPlayer.player.gameMode = GameMode.SPECTATOR
dragonPlayer.bukkitPlayer.gameMode = GameMode.SPECTATOR
dragonPlayer.sendMessage(Component.text("Partie rejointe en spectateur."))
return
}

View file

@ -3,11 +3,11 @@ package xyz.ineanto.dragon.event
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.bukkit.event.player.PlayerRespawnEvent
import xyz.ineanto.dragon.RunnerDragon
import xyz.ineanto.dragon.world.WorldManager
class PlayerRespawnListener : Listener {
@EventHandler
fun onRespawn(event: PlayerRespawnEvent) {
event.respawnLocation = RunnerDragon.SPAWN
event.respawnLocation = WorldManager.GAME_WORLD.spawnLocation
}
}

View file

@ -1,39 +0,0 @@
package xyz.ineanto.dragon.event.end
import org.bukkit.Bukkit
import org.bukkit.ChatColor
import org.bukkit.Sound
import org.bukkit.World
import org.bukkit.entity.EnderDragon
import org.bukkit.potion.PotionEffect
import org.bukkit.potion.PotionEffectType
import xyz.ineanto.dragon.RunnerDragon
@Deprecated("Unused")
class SecondFight {
companion object {
fun start(dragon: EnderDragon) {
Bukkit.getScheduler().scheduleSyncDelayedTask(
RunnerDragon.instance, {
Bukkit.broadcastMessage("${RunnerDragon.PREFIX} ${ChatColor.MAGIC}??ZFjZI19112::!d!zq?")
for (i in 0..2) {
Bukkit.broadcastMessage("${RunnerDragon.PREFIX} ${ChatColor.BOLD}${ChatColor.RED}???")
}
Bukkit.getOnlinePlayers().forEach {
it.addPotionEffect(PotionEffect(PotionEffectType.BLINDNESS, 5, 1))
it.playSound(it.location, Sound.ENTITY_LIGHTNING_BOLT_IMPACT, 1f, 0.5f)
if (it.location.world!!.environment != World.Environment.THE_END) {
it.teleport(RunnerDragon.WORLD_END.spawnLocation)
}
}
Bukkit.broadcastMessage("${RunnerDragon.PREFIX} ${ChatColor.BOLD}${ChatColor.RED}")
dragon.dragonBattle!!.resetCrystals()
dragon.dragonBattle!!.initiateRespawn()
}, 20L * 5
)
}
}
}

View file

@ -25,7 +25,7 @@ class CandleItem(private val team: Team) : AbstractItem() {
team.players.forEach { player ->
val lineComponent = Component.text("-", NamedTextColor.DARK_GRAY)
.appendSpace()
.append(Component.text().append(Component.text(player.player.name, team.teamColor.namedColor)))
.append(Component.text().append(Component.text(player.bukkitPlayer.name, team.teamColor.namedColor)))
itemBuilder.addLoreLines(AdventureComponentWrapper(lineComponent))
}

View file

@ -10,14 +10,15 @@ import org.bukkit.Material
import org.bukkit.inventory.ItemStack
import xyz.ineanto.dragon.RunnerDragon
import xyz.ineanto.dragon.teams.Team
import xyz.ineanto.dragon.world.WorldManager
import java.util.*
class DragonPlayer(uuid: UUID) {
val player = Bukkit.getPlayer(uuid)!!
val bukkitPlayer = Bukkit.getPlayer(uuid)!!
fun instantiatePlayer() {
RunnerDragon.instance.worldManager.teleportAtSpawn(player)
bukkitPlayer.teleport(WorldManager.LIMBO_SPAWN)
updateHeaderFooter()
impulse101()
setTeam(Team.DEFAULT_TEAM)
@ -58,8 +59,8 @@ class DragonPlayer(uuid: UUID) {
.append(Component.text("Linkxss", NamedTextColor.BLUE))
.build()
player.sendPlayerListHeader(header)
player.sendPlayerListFooter(footer)
bukkitPlayer.sendPlayerListHeader(header)
bukkitPlayer.sendPlayerListFooter(footer)
}
fun sendMessage(component: Component) {
@ -69,28 +70,28 @@ class DragonPlayer(uuid: UUID) {
.appendSpace()
.append(component)
.build()
player.sendMessage(message)
bukkitPlayer.sendMessage(message)
}
fun getTeam(): Team? {
return RunnerDragon.instance.teamManager.getPlayerTeam(player)
return RunnerDragon.instance.teamManager.getPlayerTeam(bukkitPlayer)
}
fun setTeam(team: Team) {
RunnerDragon.instance.teamManager.setPlayerTeam(player, team)
RunnerDragon.instance.teamManager.setPlayerTeam(bukkitPlayer, team)
}
fun impulse101() {
player.gameMode = GameMode.SURVIVAL
player.inventory.clear()
player.health = 20.0
player.foodLevel = 20
player.activePotionEffects.clear()
player.walkSpeed = 0.2f
player.flySpeed = 0.1f
player.exp = 0f
player.level = 0
player.totalExperience = 0
bukkitPlayer.gameMode = GameMode.SURVIVAL
bukkitPlayer.inventory.clear()
bukkitPlayer.health = 20.0
bukkitPlayer.foodLevel = 20
bukkitPlayer.activePotionEffects.clear()
bukkitPlayer.walkSpeed = 0.2f
bukkitPlayer.flySpeed = 0.1f
bukkitPlayer.exp = 0f
bukkitPlayer.level = 0
bukkitPlayer.totalExperience = 0
}
fun setTeamItem(value: Boolean) {
@ -99,9 +100,9 @@ class DragonPlayer(uuid: UUID) {
val meta = item.itemMeta!!
meta.displayName(Component.text("Choisir une équipe", NamedTextColor.GOLD))
item.itemMeta = meta
player.inventory.setItem(4, item)
bukkitPlayer.inventory.setItem(4, item)
} else {
player.inventory.setItem(4, ItemStack(Material.AIR))
bukkitPlayer.inventory.setItem(4, ItemStack(Material.AIR))
}
}

View file

@ -1,46 +0,0 @@
package xyz.ineanto.dragon.schematic
import com.sk89q.worldedit.EditSession
import com.sk89q.worldedit.WorldEdit
import com.sk89q.worldedit.WorldEditException
import com.sk89q.worldedit.bukkit.BukkitAdapter
import com.sk89q.worldedit.extent.clipboard.Clipboard
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats
import com.sk89q.worldedit.function.operation.Operation
import com.sk89q.worldedit.function.operation.Operations
import com.sk89q.worldedit.math.BlockVector3
import com.sk89q.worldedit.session.ClipboardHolder
import org.bukkit.World
import java.io.File
import java.io.FileInputStream
class SchematicLoader(private val schematic: File, private val world: World) {
private lateinit var session: EditSession
fun spawnSchematicAt(x: Int, y: Int, z: Int): Boolean {
// probably terribly unoptimized, couldn't care less!
var clipboard: Clipboard?
val format = ClipboardFormats.findByFile(schematic)
format!!.getReader(FileInputStream(schematic)).use { reader -> clipboard = reader.read() }
WorldEdit.getInstance().newEditSession(BukkitAdapter.adapt(world)).use operation@{ weSession ->
session = weSession
val operation: Operation = ClipboardHolder(clipboard!!)
.createPaste(weSession)
.to(BlockVector3.at(x, y, z))
.copyEntities(true)
.build()
try {
Operations.complete(operation)
return true
} catch (exception: WorldEditException) {
exception.printStackTrace()
return false
}
}
}
fun removeSchematic() {
session.undo(session)
}
}

View file

@ -33,7 +33,7 @@ class ScoreboardTeams {
fun addPlayer(player: DragonPlayer) {
player.getTeam()?.let {
val team = scoreboard!!.getTeam(it.teamColor.displayName)
team?.addPlayer(player.player)
team?.addPlayer(player.bukkitPlayer)
}
}

View file

@ -1,22 +0,0 @@
package xyz.ineanto.dragon.world
import org.apache.commons.io.FileUtils
import org.bukkit.Bukkit
class PreviousWorldUtils {
companion object {
fun resetEnd() {
val end = Bukkit.getWorld("world_the_end")!!
end.loadedChunks.forEach { it.unload(false) }
Bukkit.unloadWorld(end, false)
FileUtils.deleteDirectory(end.worldFolder)
}
fun resetNether() {
val nether = Bukkit.getWorld("world_nether")!!
nether.loadedChunks.forEach { it.unload(false) }
Bukkit.unloadWorld(nether, false)
FileUtils.deleteDirectory(nether.worldFolder)
}
}
}

View file

@ -1,19 +1,89 @@
package xyz.ineanto.dragon.world
import org.apache.commons.io.FileUtils
import org.bukkit.Bukkit
import org.bukkit.GameRule
import org.bukkit.Location
import org.bukkit.World
import org.bukkit.entity.Player
import org.bukkit.WorldCreator
import org.bukkit.WorldType
import xyz.ineanto.dragon.RunnerDragon
import java.io.File
class WorldManager(private vararg val worlds: World) {
fun applyRules() {
worlds.forEach {
class WorldManager(private val instance: RunnerDragon) {
companion object {
const val LIMBO_WORLD_NAME = "limbo"
const val GAME_WORLD_NAME = "world"
const val GAME_WORLD_THE_END_NAME = "world_the_end"
const val GAME_WORLD_NETHER_NAME = "world_nether"
lateinit var LIMBO_SPAWN: Location
lateinit var LIMBO_WORLD: World
lateinit var GAME_WORLD: World
lateinit var GAME_WORLD_THE_END: World
lateinit var GAME_WORLD_NETHER: World
}
fun init(): Boolean {
val limboFolder = File(GAME_WORLD_NAME)
if (!limboFolder.exists()) {
// Limbo is the world where players are
// held (hostage) before the game starts.
instance.logger.severe("Limbo world doesn't exist! Generating an emergency spawn...")
LIMBO_WORLD = createWorld(LIMBO_WORLD_NAME, World.Environment.NETHER, false)
}
else {
LIMBO_WORLD = Bukkit.getWorld(LIMBO_WORLD_NAME)!!
}
LIMBO_SPAWN = LIMBO_WORLD.spawnLocation
cleanupPreviousWorlds()
return true
}
fun createGameWorlds() {
instance.logger.info("Création des nouveaux mondes...")
createWorld(GAME_WORLD_NAME, World.Environment.NORMAL, true)
createWorld(GAME_WORLD_THE_END_NAME, World.Environment.THE_END, true)
createWorld(GAME_WORLD_NETHER_NAME, World.Environment.NETHER, true)
instance.logger.info("Création des nouveaux mondes terminée.")
applyGameRules()
}
fun cleanupPreviousWorlds() {
arrayOf(
File(GAME_WORLD_NAME),
File(GAME_WORLD_THE_END_NAME),
File(GAME_WORLD_THE_END_NAME)
).forEach {
if (it.exists()) {
instance.logger.warning("Ancien monde détecté (${it.name}) ! Suppression...")
deleteWorld(it.name)
}
}
}
private fun applyGameRules() {
arrayOf(GAME_WORLD, GAME_WORLD_THE_END, GAME_WORLD_NETHER).forEach {
it.setGameRule(GameRule.DO_DAYLIGHT_CYCLE, false)
it.setGameRule(GameRule.ANNOUNCE_ADVANCEMENTS, false)
}
}
fun teleportAtSpawn(player: Player) {
player.teleport(RunnerDragon.SPAWN)
fun createWorld(name: String, environment: World.Environment, structures: Boolean): World {
val creator = WorldCreator(name)
creator.environment(World.Environment.NORMAL)
creator.generateStructures(true)
creator.type(WorldType.NORMAL)
return Bukkit.createWorld(creator)!!
}
private fun deleteWorld(name: String) {
val world = Bukkit.getWorld(name)
if (world == null) return
world.loadedChunks.forEach { it.unload(false) }
Bukkit.unloadWorld(world, false)
FileUtils.deleteDirectory(world.worldFolder)
}
}

View file

@ -1,8 +1,7 @@
name: RunnerDragon
main: xyz.ineanto.dragon.RunnerDragon
version: 1.0-SNAPSHOT
api-version: "1.20"
depend: [ WorldEdit ]
version: 1.1
api-version: "1.21"
commands:
rd:
permission: "runnerdragon.admin"