Compare commits
1 Commits
new-logging
..
csr
| Author | SHA1 | Date | |
|---|---|---|---|
|
829d8a6cdf
|
@@ -24,5 +24,4 @@ plugins {
|
|||||||
dependencies {
|
dependencies {
|
||||||
testImplementation(libs.junit)
|
testImplementation(libs.junit)
|
||||||
testImplementation(libs.hamcrest)
|
testImplementation(libs.hamcrest)
|
||||||
compileOnly(project(":CommonCore:SQL"))
|
|
||||||
}
|
}
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is a part of the SteamWar software.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2026 SteamWar.de-Serverteam
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.steamwar
|
|
||||||
|
|
||||||
data class ServerInfo(val name: String, val version: Int, val checkpointed: Boolean)
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is a part of the SteamWar software.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2026 SteamWar.de-Serverteam
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.steamwar.logger
|
|
||||||
|
|
||||||
import de.steamwar.sql.AuditLog
|
|
||||||
import de.steamwar.sql.AuditLogTable
|
|
||||||
import de.steamwar.sql.SQLWrapper
|
|
||||||
import de.steamwar.sql.SteamwarUser
|
|
||||||
import de.steamwar.sql.internal.useDb
|
|
||||||
import org.jetbrains.exposed.v1.jdbc.insertIgnore
|
|
||||||
|
|
||||||
class LogEntry(val type: AuditLog.Type, val user: SteamwarUser) {
|
|
||||||
private val start = System.currentTimeMillis()
|
|
||||||
|
|
||||||
var text: String = ""
|
|
||||||
var arguments: String = ""
|
|
||||||
|
|
||||||
private var exceptionType: String? = null
|
|
||||||
private var exceptionText: String? = null
|
|
||||||
private var exceptionStacktrace: String? = null
|
|
||||||
|
|
||||||
private var owner: SteamwarUser? = null
|
|
||||||
|
|
||||||
fun addException(e: Throwable) {
|
|
||||||
exceptionType = e.javaClass.name
|
|
||||||
exceptionText = e.message
|
|
||||||
exceptionStacktrace = e.stackTraceToString()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addServerOwner(owner: SteamwarUser) {
|
|
||||||
this.owner = owner
|
|
||||||
}
|
|
||||||
|
|
||||||
fun finish() {
|
|
||||||
val end = System.currentTimeMillis()
|
|
||||||
val info = SQLWrapper.impl.serverInfo
|
|
||||||
useDb {
|
|
||||||
AuditLogTable.insertIgnore {
|
|
||||||
it[this.action] = type
|
|
||||||
it[this.actor] = user.getId()
|
|
||||||
it[this.actionText] = text
|
|
||||||
it[this.actionArguments] = arguments
|
|
||||||
it[this.duration] = end - start
|
|
||||||
it[this.errorType] = exceptionType
|
|
||||||
it[this.errorText] = exceptionText
|
|
||||||
it[this.stackTrace] = exceptionStacktrace
|
|
||||||
it[this.server] = info.name
|
|
||||||
it[this.checkpointed] = info.checkpointed
|
|
||||||
it[this.duration] = end - start
|
|
||||||
it[this.serverOwner] = owner?.getId()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is a part of the SteamWar software.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2026 SteamWar.de-Serverteam
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.steamwar.logger
|
|
||||||
|
|
||||||
import de.steamwar.sql.AuditLog
|
|
||||||
import de.steamwar.sql.SteamwarUser
|
|
||||||
|
|
||||||
object SWLogger {
|
|
||||||
fun startCommand(user: SteamwarUser) = LogEntry(AuditLog.Type.COMMAND, user)
|
|
||||||
}
|
|
||||||
@@ -19,47 +19,94 @@
|
|||||||
|
|
||||||
package de.steamwar.sql
|
package de.steamwar.sql
|
||||||
|
|
||||||
|
import de.steamwar.sql.internal.useDb
|
||||||
import org.jetbrains.exposed.v1.core.dao.id.EntityID
|
import org.jetbrains.exposed.v1.core.dao.id.EntityID
|
||||||
import org.jetbrains.exposed.v1.core.dao.id.IntIdTable
|
import org.jetbrains.exposed.v1.core.dao.id.IntIdTable
|
||||||
import org.jetbrains.exposed.v1.dao.IntEntity
|
import org.jetbrains.exposed.v1.dao.IntEntity
|
||||||
import org.jetbrains.exposed.v1.dao.IntEntityClass
|
import org.jetbrains.exposed.v1.dao.IntEntityClass
|
||||||
import org.jetbrains.exposed.v1.javatime.timestamp
|
import org.jetbrains.exposed.v1.javatime.timestamp
|
||||||
|
import java.time.Instant
|
||||||
|
|
||||||
object AuditLogTable: IntIdTable("AuditLog", "AuditLogId") {
|
object AuditLogTable: IntIdTable("AuditLog", "AuditLogId") {
|
||||||
val time = timestamp("Time")
|
val time = timestamp("Time")
|
||||||
val server = varchar("ServerName", 255)
|
val server = varchar("ServerName", 255)
|
||||||
val serverType = varchar("ServerType", 255)
|
val serverOwner = reference("ServerOwner", SteamwarUserTable).nullable()
|
||||||
val serverOwner = optReference("ServerOwner", SteamwarUserTable)
|
|
||||||
val checkpointed = bool("Checkpointed")
|
|
||||||
val actor = reference("Actor", SteamwarUserTable)
|
val actor = reference("Actor", SteamwarUserTable)
|
||||||
val action = enumerationByName("ActionType", 255, AuditLog.Type::class)
|
val action = enumerationByName("ActionType", 255, AuditLog.Type::class)
|
||||||
val actionText = text("ActionText")
|
val actionText = text("ActionText")
|
||||||
val actionArguments = text("ActionArguments")
|
|
||||||
val duration = long("Duration")
|
|
||||||
val errorType = text("ErrorType").nullable()
|
|
||||||
val errorText = text("ErrorText").nullable()
|
|
||||||
val stackTrace = text("StackTrace").nullable()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class AuditLog(id: EntityID<Int>): IntEntity(id) {
|
class AuditLog(id: EntityID<Int>): IntEntity(id) {
|
||||||
companion object: IntEntityClass<AuditLog>(AuditLogTable) {
|
companion object: IntEntityClass<AuditLog>(AuditLogTable) {
|
||||||
@JvmField
|
const val SERVER_NAME_VELOCITY: String = "Velocity"
|
||||||
val SERVER_NAME_VELOCITY: String = "Velocity"
|
|
||||||
|
private fun create(
|
||||||
|
serverName: String,
|
||||||
|
serverOwner: SteamwarUser?,
|
||||||
|
actor: SteamwarUser,
|
||||||
|
actionType: Type,
|
||||||
|
text: String = ""
|
||||||
|
) = useDb {
|
||||||
|
new {
|
||||||
|
this.time = Instant.now()
|
||||||
|
this.server = serverName
|
||||||
|
this.serverOwner = serverOwner?.id
|
||||||
|
this.actor = actor.id
|
||||||
|
this.action = actionType
|
||||||
|
this.actionText = text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun createJoin(jointServerName: String, serverOwner: SteamwarUser?, joinedPlayer: SteamwarUser) = create(jointServerName, serverOwner, joinedPlayer, Type.JOIN)
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun createLeave(leftServerName: String, serverOwner: SteamwarUser?, joinedPlayer: SteamwarUser) = create(leftServerName, serverOwner, joinedPlayer, Type.LEAVE)
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun createCommand(serverName: String, serverOwner: SteamwarUser?, player: SteamwarUser?, command: String) = player?.let { create(serverName, serverOwner, it, Type.COMMAND, command) }
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun createSensitiveCommand(
|
||||||
|
serverName: String,
|
||||||
|
serverOwner: SteamwarUser?,
|
||||||
|
player: SteamwarUser?,
|
||||||
|
command: String
|
||||||
|
) = player?.let { create(serverName, serverOwner, it, Type.SENSITIVE_COMMAND, command) }
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun createChat(serverName: String, serverOwner: SteamwarUser?, chatter: SteamwarUser, chat: String) = create(serverName, serverOwner, chatter, Type.CHAT, chat)
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun createGuiOpen(serverName: String, serverOwner: SteamwarUser?, player: SteamwarUser, guiName: String) = create(serverName, serverOwner, player, Type.GUI_OPEN, guiName)
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun createGuiClick(
|
||||||
|
serverName: String,
|
||||||
|
serverOwner: SteamwarUser?,
|
||||||
|
player: SteamwarUser,
|
||||||
|
guiName: String,
|
||||||
|
clickType: String,
|
||||||
|
slot: Int,
|
||||||
|
itemName: String
|
||||||
|
) = create(
|
||||||
|
serverName,
|
||||||
|
serverOwner,
|
||||||
|
player,
|
||||||
|
Type.GUI_CLICK,
|
||||||
|
"Gui: $guiName\nSlot: $slot\nClickType: $clickType\nItemName: $itemName"
|
||||||
|
)
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun createGuiClose(serverName: String, serverOwner: SteamwarUser?, player: SteamwarUser, guiName: String) = create(serverName, serverOwner, player, Type.GUI_CLOSE, guiName)
|
||||||
}
|
}
|
||||||
|
|
||||||
var time by AuditLogTable.time
|
var time by AuditLogTable.time
|
||||||
var server by AuditLogTable.server
|
var server by AuditLogTable.server
|
||||||
val serverType by AuditLogTable.serverType
|
|
||||||
var serverOwner by AuditLogTable.serverOwner
|
var serverOwner by AuditLogTable.serverOwner
|
||||||
var actor by AuditLogTable.actor
|
var actor by AuditLogTable.actor
|
||||||
var checkpointed by AuditLogTable.checkpointed
|
|
||||||
var action by AuditLogTable.action
|
var action by AuditLogTable.action
|
||||||
var actionText by AuditLogTable.actionText
|
var actionText by AuditLogTable.actionText
|
||||||
var actionArguments by AuditLogTable.actionArguments
|
|
||||||
var duration by AuditLogTable.duration
|
|
||||||
var errorType by AuditLogTable.errorType
|
|
||||||
var errorText by AuditLogTable.errorText
|
|
||||||
var stackTrace by AuditLogTable.stackTrace
|
|
||||||
|
|
||||||
enum class Type {
|
enum class Type {
|
||||||
JOIN,
|
JOIN,
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
package de.steamwar.sql;
|
package de.steamwar.sql;
|
||||||
|
|
||||||
import de.steamwar.ImplementationProvider;
|
import de.steamwar.ImplementationProvider;
|
||||||
import de.steamwar.ServerInfo;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -41,6 +40,4 @@ public interface SQLWrapper<M> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void additionalExceptionMetadata(StringBuilder builder);
|
void additionalExceptionMetadata(StringBuilder builder);
|
||||||
|
|
||||||
ServerInfo getServerInfo();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,8 +36,6 @@ object ExceptionTable: IntIdTable("Exception") {
|
|||||||
|
|
||||||
class SWException {
|
class SWException {
|
||||||
companion object {
|
companion object {
|
||||||
private val exceptionCache = HashSet<String>()
|
|
||||||
|
|
||||||
val cwd = System.getProperty("user.dir")
|
val cwd = System.getProperty("user.dir")
|
||||||
val serverName = File(cwd).name
|
val serverName = File(cwd).name
|
||||||
|
|
||||||
@@ -45,9 +43,7 @@ class SWException {
|
|||||||
fun init() = Unit
|
fun init() = Unit
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun log(message: String, stacktrace: String) = if (exceptionCache.contains(stacktrace)) Unit else useDb {
|
fun log(message: String, stacktrace: String) = useDb {
|
||||||
exceptionCache.add(stacktrace)
|
|
||||||
|
|
||||||
ExceptionTable.insert {
|
ExceptionTable.insert {
|
||||||
it[ExceptionTable.server] = serverName
|
it[ExceptionTable.server] = serverName
|
||||||
it[ExceptionTable.message] = generateMessage(message)
|
it[ExceptionTable.message] = generateMessage(message)
|
||||||
|
|||||||
@@ -0,0 +1,355 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.core
|
||||||
|
|
||||||
|
import com.sk89q.jnbt.ByteArrayTag
|
||||||
|
import com.sk89q.jnbt.ByteTag
|
||||||
|
import com.sk89q.jnbt.CompoundTag
|
||||||
|
import com.sk89q.jnbt.DoubleTag
|
||||||
|
import com.sk89q.jnbt.EndTag
|
||||||
|
import com.sk89q.jnbt.FloatTag
|
||||||
|
import com.sk89q.jnbt.IntArrayTag
|
||||||
|
import com.sk89q.jnbt.IntTag
|
||||||
|
import com.sk89q.jnbt.ListTag
|
||||||
|
import com.sk89q.jnbt.LongArrayTag
|
||||||
|
import com.sk89q.jnbt.LongTag
|
||||||
|
import com.sk89q.jnbt.ShortTag
|
||||||
|
import com.sk89q.jnbt.StringTag
|
||||||
|
import com.sk89q.jnbt.Tag
|
||||||
|
import com.sk89q.worldedit.WorldEdit
|
||||||
|
import com.sk89q.worldedit.WorldEditException
|
||||||
|
import com.sk89q.worldedit.extension.input.ParserContext
|
||||||
|
import com.sk89q.worldedit.extension.platform.Capability
|
||||||
|
import com.sk89q.worldedit.extension.platform.Platform
|
||||||
|
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard
|
||||||
|
import com.sk89q.worldedit.math.BlockVector3
|
||||||
|
import com.sk89q.worldedit.regions.CuboidRegion
|
||||||
|
import com.sk89q.worldedit.world.DataFixer
|
||||||
|
import com.sk89q.worldedit.world.block.BaseBlock
|
||||||
|
import com.sk89q.worldedit.world.block.BlockState
|
||||||
|
import com.sk89q.worldedit.world.block.BlockTypes
|
||||||
|
import java.io.DataInputStream
|
||||||
|
import java.io.IOException
|
||||||
|
import kotlin.collections.map
|
||||||
|
import kotlin.collections.set
|
||||||
|
|
||||||
|
class ChaosSchematicReader(
|
||||||
|
val stream: DataInputStream,
|
||||||
|
val wrapper: WorldEditWrapper14
|
||||||
|
) {
|
||||||
|
init {
|
||||||
|
println("ChaosSchematicReader init")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In reinen Unit-Tests (ohne Bukkit/WorldEdit-Plugin-Lifecycle) ist keine WorldEdit-Platform registriert.
|
||||||
|
* Daher darf der Reader beim Laden nicht hart daran scheitern.
|
||||||
|
*/
|
||||||
|
private val platform: Platform? = runCatching {
|
||||||
|
WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING)
|
||||||
|
}.getOrNull()
|
||||||
|
|
||||||
|
private val liveDataVersion: Int? = platform?.dataVersion
|
||||||
|
|
||||||
|
private var wrapped = false
|
||||||
|
|
||||||
|
var schematicVersion: Int? = null
|
||||||
|
var dataVersion: Int? = null
|
||||||
|
var metadata: Map<String, Any>? = null
|
||||||
|
|
||||||
|
var width: Short? = null
|
||||||
|
var height: Short? = null
|
||||||
|
var length: Short? = null
|
||||||
|
|
||||||
|
var offset: IntArray? = null
|
||||||
|
|
||||||
|
val blocks: Blocks = Blocks(null, null, null)
|
||||||
|
val entities = mutableListOf<Any>()
|
||||||
|
|
||||||
|
var fixer: DataFixer? = null
|
||||||
|
|
||||||
|
fun read(): BlockArrayClipboard {
|
||||||
|
require(stream.readByte() == 0x0A.toByte())
|
||||||
|
val tagName = stream.readUTF()
|
||||||
|
if (tagName != "Schematic") {
|
||||||
|
wrapped = true
|
||||||
|
stream.readByte()
|
||||||
|
stream.readUTF()
|
||||||
|
}
|
||||||
|
|
||||||
|
while (stream.readByte() != 0.toByte()) {
|
||||||
|
val name = stream.readUTF()
|
||||||
|
when (name) {
|
||||||
|
"Version" -> schematicVersion = stream.readInt()
|
||||||
|
"DataVersion" -> {
|
||||||
|
dataVersion = stream.readInt()
|
||||||
|
val targetVersion = liveDataVersion
|
||||||
|
if (targetVersion != null && dataVersion!! < targetVersion) {
|
||||||
|
fixer = platform?.dataFixer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"Metadata" -> metadata = readCompoundTag()
|
||||||
|
"Width" -> width = stream.readShort()
|
||||||
|
"Height" -> height = stream.readShort()
|
||||||
|
"Length" -> length = stream.readShort()
|
||||||
|
"Blocks" -> {
|
||||||
|
while (stream.readByte() != 0.toByte()) {
|
||||||
|
val n = stream.readUTF()
|
||||||
|
when (n) {
|
||||||
|
"Palette" -> blocks.palette = readCompoundTag() as Map<String, Int>
|
||||||
|
"Data" -> blocks.data = readTag(7.toByte()) as ByteArray
|
||||||
|
"BlockEntities" -> blocks.blockEntities = readTagCompoundList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"PaletteMax" -> stream.readInt()
|
||||||
|
"Palette" -> blocks.palette = readCompoundTag() as Map<String, Int>
|
||||||
|
"BlockEntities" -> blocks.blockEntities = readTagCompoundList()
|
||||||
|
"BlockData" -> blocks.data = readTag(7.toByte()) as ByteArray
|
||||||
|
"Offset" -> offset = readTag(11.toByte()) as IntArray
|
||||||
|
"Entities" -> entities.addAll(readTagCompoundList() as List<Any>)
|
||||||
|
"BiomePaletteMax" -> stream.readInt()
|
||||||
|
"BiomePalette" -> readCompoundTag()
|
||||||
|
"BiomeData" -> readTag(7.toByte())
|
||||||
|
"Biomes" -> {
|
||||||
|
while (stream.readByte() != 0.toByte()) {
|
||||||
|
val n = stream.readUTF()
|
||||||
|
when (n) {
|
||||||
|
"Palette" -> readCompoundTag()
|
||||||
|
"Data" -> readTag(7.toByte()) as ByteArray
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
println(this)
|
||||||
|
error("Unknown tag $name")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (wrapped) {
|
||||||
|
stream.readByte()
|
||||||
|
}
|
||||||
|
|
||||||
|
val parserContext = ParserContext()
|
||||||
|
parserContext.setRestricted(false)
|
||||||
|
parserContext.setTryLegacy(false)
|
||||||
|
parserContext.setPreferringWildcard(false)
|
||||||
|
|
||||||
|
val dv = dataVersion
|
||||||
|
val blockStates = blocks.palette!!.map {
|
||||||
|
val fixed = if (fixer != null && dv != null) {
|
||||||
|
fixer!!.fixUp(DataFixer.FixTypes.BLOCK_STATE, it.key, dv)
|
||||||
|
} else {
|
||||||
|
it.key
|
||||||
|
}
|
||||||
|
|
||||||
|
val blockstate = try {
|
||||||
|
WorldEdit.getInstance().blockFactory.parseFromInput(fixed, parserContext).toImmutableState()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
BlockTypes.AIR!!.defaultState
|
||||||
|
}
|
||||||
|
|
||||||
|
it.value to blockstate
|
||||||
|
}.toMap()
|
||||||
|
|
||||||
|
val tileEntities = blocks.blockEntities!!.map {
|
||||||
|
val entity = it as? Map<String, Any?> ?: error("Invalid entity")
|
||||||
|
val pos = entity["Pos"] as? IntArray ?: error("Invalid pos")
|
||||||
|
val x = pos[0]
|
||||||
|
val y = pos[1]
|
||||||
|
val z = pos[2]
|
||||||
|
|
||||||
|
val data = mutableMapOf<String, Any?>()
|
||||||
|
data.putAll(entity)
|
||||||
|
if (entity.containsKey("Data")) {
|
||||||
|
//data.putAll(entity["Data"] as Map<String, Any?>)
|
||||||
|
}
|
||||||
|
|
||||||
|
val fixed = if (fixer != null) {
|
||||||
|
wrapper.applyDataFixer(fixer!!, dataVersion!!, (wrapNbt(data) as CompoundTag).value)
|
||||||
|
} else (wrapNbt(data) as CompoundTag).value
|
||||||
|
|
||||||
|
BlockVector3.at(x, y, z) to fixed
|
||||||
|
}.toMap()
|
||||||
|
|
||||||
|
val offset = BlockVector3.at(offset!![0], offset!![1], offset!![2])
|
||||||
|
val region = CuboidRegion(offset, offset.add(width!! - 1, height!! - 1, length!! - 1))
|
||||||
|
|
||||||
|
val clipboard = BlockArrayClipboard(region)
|
||||||
|
clipboard.setOrigin(offset)
|
||||||
|
|
||||||
|
var index = 0
|
||||||
|
var i = 0
|
||||||
|
var value: Int
|
||||||
|
var varintLength: Int
|
||||||
|
while (i < blocks.data!!.size) {
|
||||||
|
value = 0
|
||||||
|
varintLength = 0
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
value = value or ((blocks.data!![i].toInt() and 127) shl (varintLength++ * 7))
|
||||||
|
if (varintLength > 5) {
|
||||||
|
throw IOException("VarInt too big (probably corrupted data)")
|
||||||
|
}
|
||||||
|
if ((blocks.data!![i].toInt() and 128) != 128) {
|
||||||
|
i++
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
// index = (y * length * width) + (z * width) + x
|
||||||
|
val y = index / (width!! * length!!)
|
||||||
|
val z = (index % (width!! * length!!)) / width!!
|
||||||
|
val x = (index % (width!! * length!!)) % width!!
|
||||||
|
val state: BlockState = blockStates[value] ?: error("Unknown block state $value")
|
||||||
|
val pt = BlockVector3.at(x, y, z)
|
||||||
|
try {
|
||||||
|
if (tileEntities.containsKey(pt)) {
|
||||||
|
clipboard.setBlock<BaseBlock?>(
|
||||||
|
clipboard.minimumPoint.add(pt),
|
||||||
|
state.toBaseBlock(CompoundTag(tileEntities[pt])
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
clipboard.setBlock<BlockState?>(clipboard.minimumPoint.add(pt), state)
|
||||||
|
}
|
||||||
|
} catch (e: WorldEditException) {
|
||||||
|
throw IOException("Failed to load a block in the schematic")
|
||||||
|
}
|
||||||
|
|
||||||
|
index++
|
||||||
|
}
|
||||||
|
|
||||||
|
return clipboard
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun readCompoundTag(): Map<String, Any> {
|
||||||
|
var tagId = 0.toByte()
|
||||||
|
val map = mutableMapOf<String, Any>()
|
||||||
|
|
||||||
|
while (stream.readByte().also { tagId = it } != 0.toByte()) {
|
||||||
|
val name = stream.readUTF()
|
||||||
|
map[name] = readTag(tagId)!!
|
||||||
|
}
|
||||||
|
return map
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun readTagCompoundList(): List<Any?> {
|
||||||
|
val typeId = stream.readByte()
|
||||||
|
val length = stream.readInt()
|
||||||
|
|
||||||
|
if (typeId == 0.toByte()) {
|
||||||
|
if (length != 0) {
|
||||||
|
throw IOException("Invalid TAG_List: typeId=TAG_End but length=$length")
|
||||||
|
}
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeId != 10.toByte()) {
|
||||||
|
throw IOException("Invalid TAG_List element type for (Block)Entities: expected TAG_Compound(10) but got $typeId")
|
||||||
|
}
|
||||||
|
|
||||||
|
val list = ArrayList<Any?>(length)
|
||||||
|
repeat(length) { list.add(readCompoundTag()) }
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun readTag(tagId: Byte): Any? = when (tagId.toInt()) {
|
||||||
|
0 -> null
|
||||||
|
1 -> stream.readByte()
|
||||||
|
2 -> stream.readShort()
|
||||||
|
3 -> stream.readInt()
|
||||||
|
4 -> stream.readLong()
|
||||||
|
5 -> stream.readFloat()
|
||||||
|
6 -> stream.readDouble()
|
||||||
|
7 -> {
|
||||||
|
val length = stream.readInt()
|
||||||
|
val ba = ByteArray(length)
|
||||||
|
stream.readFully(ba)
|
||||||
|
ba
|
||||||
|
}
|
||||||
|
8 -> stream.readUTF()
|
||||||
|
9 -> {
|
||||||
|
val typeId = stream.readByte()
|
||||||
|
val length = stream.readInt()
|
||||||
|
if (typeId == 0.toByte() && length != 0) {
|
||||||
|
throw IOException("Invalid TAG_List: typeId=TAG_End but length=$length")
|
||||||
|
}
|
||||||
|
val list = mutableListOf<Any?>()
|
||||||
|
repeat(length) { list.add(readTag(typeId)) }
|
||||||
|
list
|
||||||
|
}
|
||||||
|
10 -> readCompoundTag()
|
||||||
|
11 -> {
|
||||||
|
val length = stream.readInt()
|
||||||
|
IntArray(length) { stream.readInt() }
|
||||||
|
}
|
||||||
|
12 -> {
|
||||||
|
val length = stream.readInt()
|
||||||
|
LongArray(length) { stream.readLong() }
|
||||||
|
}
|
||||||
|
else -> error("Unknown tag type $tagId")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun wrapNbt(data: Any): Tag = when (data) {
|
||||||
|
is Byte -> ByteTag(data)
|
||||||
|
is Short -> ShortTag(data)
|
||||||
|
is Int -> IntTag(data)
|
||||||
|
is Long -> LongTag(data)
|
||||||
|
is Float -> FloatTag(data)
|
||||||
|
is Double -> DoubleTag(data)
|
||||||
|
is String -> StringTag(data)
|
||||||
|
is ByteArray -> ByteArrayTag(data)
|
||||||
|
is IntArray -> IntArrayTag(data)
|
||||||
|
is LongArray -> LongArrayTag(data)
|
||||||
|
is Map<*, *> -> CompoundTag(data.map { it.key as String to wrapNbt(it.value!!) }.toMap())
|
||||||
|
is List<*> -> ListTag(data.firstOrNull()?.let { nbtClass(it) } ?: EndTag::class.java, data.map { wrapNbt(it!!) })
|
||||||
|
else -> error("Unknown tag type $data")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun nbtClass(data: Any): Class<out Tag> = when (data) {
|
||||||
|
is Byte -> ByteTag::class.java
|
||||||
|
is Short -> ShortTag::class.java
|
||||||
|
is Int -> IntTag::class.java
|
||||||
|
is Long -> LongTag::class.java
|
||||||
|
is Float -> FloatTag::class.java
|
||||||
|
is Double -> DoubleTag::class.java
|
||||||
|
is String -> StringTag::class.java
|
||||||
|
is ByteArray -> ByteArrayTag::class.java
|
||||||
|
is IntArray -> IntArrayTag::class.java
|
||||||
|
is LongArray -> LongArrayTag::class.java
|
||||||
|
is Map<*, *> -> CompoundTag::class.java
|
||||||
|
is List<*> -> ListTag::class.java
|
||||||
|
else -> error("Unknown tag type $data, ${data::class.java.simpleName}")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "ChaosSchematicReader(platform=$platform, liveDataVersion=$liveDataVersion, wrapped=$wrapped, schematicVersion=$schematicVersion, dataVersion=$dataVersion, metadata=$metadata, width=$width, height=$height, length=$length, offset=${offset.contentToString()}, blocks=$blocks, entities=$entities, fixer=$fixer)"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
data class Blocks(
|
||||||
|
var data: ByteArray?,
|
||||||
|
var palette: Map<String, Int>?,
|
||||||
|
var blockEntities: List<Any?>?
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -89,7 +89,7 @@ public class WorldEditWrapper14 implements WorldEditWrapper {
|
|||||||
switch (schemFormat) {
|
switch (schemFormat) {
|
||||||
case SPONGE_V2:
|
case SPONGE_V2:
|
||||||
case SPONGE_V3:
|
case SPONGE_V3:
|
||||||
return new SpongeSchematicReader(new NBTInputStream(is), this).read();
|
return new ChaosSchematicReader(new DataInputStream(is), this).read();
|
||||||
case MCEDIT:
|
case MCEDIT:
|
||||||
return new MCEditSchematicReader(new NBTInputStream(is)).read();
|
return new MCEditSchematicReader(new NBTInputStream(is)).read();
|
||||||
default:
|
default:
|
||||||
@@ -576,8 +576,6 @@ public class WorldEditWrapper14 implements WorldEditWrapper {
|
|||||||
values.putIfAbsent("z", new IntTag(pt.getBlockZ()));
|
values.putIfAbsent("z", new IntTag(pt.getBlockZ()));
|
||||||
}
|
}
|
||||||
values.putIfAbsent("id", values.get("Id"));
|
values.putIfAbsent("id", values.get("Id"));
|
||||||
values.remove("Id");
|
|
||||||
values.remove("Pos");
|
|
||||||
if (fixer != null) {
|
if (fixer != null) {
|
||||||
tileEntity = wrapper.applyDataFixer(fixer, dataVersion, values);
|
tileEntity = wrapper.applyDataFixer(fixer, dataVersion, values);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -20,29 +20,32 @@
|
|||||||
package de.steamwar.core;
|
package de.steamwar.core;
|
||||||
|
|
||||||
import com.sk89q.jnbt.NBTInputStream;
|
import com.sk89q.jnbt.NBTInputStream;
|
||||||
|
import com.sk89q.jnbt.Tag;
|
||||||
import com.sk89q.worldedit.extension.platform.Actor;
|
import com.sk89q.worldedit.extension.platform.Actor;
|
||||||
import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
||||||
import com.sk89q.worldedit.extent.clipboard.io.BuiltInClipboardFormat;
|
import com.sk89q.worldedit.extent.clipboard.io.BuiltInClipboardFormat;
|
||||||
import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter;
|
import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter;
|
||||||
import com.sk89q.worldedit.extent.clipboard.io.MCEditSchematicReader;
|
import com.sk89q.worldedit.extent.clipboard.io.MCEditSchematicReader;
|
||||||
import com.sk89q.worldedit.extent.clipboard.io.sponge.SpongeSchematicV2Reader;
|
|
||||||
import com.sk89q.worldedit.extent.clipboard.io.sponge.SpongeSchematicV3Reader;
|
|
||||||
import com.sk89q.worldedit.math.Vector3;
|
import com.sk89q.worldedit.math.Vector3;
|
||||||
import com.sk89q.worldedit.math.transform.Transform;
|
import com.sk89q.worldedit.math.transform.Transform;
|
||||||
import com.sk89q.worldedit.regions.Region;
|
import com.sk89q.worldedit.regions.Region;
|
||||||
import com.sk89q.worldedit.session.ClipboardHolder;
|
import com.sk89q.worldedit.session.ClipboardHolder;
|
||||||
|
import com.sk89q.worldedit.world.DataFixer;
|
||||||
import de.steamwar.sql.NodeData;
|
import de.steamwar.sql.NodeData;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.util.Vector;
|
import org.bukkit.util.Vector;
|
||||||
import org.enginehub.linbus.stream.LinBinaryIO;
|
import org.enginehub.linbus.tree.LinCompoundTag;
|
||||||
|
import org.enginehub.linbus.tree.LinTag;
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class WorldEditWrapper21 implements WorldEditWrapper {
|
public class WorldEditWrapper21 extends WorldEditWrapper18 {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream getPlayerClipboard(Player player) {
|
public InputStream getPlayerClipboard(Player player) {
|
||||||
@@ -72,22 +75,10 @@ public class WorldEditWrapper21 implements WorldEditWrapper {
|
|||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("removal")
|
@SuppressWarnings("removal")
|
||||||
public Clipboard getClipboard(InputStream is, NodeData.SchematicFormat ignored) throws IOException {
|
public Clipboard getClipboard(InputStream is, NodeData.SchematicFormat ignored) throws IOException {
|
||||||
ResetableInputStream ris = new ResetableInputStream(is);
|
return switch (ignored) {
|
||||||
for (NodeData.SchematicFormat schemFormat : NodeData.SchematicFormat.values()) {
|
case MCEDIT -> new MCEditSchematicReader(new NBTInputStream(is)).read();
|
||||||
try {
|
case SPONGE_V2, SPONGE_V3 -> new ChaosSchematicReader(new DataInputStream(is), this).read();
|
||||||
Clipboard clipboard = switch (schemFormat) {
|
};
|
||||||
case MCEDIT -> new MCEditSchematicReader(new NBTInputStream(ris)).read();
|
|
||||||
case SPONGE_V2 -> new SpongeSchematicV2Reader(LinBinaryIO.read(new DataInputStream(ris))).read();
|
|
||||||
case SPONGE_V3 -> new SpongeSchematicV3Reader(LinBinaryIO.read(new DataInputStream(ris))).read();
|
|
||||||
};
|
|
||||||
ris.close();
|
|
||||||
return clipboard;
|
|
||||||
} catch (Exception e) {
|
|
||||||
// Ignore
|
|
||||||
}
|
|
||||||
ris.reset();
|
|
||||||
}
|
|
||||||
throw new IOException("No clipboard found");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ResetableInputStream extends InputStream {
|
private class ResetableInputStream extends InputStream {
|
||||||
|
|||||||
@@ -19,14 +19,9 @@
|
|||||||
|
|
||||||
package de.steamwar.core;
|
package de.steamwar.core;
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
|
|
||||||
public class CheckpointUtils {
|
public class CheckpointUtils {
|
||||||
private CheckpointUtils() {}
|
private CheckpointUtils() {}
|
||||||
|
|
||||||
@Getter
|
|
||||||
private static boolean restored = false;
|
|
||||||
|
|
||||||
public static void signalHandler() {
|
public static void signalHandler() {
|
||||||
try {
|
try {
|
||||||
CheckpointUtilsJ9.signalHandler();
|
CheckpointUtilsJ9.signalHandler();
|
||||||
@@ -38,7 +33,6 @@ public class CheckpointUtils {
|
|||||||
public static void freeze() {
|
public static void freeze() {
|
||||||
try {
|
try {
|
||||||
CheckpointUtilsJ9.freeze();
|
CheckpointUtilsJ9.freeze();
|
||||||
restored = true;
|
|
||||||
} catch (NoClassDefFoundError e) {
|
} catch (NoClassDefFoundError e) {
|
||||||
//ignore
|
//ignore
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,8 +19,6 @@
|
|||||||
|
|
||||||
package de.steamwar.sql;
|
package de.steamwar.sql;
|
||||||
|
|
||||||
import de.steamwar.ServerInfo;
|
|
||||||
import de.steamwar.core.CheckpointUtils;
|
|
||||||
import de.steamwar.core.Core;
|
import de.steamwar.core.Core;
|
||||||
import de.steamwar.data.GameModeConfigUtils;
|
import de.steamwar.data.GameModeConfigUtils;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
@@ -70,9 +68,4 @@ public class SQLWrapperImpl implements SQLWrapper<Material> {
|
|||||||
builder.append(world.getName()).append(" ");
|
builder.append(world.getName()).append(" ");
|
||||||
builder.append("\nServer: ").append(SERVER_VERSION);
|
builder.append("\nServer: ").append(SERVER_VERSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public ServerInfo getServerInfo() {
|
|
||||||
return new ServerInfo(Core.getServerName(), Core.getVersion(), CheckpointUtils.isRestored());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user