You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
178 lines
6.5 KiB
178 lines
6.5 KiB
package net.deliciousreya.minecraftportal.model
|
|
|
|
import com.google.common.collect.ImmutableMap
|
|
import com.google.protobuf.InvalidProtocolBufferException
|
|
import net.deliciousreya.minecraftportal.extensions.MINERAL_MAPPING
|
|
import net.deliciousreya.minecraftportal.proto.PortalSaveDataProtos
|
|
import org.bukkit.Location
|
|
import org.bukkit.Material
|
|
import org.bukkit.Server
|
|
import java.io.File
|
|
import java.io.IOException
|
|
import java.lang.IllegalArgumentException
|
|
|
|
class PortalDataStore {
|
|
var saveDataTo: File? = null
|
|
var useBackup: File? = null
|
|
val portalTypes: ImmutableMap<Material, PortalType>
|
|
init {
|
|
val portalTypesBuilder = ImmutableMap.builder<Material, PortalType>()
|
|
for (mineralType in MINERAL_MAPPING.keys) {
|
|
portalTypesBuilder.put(mineralType, PortalType(mineralType))
|
|
}
|
|
portalTypes = portalTypesBuilder.build()
|
|
}
|
|
|
|
fun isLocationInPortalOrChamber(location: Location): PortalFrame? {
|
|
for (type in this.portalTypes.values) {
|
|
if (type.isEmpty) {
|
|
continue
|
|
}
|
|
val result = type.isLocationInPortalOrChamber(location)
|
|
if (result != null) {
|
|
return result.frame
|
|
}
|
|
}
|
|
return null
|
|
}
|
|
|
|
fun isLocationInPortalChamber(location: Location): PortalFrame? {
|
|
for (type in this.portalTypes.values) {
|
|
if (type.isEmpty) {
|
|
continue
|
|
}
|
|
val result = type.isLocationInPortalChamber(location)
|
|
if (result != null) {
|
|
return result.frame
|
|
}
|
|
}
|
|
return null
|
|
}
|
|
|
|
fun loadFromAndAutoSaveTo(server: Server, mainFile: File, backupFile: File? = null) {
|
|
var firstException:Throwable? = null
|
|
val loadingMainFile = mainFile.exists()
|
|
if (loadingMainFile) {
|
|
try {
|
|
val protoData = mainFile.readBytes()
|
|
val proto = PortalSaveDataProtos.PortalSaveData.parseFrom(protoData)
|
|
this.loadFromProto(server, proto)
|
|
} catch (e: IOException) {
|
|
if (backupFile == null || !backupFile.exists()) {
|
|
throw DeserializationException(cause = e)
|
|
} else {
|
|
firstException = e
|
|
}
|
|
} catch (e: InvalidProtocolBufferException) {
|
|
if (backupFile == null || !backupFile.exists()) {
|
|
throw DeserializationException(cause = e)
|
|
} else {
|
|
firstException = e
|
|
}
|
|
}
|
|
}
|
|
if ((!loadingMainFile || firstException != null) && backupFile != null && backupFile.exists()) {
|
|
try {
|
|
val protoData = backupFile.readBytes()
|
|
backupFile.renameTo(mainFile)
|
|
val proto = PortalSaveDataProtos.PortalSaveData.parseFrom(protoData)
|
|
this.loadFromProto(server, proto)
|
|
} catch (e: IOException) {
|
|
throw DeserializationException(cause = firstException ?: e)
|
|
} catch (e: InvalidProtocolBufferException) {
|
|
throw DeserializationException(cause = firstException ?: e)
|
|
}
|
|
}
|
|
this.saveDataTo = mainFile
|
|
this.useBackup = backupFile
|
|
}
|
|
|
|
fun unload() {
|
|
this.clear()
|
|
this.saveDataTo = null
|
|
this.useBackup = null
|
|
}
|
|
|
|
fun onAfterChanged() {
|
|
val destination = saveDataTo ?: return
|
|
val tempDestination = File.createTempFile("PortalSaveData", ".tmp.binproto")
|
|
val backupDestination = useBackup
|
|
if (backupDestination != null && destination.exists()) {
|
|
backupDestination.delete()
|
|
destination.copyTo(backupDestination)
|
|
}
|
|
val proto = toProto()
|
|
val bytes = proto.toByteArray()
|
|
tempDestination.writeBytes(bytes)
|
|
destination.delete()
|
|
tempDestination.renameTo(destination)
|
|
backupDestination?.delete()
|
|
}
|
|
|
|
/** Marks the new portal as active, and removes the one that needs to be deactivated, if any. */
|
|
fun activateAndReplacePortal(frame: PortalFrame): PortalFrame? {
|
|
val type = getPortalType(frame)
|
|
val result = type.addOrReplacePortal(Portal(frame, type))?.frame
|
|
onAfterChanged()
|
|
return result
|
|
}
|
|
|
|
/** Deactivates the given portal. Returns the other portal if it needs to be closed. */
|
|
fun deactivatePortal(frame: PortalFrame): PortalFrame? {
|
|
val type = getPortalType(frame)
|
|
val result = type.removePortalWithFrame(frame)?.frame
|
|
onAfterChanged()
|
|
return result
|
|
}
|
|
|
|
fun getPortalType(frame: PortalFrame): PortalType {
|
|
return portalTypes[frame.mineral] ?: throw IllegalArgumentException("There are no portals of type ${frame.mineral}")
|
|
}
|
|
|
|
/** Gets the other portal, if the given frame is one of the portals in a pair. */
|
|
fun getOtherPortal(frame: PortalFrame): PortalFrame? {
|
|
val type = getPortalType(frame)
|
|
return type.getOtherPortal(frame)?.frame
|
|
}
|
|
|
|
/** Forgets all the data without actually affecting the world. */
|
|
fun clear() {
|
|
for (portalType in this.portalTypes.values) {
|
|
portalType.clear()
|
|
}
|
|
}
|
|
|
|
fun loadFromProto(server: Server, proto: PortalSaveDataProtos.PortalSaveData) {
|
|
clear()
|
|
for (portalProto in proto.unpairedPortalsList) {
|
|
val portal = portalProto.toPortal(server, portalTypes)
|
|
portal.type.loadUnpairedPortal(portal)
|
|
}
|
|
for (portalPair in proto.pairedPortalsList) {
|
|
val olderPortal = portalPair.older.toPortal(server, portalTypes)
|
|
val newerPortal = portalPair.newer.toPortal(server, portalTypes)
|
|
if (olderPortal.type != newerPortal.type) {
|
|
throw DeserializationException("Pair had non-matching types: ${olderPortal.type} and ${newerPortal.type}")
|
|
}
|
|
olderPortal.type.loadPairedPortals(olderPortal, newerPortal)
|
|
}
|
|
}
|
|
|
|
fun toProto(): PortalSaveDataProtos.PortalSaveData {
|
|
val builder = PortalSaveDataProtos.PortalSaveData.newBuilder()
|
|
for (portalType in this.portalTypes.values) {
|
|
when {
|
|
portalType.isEmpty -> {
|
|
// Nothing here to save. Just skip it.
|
|
}
|
|
portalType.isPaired -> {
|
|
builder.addPairedPortals(portalType.toProto())
|
|
}
|
|
else -> {
|
|
builder.addUnpairedPortals(portalType.getOnlyPortal.toProto())
|
|
}
|
|
}
|
|
}
|
|
return builder.build()
|
|
}
|
|
} |