1
0
Fork 0
The portal creating plugin for Minecraft.
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

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()
}
}