Quantum batteries
This commit is contained in:
parent
2da5219739
commit
69b8115395
@ -191,6 +191,13 @@ private fun misc(provider: MatteryLanguageProvider) {
|
|||||||
misc("matter_bottler.switch_mode", "Switch work mode")
|
misc("matter_bottler.switch_mode", "Switch work mode")
|
||||||
|
|
||||||
misc("container.matter_panel.number_input", "Input replication task count")
|
misc("container.matter_panel.number_input", "Input replication task count")
|
||||||
|
|
||||||
|
misc("item.quantum_battery.creative", "Fill this to win Minecraft.")
|
||||||
|
misc("item.quantum_battery.creative2", "See ya after millions of stars burn out.")
|
||||||
|
misc("item.quantum_battery.creative_power", "Stored energy: %s / Infinity")
|
||||||
|
|
||||||
|
misc("item.quantum_link_id", "Quantum link ID: %s")
|
||||||
|
misc("item.quantum_description", "This item is sharing it's contents with other similar items using quantum entanglement")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -287,6 +294,10 @@ private fun items(provider: MatteryLanguageProvider) {
|
|||||||
add(MItems.BATTERY_CAPACITOR, "Capacitor Battery")
|
add(MItems.BATTERY_CAPACITOR, "Capacitor Battery")
|
||||||
add(MItems.BATTERY_CREATIVE, "Creative Battery")
|
add(MItems.BATTERY_CREATIVE, "Creative Battery")
|
||||||
|
|
||||||
|
add(MItems.QUANTUM_BATTERY, "Quantum Battery")
|
||||||
|
add(MItems.QUANTUM_CAPACITOR, "Quantum Capacitor")
|
||||||
|
add(MItems.QUANTUM_BATTERY_CREATIVE, "Quantum Creative Battery")
|
||||||
|
|
||||||
add(MItems.TRITANIUM_SWORD, "Tritanium Sword")
|
add(MItems.TRITANIUM_SWORD, "Tritanium Sword")
|
||||||
add(MItems.TRITANIUM_PICKAXE, "Tritanium Pickaxe")
|
add(MItems.TRITANIUM_PICKAXE, "Tritanium Pickaxe")
|
||||||
add(MItems.TRITANIUM_SHOVEL, "Tritanium Shovel")
|
add(MItems.TRITANIUM_SHOVEL, "Tritanium Shovel")
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
package ru.dbotthepony.mc.otm
|
package ru.dbotthepony.mc.otm
|
||||||
|
|
||||||
|
import net.minecraft.server.MinecraftServer
|
||||||
import net.minecraft.world.level.Level
|
import net.minecraft.world.level.Level
|
||||||
import net.minecraftforge.event.TickEvent
|
import net.minecraftforge.event.TickEvent
|
||||||
import net.minecraftforge.event.TickEvent.ServerTickEvent
|
import net.minecraftforge.event.TickEvent.ServerTickEvent
|
||||||
@ -26,6 +27,18 @@ private val postWorldTick = WeakHashMap<Level, ArrayList<IConditionalTickable>>(
|
|||||||
private val preWorldTickOnce = WeakHashMap<Level, ArrayList<ITickable>>()
|
private val preWorldTickOnce = WeakHashMap<Level, ArrayList<ITickable>>()
|
||||||
private val postWorldTickOnce = WeakHashMap<Level, ArrayList<ITickable>>()
|
private val postWorldTickOnce = WeakHashMap<Level, ArrayList<ITickable>>()
|
||||||
|
|
||||||
|
private var _server: MinecraftServer? = null
|
||||||
|
private var _serverThread: Thread? = null
|
||||||
|
|
||||||
|
fun isServerThread(): Boolean {
|
||||||
|
return Thread.currentThread() === _serverThread
|
||||||
|
}
|
||||||
|
|
||||||
|
val MINECRAFT_SERVER: MinecraftServer
|
||||||
|
get() {
|
||||||
|
return _server ?: throw NullPointerException("Not running a server!")
|
||||||
|
}
|
||||||
|
|
||||||
// Flag whenever is server alive
|
// Flag whenever is server alive
|
||||||
// To avoid baby deadlocks caused by minecraft engine design issues
|
// To avoid baby deadlocks caused by minecraft engine design issues
|
||||||
var SERVER_IS_DYING = true
|
var SERVER_IS_DYING = true
|
||||||
@ -216,23 +229,28 @@ private fun clear() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SubscribeEvent(priority = EventPriority.HIGHEST)
|
@SubscribeEvent(priority = EventPriority.HIGHEST)
|
||||||
fun onServerStarting(event: ServerAboutToStartEvent?) {
|
fun onServerStarting(event: ServerAboutToStartEvent) {
|
||||||
clear()
|
clear()
|
||||||
SERVER_IS_DYING = false
|
SERVER_IS_DYING = false
|
||||||
|
_server = event.server
|
||||||
|
_serverThread = Thread.currentThread()
|
||||||
}
|
}
|
||||||
|
|
||||||
@SubscribeEvent(priority = EventPriority.HIGHEST)
|
@SubscribeEvent(priority = EventPriority.HIGHEST)
|
||||||
fun onServerStopping(event: ServerStoppingEvent?) {
|
fun onServerStopping(event: ServerStoppingEvent) {
|
||||||
clear()
|
clear()
|
||||||
SERVER_IS_DYING = true
|
SERVER_IS_DYING = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@SubscribeEvent(priority = EventPriority.HIGHEST)
|
@SubscribeEvent(priority = EventPriority.HIGHEST)
|
||||||
fun onServerStopped(event: ServerStoppedEvent?) {
|
fun onServerStopped(event: ServerStoppedEvent) {
|
||||||
if (!SERVER_IS_DYING) {
|
if (!SERVER_IS_DYING) {
|
||||||
LOGGER.fatal("ServerStoppingEvent did not fire. If server has crashed this is normal. However, if server finished it's work 'gracefully' this is a bug.")
|
LOGGER.fatal("ServerStoppingEvent did not fire. If server has crashed this is normal. However, if server finished it's work 'gracefully' this is a bug.")
|
||||||
|
|
||||||
clear()
|
clear()
|
||||||
SERVER_IS_DYING = true
|
SERVER_IS_DYING = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_server = null
|
||||||
|
_serverThread = null
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ import java.util.ArrayList
|
|||||||
* 3. This data can not be stored inside unshared (server only) nbt tag because, again, mods prone to use and interact with
|
* 3. This data can not be stored inside unshared (server only) nbt tag because, again, mods prone to use and interact with
|
||||||
* it wrong, causing loss of stored data or mods exposing full content of a drive inside their own tag (which cause very real "NBT size too large"
|
* it wrong, causing loss of stored data or mods exposing full content of a drive inside their own tag (which cause very real "NBT size too large"
|
||||||
* network kicks, often locking players out of server/singleplayer worlds
|
* network kicks, often locking players out of server/singleplayer worlds
|
||||||
* 4. net.minecraft.world.level.saveddata.SaveData is for storing everything inside one dat file, which
|
* 4. [net.minecraft.world.level.saveddata.SavedData] is for storing everything inside one dat file, which
|
||||||
* is performance tanking, because we have to write *entire* NBT on each save, not the data of Drives that are dirty
|
* is performance tanking, because we have to write *entire* NBT on each save, not the data of Drives that are dirty
|
||||||
* 5. Mods which check items for being stack-able even with stack size of 1 gonna compare nbt tag,
|
* 5. Mods which check items for being stack-able even with stack size of 1 gonna compare nbt tag,
|
||||||
* which will be performance tanking due to clause 1.
|
* which will be performance tanking due to clause 1.
|
||||||
|
193
src/main/kotlin/ru/dbotthepony/mc/otm/item/QuantumBatteryItem.kt
Normal file
193
src/main/kotlin/ru/dbotthepony/mc/otm/item/QuantumBatteryItem.kt
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.item
|
||||||
|
|
||||||
|
import net.minecraft.ChatFormatting
|
||||||
|
import net.minecraft.core.Direction
|
||||||
|
import net.minecraft.nbt.CompoundTag
|
||||||
|
import net.minecraft.nbt.IntTag
|
||||||
|
import net.minecraft.network.chat.Component
|
||||||
|
import net.minecraft.world.item.Item
|
||||||
|
import net.minecraft.world.item.ItemStack
|
||||||
|
import net.minecraft.world.item.Rarity
|
||||||
|
import net.minecraft.world.item.TooltipFlag
|
||||||
|
import net.minecraft.world.level.Level
|
||||||
|
import net.minecraftforge.common.capabilities.Capability
|
||||||
|
import net.minecraftforge.common.capabilities.ForgeCapabilities
|
||||||
|
import net.minecraftforge.common.capabilities.ICapabilityProvider
|
||||||
|
import net.minecraftforge.common.util.INBTSerializable
|
||||||
|
import net.minecraftforge.common.util.LazyOptional
|
||||||
|
import ru.dbotthepony.mc.otm.*
|
||||||
|
import ru.dbotthepony.mc.otm.capability.IMatteryEnergyStorage
|
||||||
|
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
||||||
|
import ru.dbotthepony.mc.otm.core.ImpreciseFraction
|
||||||
|
import ru.dbotthepony.mc.otm.core.formatPower
|
||||||
|
import ru.dbotthepony.mc.otm.saveddata.SavedCountingMap
|
||||||
|
import ru.dbotthepony.mc.otm.saveddata.SavedMapDelegate
|
||||||
|
|
||||||
|
class QuantumBatteryItem : Item {
|
||||||
|
private inner class Power : IMatteryEnergyStorage, ICapabilityProvider, INBTSerializable<IntTag> {
|
||||||
|
private val resolver = LazyOptional.of { this }
|
||||||
|
var delegate = SavedMapDelegate(ImpreciseFraction.ZERO)
|
||||||
|
|
||||||
|
override fun <T : Any?> getCapability(cap: Capability<T>, side: Direction?): LazyOptional<T> {
|
||||||
|
if (cap == ForgeCapabilities.ENERGY || cap == MatteryCapability.ENERGY) {
|
||||||
|
return resolver.cast()
|
||||||
|
}
|
||||||
|
|
||||||
|
return LazyOptional.empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun extractEnergyOuter(howMuch: ImpreciseFraction, simulate: Boolean): ImpreciseFraction {
|
||||||
|
return extractEnergyInner(howMuch, simulate)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun extractEnergyInner(howMuch: ImpreciseFraction, simulate: Boolean): ImpreciseFraction {
|
||||||
|
if (howMuch.isNegative) {
|
||||||
|
return ImpreciseFraction.ZERO
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isCreative) {
|
||||||
|
val newEnergy = (delegate.value - howMuch).moreThanZero()
|
||||||
|
|
||||||
|
if (!simulate) {
|
||||||
|
delegate.value = newEnergy
|
||||||
|
}
|
||||||
|
|
||||||
|
return delegate.value - newEnergy
|
||||||
|
}
|
||||||
|
|
||||||
|
val newEnergy = (delegate.value - howMuch.coerceAtMost(throughput!!)).moreThanZero()
|
||||||
|
|
||||||
|
if (!simulate) {
|
||||||
|
delegate.value = newEnergy
|
||||||
|
}
|
||||||
|
|
||||||
|
return delegate.value - newEnergy
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun receiveEnergyOuter(howMuch: ImpreciseFraction, simulate: Boolean): ImpreciseFraction {
|
||||||
|
return receiveEnergyInner(howMuch, simulate)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun receiveEnergyInner(howMuch: ImpreciseFraction, simulate: Boolean): ImpreciseFraction {
|
||||||
|
if (howMuch.isNegative) {
|
||||||
|
return ImpreciseFraction.ZERO
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isCreative) {
|
||||||
|
if (!simulate) {
|
||||||
|
delegate.value += howMuch
|
||||||
|
}
|
||||||
|
|
||||||
|
return howMuch
|
||||||
|
}
|
||||||
|
|
||||||
|
val newEnergy = (delegate.value + howMuch.coerceAtMost(throughput!!)).coerceAtMost(capacity!!)
|
||||||
|
|
||||||
|
if (!simulate) {
|
||||||
|
delegate.value = newEnergy
|
||||||
|
}
|
||||||
|
|
||||||
|
return newEnergy - delegate.value
|
||||||
|
}
|
||||||
|
|
||||||
|
override val batteryLevel: ImpreciseFraction
|
||||||
|
get() = delegate.value
|
||||||
|
|
||||||
|
override val maxBatteryLevel: ImpreciseFraction
|
||||||
|
get() = capacity ?: (batteryLevel + ImpreciseFraction.LONG_MAX_VALUE)
|
||||||
|
|
||||||
|
override val missingPower: ImpreciseFraction
|
||||||
|
get() = if (isCreative) ImpreciseFraction.LONG_MAX_VALUE else super.missingPower
|
||||||
|
|
||||||
|
override fun canExtract(): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun canReceive(): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun serializeNBT(): IntTag {
|
||||||
|
if (isServerThread() && delegate.parent == null) {
|
||||||
|
val old = delegate
|
||||||
|
delegate = saveData!!.factorize()
|
||||||
|
delegate.value = old.value
|
||||||
|
}
|
||||||
|
|
||||||
|
return IntTag.valueOf(if (delegate.parent == null) Int.MIN_VALUE else delegate.index)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserializeNBT(nbt: IntTag) {
|
||||||
|
if (nbt.asInt == Int.MIN_VALUE) {
|
||||||
|
delegate = SavedMapDelegate(ImpreciseFraction.ZERO)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate = saveData?.computeIfAbsent(nbt.asInt) ?: SavedMapDelegate(null, nbt.asInt, delegate.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val isCreative: Boolean
|
||||||
|
val capacity: ImpreciseFraction?
|
||||||
|
val throughput: ImpreciseFraction?
|
||||||
|
|
||||||
|
val saveDataID: String
|
||||||
|
|
||||||
|
val saveData: SavedCountingMap<SavedMapDelegate<ImpreciseFraction>>? get() {
|
||||||
|
if (isServerThread()) {
|
||||||
|
return MINECRAFT_SERVER.overworld().dataStorage.computeIfAbsent({
|
||||||
|
SavedMapDelegate.makeMap(ImpreciseFraction::serializeNBT, ImpreciseFraction.Companion::deserializeNBT, ImpreciseFraction.ZERO).load(it)
|
||||||
|
}, {
|
||||||
|
SavedMapDelegate.makeMap(ImpreciseFraction::serializeNBT, ImpreciseFraction.Companion::deserializeNBT, ImpreciseFraction.ZERO)
|
||||||
|
}, saveDataID) ?: throw NullPointerException("Unable to get save data for $this in ${MINECRAFT_SERVER.overworld()}")
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(saveDataID: String) : super(Properties().stacksTo(1).rarity(Rarity.EPIC).tab(OverdriveThatMatters.INSTANCE.CREATIVE_TAB)) {
|
||||||
|
isCreative = true
|
||||||
|
capacity = null
|
||||||
|
throughput = null
|
||||||
|
this.saveDataID = "otm_$saveDataID".intern()
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(saveDataID: String, capacity: ImpreciseFraction, io: ImpreciseFraction) : super(Properties().stacksTo(1).tab(OverdriveThatMatters.INSTANCE.CREATIVE_TAB)) {
|
||||||
|
isCreative = false
|
||||||
|
this.capacity = capacity
|
||||||
|
this.throughput = io
|
||||||
|
this.saveDataID = "otm_$saveDataID".intern()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun initCapabilities(stack: ItemStack, nbt: CompoundTag?): ICapabilityProvider {
|
||||||
|
return Power()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun appendHoverText(
|
||||||
|
itemStack: ItemStack,
|
||||||
|
p_41422_: Level?,
|
||||||
|
components: MutableList<Component>,
|
||||||
|
p_41424_: TooltipFlag
|
||||||
|
) {
|
||||||
|
super.appendHoverText(itemStack, p_41422_, components, p_41424_)
|
||||||
|
|
||||||
|
val power = itemStack.getCapability(MatteryCapability.ENERGY).orThrow() as Power
|
||||||
|
|
||||||
|
components.add(TranslatableComponent("otm.item.quantum_description").withStyle(ChatFormatting.DARK_GRAY))
|
||||||
|
|
||||||
|
if (isCreative) {
|
||||||
|
components.add(TranslatableComponent("otm.item.quantum_battery.creative_power", power.batteryLevel.formatPower()).withStyle(ChatFormatting.GRAY))
|
||||||
|
components.add(TranslatableComponent("otm.item.quantum_battery.creative").withStyle(ChatFormatting.DARK_GRAY))
|
||||||
|
components.add(TranslatableComponent("otm.item.quantum_battery.creative2").withStyle(ChatFormatting.DARK_GRAY))
|
||||||
|
} else {
|
||||||
|
components.add(TranslatableComponent("otm.item.power.normal.storage", power.batteryLevel.formatPower(), capacity!!.formatPower()).withStyle(ChatFormatting.GRAY))
|
||||||
|
components.add(TranslatableComponent(
|
||||||
|
"otm.item.power.normal.throughput",
|
||||||
|
throughput!!.formatPower(),
|
||||||
|
throughput.formatPower()
|
||||||
|
).withStyle(ChatFormatting.GRAY))
|
||||||
|
}
|
||||||
|
|
||||||
|
components.add(TranslatableComponent("otm.item.quantum_link_id", power.delegate.index).withStyle(ChatFormatting.DARK_GRAY))
|
||||||
|
}
|
||||||
|
}
|
@ -176,6 +176,10 @@ object MItems {
|
|||||||
val BATTERY_CAPACITOR: Item by registry.register(MNames.BATTERY_CAPACITOR) { BatteryItem(ImpreciseFraction(150_000), ImpreciseFraction(15000), ImpreciseFraction(15000)) }
|
val BATTERY_CAPACITOR: Item by registry.register(MNames.BATTERY_CAPACITOR) { BatteryItem(ImpreciseFraction(150_000), ImpreciseFraction(15000), ImpreciseFraction(15000)) }
|
||||||
val BATTERY_CREATIVE: Item by registry.register(MNames.BATTERY_CREATIVE) { BatteryItem() }
|
val BATTERY_CREATIVE: Item by registry.register(MNames.BATTERY_CREATIVE) { BatteryItem() }
|
||||||
|
|
||||||
|
val QUANTUM_BATTERY: Item by registry.register(MNames.QUANTUM_BATTERY) { QuantumBatteryItem(MNames.QUANTUM_BATTERY, ImpreciseFraction(20_000_000), ImpreciseFraction(10_000)) }
|
||||||
|
val QUANTUM_CAPACITOR: Item by registry.register(MNames.QUANTUM_CAPACITOR) { QuantumBatteryItem(MNames.QUANTUM_CAPACITOR, ImpreciseFraction(1_000_000), ImpreciseFraction(200_000)) }
|
||||||
|
val QUANTUM_BATTERY_CREATIVE: Item by registry.register(MNames.QUANTUM_BATTERY_CREATIVE) { QuantumBatteryItem(MNames.QUANTUM_BATTERY_CREATIVE) }
|
||||||
|
|
||||||
val BATTERIES = LazyList(
|
val BATTERIES = LazyList(
|
||||||
{ BATTERY_CRUDE },
|
{ BATTERY_CRUDE },
|
||||||
{ BATTERY_BASIC },
|
{ BATTERY_BASIC },
|
||||||
|
@ -34,22 +34,6 @@ object MNames {
|
|||||||
const val GRAVITATION_STABILIZER_LENS = "gravitation_stabilizer_lens"
|
const val GRAVITATION_STABILIZER_LENS = "gravitation_stabilizer_lens"
|
||||||
|
|
||||||
const val CARGO_CRATE = "cargo_crate"
|
const val CARGO_CRATE = "cargo_crate"
|
||||||
const val CARGO_CRATE_WHITE = "cargo_crate_white" // нужен рецепт
|
|
||||||
const val CARGO_CRATE_ORANGE = "cargo_crate_orange" // нужен рецепт
|
|
||||||
const val CARGO_CRATE_MAGENTA = "cargo_crate_magenta" // нужен рецепт
|
|
||||||
const val CARGO_CRATE_LIGHT_BLUE = "cargo_crate_light_blue" // нужен рецепт
|
|
||||||
const val CARGO_CRATE_YELLOW = "cargo_crate_yellow" // нужен рецепт
|
|
||||||
const val CARGO_CRATE_LIME = "cargo_crate_lime" // нужен рецепт
|
|
||||||
const val CARGO_CRATE_PINK = "cargo_crate_pink" // нужен рецепт
|
|
||||||
const val CARGO_CRATE_GRAY = "cargo_crate_gray" // нужен рецепт
|
|
||||||
const val CARGO_CRATE_LIGHT_GRAY = "cargo_crate_light_gray" // нужен рецепт
|
|
||||||
const val CARGO_CRATE_CYAN = "cargo_crate_cyan" // нужен рецепт
|
|
||||||
const val CARGO_CRATE_PURPLE = "cargo_crate_purple" // нужен рецепт
|
|
||||||
const val CARGO_CRATE_BLUE = "cargo_crate_blue" // нужен рецепт
|
|
||||||
const val CARGO_CRATE_BROWN = "cargo_crate_brown" // нужен рецепт
|
|
||||||
const val CARGO_CRATE_GREEN = "cargo_crate_green" // нужен рецепт
|
|
||||||
const val CARGO_CRATE_RED = "cargo_crate_red" // нужен рецепт
|
|
||||||
const val CARGO_CRATE_BLACK = "cargo_crate_black" // нужен рецепт
|
|
||||||
|
|
||||||
const val STORAGE_BUS = "storage_bus"
|
const val STORAGE_BUS = "storage_bus"
|
||||||
const val STORAGE_IMPORTER = "storage_importer"
|
const val STORAGE_IMPORTER = "storage_importer"
|
||||||
@ -89,6 +73,10 @@ object MNames {
|
|||||||
const val BATTERY_CAPACITOR = "battery_capacitor"
|
const val BATTERY_CAPACITOR = "battery_capacitor"
|
||||||
const val BATTERY_CREATIVE = "battery_creative"
|
const val BATTERY_CREATIVE = "battery_creative"
|
||||||
|
|
||||||
|
const val QUANTUM_BATTERY = "quantum_battery"
|
||||||
|
const val QUANTUM_CAPACITOR = "quantum_capacitor"
|
||||||
|
const val QUANTUM_BATTERY_CREATIVE = "quantum_battery_creative"
|
||||||
|
|
||||||
const val MATTER_CAPACITOR_PARTS = "matter_capacitor_parts"
|
const val MATTER_CAPACITOR_PARTS = "matter_capacitor_parts"
|
||||||
const val MATTER_CAPACITOR_BASIC = "matter_capacitor_basic"
|
const val MATTER_CAPACITOR_BASIC = "matter_capacitor_basic"
|
||||||
const val MATTER_CAPACITOR_NORMAL = "matter_capacitor_normal"
|
const val MATTER_CAPACITOR_NORMAL = "matter_capacitor_normal"
|
||||||
|
@ -0,0 +1,411 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.saveddata
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectFunction
|
||||||
|
import net.minecraft.nbt.CompoundTag
|
||||||
|
import net.minecraft.nbt.IntTag
|
||||||
|
import net.minecraft.nbt.ListTag
|
||||||
|
import net.minecraft.nbt.Tag
|
||||||
|
import net.minecraft.world.level.saveddata.SavedData
|
||||||
|
import org.apache.logging.log4j.LogManager
|
||||||
|
import ru.dbotthepony.mc.otm.set
|
||||||
|
|
||||||
|
class SavedMapDelegate<V>(val parent: SavedCountingMap<SavedMapDelegate<V>>?, val index: Int, value: V) {
|
||||||
|
constructor(value: V) : this(null, -1, value)
|
||||||
|
|
||||||
|
var value: V = value
|
||||||
|
set(value) {
|
||||||
|
if (field != value) {
|
||||||
|
field = value
|
||||||
|
parent?.isDirty = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun <V> makeMap(
|
||||||
|
serializer: (value: V) -> Tag,
|
||||||
|
deserializer: (nbt: Tag) -> V,
|
||||||
|
defaultValue: V
|
||||||
|
): SavedCountingMap<SavedMapDelegate<V>> {
|
||||||
|
return SavedCountingMap(
|
||||||
|
serializer = { _, value, _ -> serializer.invoke(value.value) },
|
||||||
|
deserializer = { map, nbt, index -> SavedMapDelegate(map, index, deserializer.invoke(nbt)) },
|
||||||
|
factory = { map, index -> SavedMapDelegate(map, index, defaultValue) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SavedCountingMap<T>(
|
||||||
|
val serializer: (map: SavedCountingMap<T>, value: T, index: Int) -> Tag,
|
||||||
|
val deserializer: (map: SavedCountingMap<T>, nbt: Tag, index: Int) -> T,
|
||||||
|
val factory: (map: SavedCountingMap<T>, index: Int) -> T
|
||||||
|
) : SavedData(), MutableMap<Int, T> {
|
||||||
|
var nextIndex = 0
|
||||||
|
private set
|
||||||
|
|
||||||
|
fun computeIfAbsent(index: Int): T {
|
||||||
|
return map.computeIfAbsent(index, Int2ObjectFunction {
|
||||||
|
isDirty = true
|
||||||
|
return@Int2ObjectFunction factory.invoke(this, it)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun factorize(): T {
|
||||||
|
return computeIfAbsent(nextIndex++)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val map = Int2ObjectAVLTreeMap<T>()
|
||||||
|
|
||||||
|
override val size: Int
|
||||||
|
get() = map.size
|
||||||
|
|
||||||
|
override fun containsKey(key: Int): Boolean {
|
||||||
|
return map.containsKey(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun containsValue(value: T): Boolean {
|
||||||
|
return map.containsValue(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun get(key: Int): T? {
|
||||||
|
return map[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isEmpty(): Boolean {
|
||||||
|
return map.isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
override val entries: MutableSet<MutableMap.MutableEntry<Int, T>> by lazy {
|
||||||
|
object : MutableSet<MutableMap.MutableEntry<Int, T>> {
|
||||||
|
override fun add(element: MutableMap.MutableEntry<Int, T>): Boolean {
|
||||||
|
this@SavedCountingMap[element.key] = element.value
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addAll(elements: Collection<MutableMap.MutableEntry<Int, T>>): Boolean {
|
||||||
|
for (value in elements) {
|
||||||
|
add(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun clear() {
|
||||||
|
this@SavedCountingMap.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val setParent = this@SavedCountingMap.map.int2ObjectEntrySet()
|
||||||
|
|
||||||
|
override fun iterator(): MutableIterator<MutableMap.MutableEntry<Int, T>> {
|
||||||
|
return object : MutableIterator<MutableMap.MutableEntry<Int, T>> {
|
||||||
|
private val parent = setParent.iterator()
|
||||||
|
|
||||||
|
override fun hasNext(): Boolean {
|
||||||
|
return parent.hasNext()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun next(): MutableMap.MutableEntry<Int, T> {
|
||||||
|
return parent.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun remove() {
|
||||||
|
parent.remove()
|
||||||
|
isDirty = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun remove(element: MutableMap.MutableEntry<Int, T>): Boolean {
|
||||||
|
if (setParent.remove(element)) {
|
||||||
|
isDirty = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeAll(elements: Collection<MutableMap.MutableEntry<Int, T>>): Boolean {
|
||||||
|
if (setParent.removeAll(elements)) {
|
||||||
|
isDirty = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun retainAll(elements: Collection<MutableMap.MutableEntry<Int, T>>): Boolean {
|
||||||
|
if (setParent.retainAll(elements)) {
|
||||||
|
isDirty = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override val size: Int
|
||||||
|
get() = setParent.size
|
||||||
|
|
||||||
|
override fun contains(element: MutableMap.MutableEntry<Int, T>): Boolean {
|
||||||
|
return setParent.contains(element)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun containsAll(elements: Collection<MutableMap.MutableEntry<Int, T>>): Boolean {
|
||||||
|
return setParent.containsAll(elements)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isEmpty(): Boolean {
|
||||||
|
return setParent.isEmpty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override val keys: MutableSet<Int> by lazy {
|
||||||
|
object : MutableSet<Int> {
|
||||||
|
val parent = this@SavedCountingMap.map.keys
|
||||||
|
|
||||||
|
override fun add(element: Int): Boolean {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addAll(elements: Collection<Int>): Boolean {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun clear() {
|
||||||
|
this@SavedCountingMap.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun iterator(): MutableIterator<Int> {
|
||||||
|
return object : MutableIterator<Int> {
|
||||||
|
private val parentIterator = parent.iterator()
|
||||||
|
|
||||||
|
override fun hasNext(): Boolean {
|
||||||
|
return parentIterator.hasNext()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun next(): Int {
|
||||||
|
return parentIterator.nextInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun remove() {
|
||||||
|
parentIterator.remove()
|
||||||
|
isDirty = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun remove(element: Int): Boolean {
|
||||||
|
return this@SavedCountingMap.remove(element) != null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeAll(elements: Collection<Int>): Boolean {
|
||||||
|
var changes = false
|
||||||
|
|
||||||
|
for (element in elements) {
|
||||||
|
changes = remove(element) || changes
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun retainAll(elements: Collection<Int>): Boolean {
|
||||||
|
val iterator = this.iterator()
|
||||||
|
var changes = false
|
||||||
|
|
||||||
|
for (element in iterator) {
|
||||||
|
if (element !in elements) {
|
||||||
|
iterator.remove()
|
||||||
|
changes = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes
|
||||||
|
}
|
||||||
|
|
||||||
|
override val size: Int
|
||||||
|
get() = parent.size
|
||||||
|
|
||||||
|
override fun contains(element: Int): Boolean {
|
||||||
|
return parent.contains(element)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun containsAll(elements: Collection<Int>): Boolean {
|
||||||
|
return parent.containsAll(elements)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isEmpty(): Boolean {
|
||||||
|
return parent.isEmpty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override val values: MutableCollection<T> by lazy {
|
||||||
|
object : MutableCollection<T> {
|
||||||
|
private val parent = this@SavedCountingMap.map.values
|
||||||
|
|
||||||
|
override val size: Int
|
||||||
|
get() = parent.size
|
||||||
|
|
||||||
|
override fun contains(element: T): Boolean {
|
||||||
|
return parent.contains(element)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun containsAll(elements: Collection<T>): Boolean {
|
||||||
|
return parent.containsAll(elements)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isEmpty(): Boolean {
|
||||||
|
return parent.isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun add(element: T): Boolean {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addAll(elements: Collection<T>): Boolean {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun clear() {
|
||||||
|
this@SavedCountingMap.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun iterator(): MutableIterator<T> {
|
||||||
|
return object : MutableIterator<T> {
|
||||||
|
private val parentIterator = parent.iterator()
|
||||||
|
|
||||||
|
override fun hasNext(): Boolean {
|
||||||
|
return parentIterator.hasNext()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun next(): T {
|
||||||
|
return parentIterator.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun remove() {
|
||||||
|
parentIterator.remove()
|
||||||
|
isDirty = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun remove(element: T): Boolean {
|
||||||
|
val indexOf = this@SavedCountingMap.map.firstNotNullOfOrNull { if (it.value == element) it.key else null } ?: return false
|
||||||
|
this@SavedCountingMap.remove(indexOf)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeAll(elements: Collection<T>): Boolean {
|
||||||
|
var changes = false
|
||||||
|
|
||||||
|
for (element in elements) {
|
||||||
|
changes = remove(element) || changes
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun retainAll(elements: Collection<T>): Boolean {
|
||||||
|
val iterator = this.iterator()
|
||||||
|
var changes = false
|
||||||
|
|
||||||
|
for (element in iterator) {
|
||||||
|
if (element !in elements) {
|
||||||
|
iterator.remove()
|
||||||
|
changes = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun clear() {
|
||||||
|
isDirty = true
|
||||||
|
return map.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun put(key: Int, value: T): T? {
|
||||||
|
val existing = map.put(key, value)
|
||||||
|
|
||||||
|
if (existing == value) {
|
||||||
|
return existing
|
||||||
|
}
|
||||||
|
|
||||||
|
isDirty = true
|
||||||
|
return existing
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun putAll(from: Map<out Int, T>) {
|
||||||
|
isDirty = true
|
||||||
|
return map.putAll(from)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun remove(key: Int): T? {
|
||||||
|
val removed = map.remove(key)
|
||||||
|
|
||||||
|
if (removed != null) {
|
||||||
|
isDirty = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return removed
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun save(output: CompoundTag): CompoundTag {
|
||||||
|
output["map"] = ListTag().also {
|
||||||
|
for ((key, value) in this.map) {
|
||||||
|
val compound = CompoundTag()
|
||||||
|
|
||||||
|
try {
|
||||||
|
compound["value"] = this.serializer.invoke(this, value, key)
|
||||||
|
it.add(compound)
|
||||||
|
} catch(err: Exception) {
|
||||||
|
LOGGER.error("Unable to serialize $value at $key", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
compound["key"] = key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output["next_index"] = nextIndex
|
||||||
|
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
fun load(input: CompoundTag): SavedCountingMap<T> {
|
||||||
|
this.map.clear()
|
||||||
|
|
||||||
|
val map = input["map"] as? ListTag ?: ListTag()
|
||||||
|
var maximal = -1
|
||||||
|
|
||||||
|
for (value in map) {
|
||||||
|
if (value !is CompoundTag) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
val key = (value["key"] as IntTag?)?.asInt ?: continue
|
||||||
|
val nbt = value["value"] ?: continue
|
||||||
|
|
||||||
|
try {
|
||||||
|
val deserialized = this.deserializer.invoke(this, nbt, key)
|
||||||
|
this.map.put(key, deserialized)
|
||||||
|
} catch(err: Exception) {
|
||||||
|
LOGGER.error("Unable to deserialize value in $this at $key", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
maximal = maximal.coerceAtLeast(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
nextIndex = (input["next_index"] as IntTag?)?.asInt ?: (maximal + 1)
|
||||||
|
|
||||||
|
isDirty = false
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val LOGGER = LogManager.getLogger()
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user