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.
271 lines
10 KiB
271 lines
10 KiB
package net.deliciousreya.minecraftportal.model
|
|
|
|
import com.google.common.collect.ImmutableList
|
|
import com.google.common.collect.ImmutableMap
|
|
import com.google.protobuf.InvalidProtocolBufferException
|
|
import net.deliciousreya.minecraftportal.extensions.COLOR_MAPPING
|
|
import net.deliciousreya.minecraftportal.extensions.MINERAL_MAPPING
|
|
import net.deliciousreya.minecraftportal.extensions.toColorProto
|
|
import net.deliciousreya.minecraftportal.extensions.toMaterial
|
|
import net.deliciousreya.minecraftportal.proto.PortalSaveDataProtos
|
|
import org.bukkit.Location
|
|
import org.bukkit.Material
|
|
import org.bukkit.Server
|
|
import org.bukkit.plugin.Plugin
|
|
import java.io.File
|
|
import java.io.IOException
|
|
import java.lang.IllegalArgumentException
|
|
import java.util.logging.Logger
|
|
|
|
class PortalDataStore (){
|
|
var saveDataTo: File? = null
|
|
var useBackup: File? = null
|
|
private val userColors: MutableMap<String, Material> = mutableMapOf()
|
|
private val portalTypes: ImmutableMap<Material, ImmutableMap<Material, PortalType>>
|
|
private val allPortals: ImmutableList<PortalType>
|
|
init {
|
|
val allPortalsBuilder = ImmutableList.builder<PortalType>()
|
|
val portalTypesBuilder = ImmutableMap.builder<Material, ImmutableMap<Material, PortalType>>()
|
|
for (mineralType in MINERAL_MAPPING.keys) {
|
|
val colors = ImmutableMap.Builder<Material, PortalType>()
|
|
for (color in COLOR_MAPPING.keys) {
|
|
val portalType = PortalType(mineralType, color)
|
|
colors.put(color, portalType)
|
|
allPortalsBuilder.add(portalType)
|
|
}
|
|
portalTypesBuilder.put(mineralType, colors.build())
|
|
}
|
|
portalTypes = portalTypesBuilder.build()
|
|
allPortals = allPortalsBuilder.build()
|
|
}
|
|
|
|
fun isLocationInPortalOrChamber(location: Location): PortalFrame? {
|
|
for (type in this.allPortals) {
|
|
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.allPortals) {
|
|
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 validateWorldOnStartup(logger: Logger) {
|
|
for (portalType in this.allPortals) {
|
|
val oldPortal = portalType.oldestActivePortal
|
|
val newPortal = portalType.newestActivePortal
|
|
if (newPortal != null && !validatePortal(newPortal)) {
|
|
logger.warning("Portal $newPortal was no longer valid!")
|
|
portalType.newestActivePortal = null
|
|
}
|
|
if (oldPortal != null && !validatePortal(oldPortal)) {
|
|
logger.warning("Portal $oldPortal was no longer valid!")
|
|
portalType.oldestActivePortal = portalType.newestActivePortal
|
|
portalType.newestActivePortal = null
|
|
}
|
|
}
|
|
}
|
|
|
|
fun validatePortal(portal: Portal): Boolean {
|
|
return portal.frame.mineral == portal.type.mineral && portal.frame.color == portal.type.color && portal.frame.isActivePortal()
|
|
}
|
|
|
|
fun launchEffectsOnStartup(plugin: Plugin) {
|
|
for (portalType in this.allPortals) {
|
|
startEffectsFor(portalType, plugin)
|
|
}
|
|
}
|
|
|
|
fun startEffectsFor(portal: PortalFrame, plugin: Plugin) {
|
|
startEffectsFor(getPortalType(portal), plugin)
|
|
}
|
|
|
|
fun startEffectsFor(portalType: PortalType, plugin: Plugin) {
|
|
val oldPortal = portalType.oldestActivePortal
|
|
val newPortal = portalType.newestActivePortal
|
|
if (portalType.isPaired) {
|
|
oldPortal?.startEffects(plugin)
|
|
oldPortal?.frame?.open()
|
|
newPortal?.startEffects(plugin)
|
|
newPortal?.frame?.open()
|
|
} else if (!portalType.isEmpty) {
|
|
oldPortal?.stopEffects()
|
|
oldPortal?.frame?.close()
|
|
newPortal?.stopEffects()
|
|
newPortal?.frame?.close()
|
|
}
|
|
}
|
|
|
|
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))
|
|
onAfterChanged()
|
|
return result?.frame
|
|
}
|
|
|
|
/** 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)
|
|
onAfterChanged()
|
|
return result?.frame
|
|
}
|
|
|
|
fun getPortalType(frame: PortalFrame): PortalType {
|
|
return portalTypes[frame.mineral]?.get(frame.color) ?: throw IllegalArgumentException("There are no portals of type ${frame.mineral}/${frame.color}")
|
|
}
|
|
|
|
/** 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.allPortals) {
|
|
portalType.oldestActivePortal?.stopEffects()
|
|
portalType.newestActivePortal?.stopEffects()
|
|
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)
|
|
}
|
|
for (userData in proto.userDataList) {
|
|
userColors[userData.name] = userData.color.toMaterial()
|
|
}
|
|
}
|
|
|
|
fun toProto(): PortalSaveDataProtos.PortalSaveData {
|
|
val builder = PortalSaveDataProtos.PortalSaveData.newBuilder()
|
|
for (portalType in this.allPortals) {
|
|
when {
|
|
portalType.isEmpty -> {
|
|
// Nothing here to save. Just skip it.
|
|
}
|
|
portalType.isPaired -> {
|
|
builder.addPairedPortals(portalType.toProto())
|
|
}
|
|
else -> {
|
|
builder.addUnpairedPortals(portalType.getOnlyPortal.toProto())
|
|
}
|
|
}
|
|
}
|
|
for (user in this.userColors.keys) {
|
|
builder.addUserData(PortalSaveDataProtos.UserData.newBuilder().setName(user).setColor(this.userColors[user]?.toColorProto()))
|
|
}
|
|
return builder.build()
|
|
}
|
|
|
|
fun startTeleporting(portal: PortalFrame, plugin: Plugin) {
|
|
getPortalType(portal).beginTeleportation(plugin)
|
|
}
|
|
|
|
fun stopTeleporting(portal: PortalFrame) {
|
|
getPortalType(portal).cancelTeleportation()
|
|
}
|
|
|
|
private val defaultColor = Material.GRAY_STAINED_GLASS
|
|
|
|
fun getColorFor(name: String): Material {
|
|
return userColors.getOrDefault(name, defaultColor)
|
|
}
|
|
|
|
fun setColorFor(name: String, color: Material) {
|
|
if (color == defaultColor) {
|
|
userColors.remove(name)
|
|
} else {
|
|
userColors[name] = color
|
|
}
|
|
onAfterChanged()
|
|
}
|
|
} |