From 09db4ad9e98ff8953e0d4bfd722ca4ed959d10dd Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Mon, 19 Jun 2023 21:37:43 +0700 Subject: [PATCH] Revisit how android research datapacks work Instead of "feature results" provide them with just "results", which reference arbitrary features from registry Do the same to research description, with its own registry --- .../mc/otm/datagen/ResearchData.kt | 58 ++-- .../mc/otm/android/AndroidResearch.kt | 90 +----- .../android/AndroidResearchDataProvider.kt | 2 - .../otm/android/AndroidResearchDescription.kt | 133 +++++++++ .../mc/otm/android/AndroidResearchManager.kt | 55 ---- .../mc/otm/android/AndroidResearchResult.kt | 180 ++++++++++++ .../mc/otm/android/AndroidResearchType.kt | 278 ++++-------------- .../android/feature/EnderTeleporterFeature.kt | 19 +- .../android/feature/FallDampenersFeature.kt | 6 - .../otm/android/feature/ItemMagnetFeature.kt | 8 - .../otm/android/feature/JumpBoostFeature.kt | 9 - .../android/feature/NanobotsArmorFeature.kt | 28 -- .../otm/android/feature/ShockwaveFeature.kt | 8 - .../ru/dbotthepony/mc/otm/data/Codecs.kt | 33 +++ .../dbotthepony/mc/otm/data/ComponentCodec.kt | 25 ++ .../mc/otm/data/SerializedFunctionRegistry.kt | 225 -------------- .../dbotthepony/mc/otm/data/SingletonCodec.kt | 16 + .../mc/otm/registry/AndroidFeatures.kt | 28 +- .../dbotthepony/mc/otm/registry/MRegistry.kt | 16 +- 19 files changed, 508 insertions(+), 709 deletions(-) create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearchDescription.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearchResult.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/data/Codecs.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/data/ComponentCodec.kt delete mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/data/SerializedFunctionRegistry.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/data/SingletonCodec.kt diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/ResearchData.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/ResearchData.kt index 5d8dc61b8..00599e085 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/ResearchData.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/ResearchData.kt @@ -4,6 +4,9 @@ import net.minecraft.tags.ItemTags import net.minecraft.world.item.Items import net.minecraftforge.common.Tags import ru.dbotthepony.mc.otm.OverdriveThatMatters +import ru.dbotthepony.mc.otm.android.AndroidResearchDescriptions +import ru.dbotthepony.mc.otm.android.AndroidResearchResult +import ru.dbotthepony.mc.otm.android.AndroidResearchResults import ru.dbotthepony.mc.otm.android.AndroidResearchType import ru.dbotthepony.mc.otm.android.feature.EnderTeleporterFeature import ru.dbotthepony.mc.otm.android.feature.FallDampenersFeature @@ -132,11 +135,13 @@ fun addResearchData(serializer: Consumer, lang: MatteryLang ) ) .addItem(MItemTags.COPPER_WIRES, 4 + i * 2) - .addFeatureResult(OverdriveThatMatters.loc(MNames.LIMB_OVERCLOCKING), i) if (i > 0) { + research.addFeatureLevel(AndroidFeatures.LIMB_OVERCLOCKING) research.addPrerequisite(OverdriveThatMatters.loc(MNames.LIMB_OVERCLOCKING_LIST[i - 1]), rigid = true) research.addItem(MItemTags.GOLD_WIRES, i * 2) + } else { + research.addFeatureResult(AndroidFeatures.LIMB_OVERCLOCKING) } research.build() @@ -155,18 +160,20 @@ fun addResearchData(serializer: Consumer, lang: MatteryLang (i + 1) * 15 ) ) - .addFeatureResult(AndroidFeatures.ATTACK_BOOST, i) .addBlocker(NANOBOTS_ARMOR) if (i > 0) { + research.addFeatureLevel(AndroidFeatures.ATTACK_BOOST) research.addPrerequisite(OverdriveThatMatters.loc(MNames.ATTACK_BOOST_LIST[i - 1]), rigid = true) + } else { + research.addFeatureResult(AndroidFeatures.ATTACK_BOOST) } research.build() }) regenList.add(run { - val regeneration = AndroidResearchType.Builder(modLocation(MNames.NANOBOTS_REGENERATION_LIST[i])) + val research = AndroidResearchType.Builder(modLocation(MNames.NANOBOTS_REGENERATION_LIST[i])) .withExperience(20 + i * 6) .withIconText(TextComponent((i + 1).toString())) .withIcon(ResearchIcons.ICON_NANOBOTS) @@ -179,15 +186,16 @@ fun addResearchData(serializer: Consumer, lang: MatteryLang .addItem(MItems.MATTER_CAPACITOR_PARTS, 1) .addItem(Items.SUGAR, 2 + i * 2) .addItem(Tags.Items.DUSTS_REDSTONE, 2 + i * 2) - .addFeatureResult(AndroidFeatures.NANOBOTS_REGENERATION, i) if (i > 0) { - regeneration.addPrerequisite(OverdriveThatMatters.loc(MNames.NANOBOTS_REGENERATION_LIST[i - 1]), rigid = true) + research.addFeatureLevel(AndroidFeatures.NANOBOTS_REGENERATION) + research.addPrerequisite(OverdriveThatMatters.loc(MNames.NANOBOTS_REGENERATION_LIST[i - 1]), rigid = true) } else { - regeneration.addPrerequisite(OverdriveThatMatters.loc(MNames.NANOBOTS), rigid = true) + research.addPrerequisite(OverdriveThatMatters.loc(MNames.NANOBOTS), rigid = true) + research.addFeatureResult(AndroidFeatures.NANOBOTS_REGENERATION) } - regeneration.build() + research.build() }) } @@ -216,10 +224,7 @@ fun addResearchData(serializer: Consumer, lang: MatteryLang .addItem(MItemTags.TRITANIUM_PLATES, 2 + i * 2) .addItem(Items.SUGAR, 1 + i) .addItem(MItems.ELECTROMAGNET) - .addFeatureResult(AndroidFeatures.NANOBOTS_ARMOR, 0, - transformersUp = listOf(NanobotsArmorFeature.STRENGTH_TRANSFORMER_UP.bind(level)), - transformersDown = listOf(NanobotsArmorFeature.STRENGTH_TRANSFORMER_DOWN.bind(level)), - ) + .addResult(AndroidResearchResults.NANOBOTS_ARMOR_STRENGTH) .build() }) @@ -243,10 +248,7 @@ fun addResearchData(serializer: Consumer, lang: MatteryLang ) ) .addItem(Tags.Items.DUSTS_REDSTONE, 4 + i * 4) - .addFeatureResult(AndroidFeatures.NANOBOTS_ARMOR, 0, - transformersUp = listOf(NanobotsArmorFeature.SPEED_TRANSFORMER_UP.bind(level)), - transformersDown = listOf(NanobotsArmorFeature.SPEED_TRANSFORMER_DOWN.bind(level)), - ) + .addResult(AndroidResearchResults.NANOBOTS_ARMOR_SPEED) .build() }) } @@ -261,7 +263,7 @@ fun addResearchData(serializer: Consumer, lang: MatteryLang AndroidResearchType.Builder(modLocation(MNames.SHOCKWAVE)) .withExperience(40) .withDescription(0 .. 1) - .appendDescription(ShockwaveFeature.POWER_COST_DESCRIPTION) + .withDescription(AndroidResearchDescriptions.SHOCKWAVE) .withIcon(ResearchIcons.ICON_SHOCKWAVE) .addFeatureResult(AndroidFeatures.SHOCKWAVE) .addPrerequisite(attackBoostList[2]) @@ -276,7 +278,7 @@ fun addResearchData(serializer: Consumer, lang: MatteryLang AndroidResearchType.Builder(modLocation(MNames.ITEM_MAGNET)) .withExperience(28) .withDescription(0 .. 1) - .appendDescription(ItemMagnetFeature.POWER_COST_DESCRIPTION) + .withDescription(AndroidResearchDescriptions.ITEM_MAGNET) .withIcon(ResearchIcons.ICON_ITEM_MAGNET) .addFeatureResult(AndroidFeatures.ITEM_MAGNET) .addPrerequisite(STEP_ASSIST) @@ -292,9 +294,9 @@ fun addResearchData(serializer: Consumer, lang: MatteryLang AndroidResearchType.Builder(modLocation(MNames.FALL_DAMPENERS + "_1")) .withExperience(25) .withDescription() - .appendDescription(FallDampenersFeature.DESCRIPTION.bind(1)) + .withDescription(AndroidResearchDescriptions.FALL_DAMPENERS.Instance(1)) .withIcon(ResearchIcons.ICON_FEATHER_FALLING) - .addFeatureResult(AndroidFeatures.FALL_DAMPENERS, 0) + .addFeatureResult(AndroidFeatures.FALL_DAMPENERS) .addPrerequisite(STEP_ASSIST) .addItem(MItems.ELECTROMAGNET, 2) .addItem(ItemTags.WOOL, 2) @@ -305,9 +307,9 @@ fun addResearchData(serializer: Consumer, lang: MatteryLang AndroidResearchType.Builder(modLocation(MNames.FALL_DAMPENERS + "_2")) .withExperience(30) .withDescription() - .appendDescription(FallDampenersFeature.DESCRIPTION.bind(2)) + .withDescription(AndroidResearchDescriptions.FALL_DAMPENERS.Instance(2)) .withIcon(ResearchIcons.ICON_FEATHER_FALLING) - .addFeatureResult(AndroidFeatures.FALL_DAMPENERS, 1) + .addFeatureLevel(AndroidFeatures.FALL_DAMPENERS) .addPrerequisite(FALL_DAMPENERS_1) .addItem(MItemTags.GOLD_PLATES, 2) .addItem(MItemTags.COPPER_WIRES, 4) @@ -319,9 +321,9 @@ fun addResearchData(serializer: Consumer, lang: MatteryLang AndroidResearchType.Builder(modLocation(MNames.FALL_DAMPENERS + "_3")) .withExperience(35) .withDescription(0 .. 1) - .appendDescription(FallDampenersFeature.DESCRIPTION.bind(3)) + .withDescription(AndroidResearchDescriptions.FALL_DAMPENERS.Instance(2)) .withIcon(ResearchIcons.ICON_FEATHER_FALLING) - .addFeatureResult(AndroidFeatures.FALL_DAMPENERS, 2) + .addFeatureLevel(AndroidFeatures.FALL_DAMPENERS) .addPrerequisite(FALL_DAMPENERS_2) .addItem(MItemTags.ADVANCED_CIRCUIT, 2) .addItem(Tags.Items.GEMS_DIAMOND, 4) @@ -337,7 +339,7 @@ fun addResearchData(serializer: Consumer, lang: MatteryLang AndroidResearchType.Builder(modLocation(MNames.ENDER_TELEPORTER)) .withExperience(35) .withDescription() - .appendDescription(EnderTeleporterFeature.POWER_COST_DESCRIPTION) + .withDescription(AndroidResearchDescriptions.ENDER_TELEPORTER) .withIcon(ResearchIcons.ICON_ENDER_TELEPORT) .addFeatureResult(AndroidFeatures.ENDER_TELEPORTER) .addPrerequisite(FALL_DAMPENERS_1) @@ -368,9 +370,9 @@ fun addResearchData(serializer: Consumer, lang: MatteryLang AndroidResearchType.Builder(modLocation(MNames.JUMP_BOOST + "_1")) .withExperience(27) .withDescription(0 .. 1) - .appendDescription(JumpBoostFeature.POWER_COST_DESCRIPTION) + .withDescription(AndroidResearchDescriptions.JUMP_BOOST) .withIcon(ResearchIcons.ICON_JUMP_BOOST) - .addFeatureResult(AndroidFeatures.JUMP_BOOST, 0) + .addFeatureResult(AndroidFeatures.JUMP_BOOST) .addItem(MItemTags.PISTONS, 2) .addItem(MItemTags.GOLD_WIRES, 4) .addItem(MItems.ELECTROMAGNET, 2) @@ -382,9 +384,9 @@ fun addResearchData(serializer: Consumer, lang: MatteryLang AndroidResearchType.Builder(modLocation(MNames.JUMP_BOOST + "_2")) .withExperience(34) .withDescription() - .appendDescription(JumpBoostFeature.POWER_COST_DESCRIPTION) + .withDescription(AndroidResearchDescriptions.JUMP_BOOST) .withIcon(ResearchIcons.ICON_JUMP_BOOST) - .addFeatureResult(AndroidFeatures.JUMP_BOOST, 1) + .addFeatureLevel(AndroidFeatures.JUMP_BOOST) .addItem(MItems.ELECTRIC_PARTS, 4) .addItem(MItems.ELECTROMAGNET, 4) .addPrerequisite(JUMP_BOOST_1) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearch.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearch.kt index c9cc9e664..aa03048e3 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearch.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearch.kt @@ -1,7 +1,6 @@ package ru.dbotthepony.mc.otm.android import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream -import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap import net.minecraft.ChatFormatting import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.ListTag @@ -15,7 +14,6 @@ import ru.dbotthepony.mc.otm.capability.MatteryPlayerCapability import ru.dbotthepony.mc.otm.capability.awareItemsStream import ru.dbotthepony.mc.otm.core.TextComponent import ru.dbotthepony.mc.otm.core.TranslatableComponent -import ru.dbotthepony.mc.otm.core.addAll import ru.dbotthepony.mc.otm.core.nbt.getCompoundList import ru.dbotthepony.mc.otm.core.registryName import ru.dbotthepony.mc.otm.core.nbt.set @@ -46,62 +44,19 @@ class AndroidResearch(val type: AndroidResearchType, val capability: MatteryPlay return } - onUnResearch() + onRefunded() isResearched = false } - private data class RememberResearchLevel(val level: Int?) - - private val oldResearchLevel = Object2ObjectArrayMap, RememberResearchLevel>() - - fun onUnResearch() { - for (feature in type.resolvedFeatures) { - val level = oldResearchLevel[feature.feature] - val get = capability.getFeature(feature.feature) - - if (level != null && get != null) { - if (get.level == feature.level) { - if (level.level == null) { - capability.removeFeature(feature.feature) - } else { - get.level = level.level - - for (transformer in type.features.first { it.id == feature.feature.registryName }.transformersDown) { - transformer.apply(this to get) - } - } - } - } + fun onRefunded() { + for (result in type.results) { + result.onRefunded(this) } - - oldResearchLevel.clear() } fun onResearched() { - oldResearchLevel.clear() - - try { - for (feature in type.resolvedFeatures) { - var get = capability.getFeature(feature.feature) - - if (get == null) { - get = capability.addFeature(feature.feature) - get.level = feature.level - oldResearchLevel[feature.feature] = RememberResearchLevel(null) - } else { - if (get.level < feature.level) { - oldResearchLevel[feature.feature] = RememberResearchLevel(feature.level) - get.level = feature.level - } - } - - for (transformer in type.features.first { it.id == feature.feature.registryName }.transformersUp) { - transformer.apply(this to get) - } - } - } catch(err: Throwable) { - oldResearchLevel.clear() - throw err + for (result in type.results) { + result.onResearched(this) } } @@ -297,7 +252,10 @@ class AndroidResearch(val type: AndroidResearchType, val capability: MatteryPlay val tooltipLines: List get() { val lines = ArrayList() lines.add(type.displayName) - lines.addAll(type.description.iterator()) + + for (line in type.description) { + line.addLines(this, lines) + } return lines } @@ -382,38 +340,10 @@ class AndroidResearch(val type: AndroidResearchType, val capability: MatteryPlay override fun serializeNBT(): CompoundTag { return CompoundTag().also { it["researched"] = isResearched - - it["oldResearchLevel"] = ListTag().also { - for ((k, v) in oldResearchLevel) { - it.add(CompoundTag().also { - it["key"] = k.registryName!!.toString() - it["value"] = CompoundTag().also { - it["isPresent"] = v.level != null - - if (v.level != null) { - it["value"] = v.level - } - } - }) - } - } } } override fun deserializeNBT(nbt: CompoundTag) { isResearched = nbt.getBoolean("researched") - - oldResearchLevel.clear() - - for (tag in nbt.getCompoundList("oldResearchLevel")) { - val key = tag.getString("key") - val type = MRegistry.ANDROID_FEATURES.getValue(ResourceLocation(key)) ?: continue - val value = tag.getCompound("value") - - val isPresent = value.getBoolean("isPresent") - val int = value.getInt("value") - - oldResearchLevel[type] = RememberResearchLevel(if (isPresent) int else null) - } } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearchDataProvider.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearchDataProvider.kt index 779263e7d..ee9b57c9a 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearchDataProvider.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearchDataProvider.kt @@ -50,8 +50,6 @@ open class AndroidResearchDataProvider() : DataProvider { } final override fun run(output: CachedOutput): CompletableFuture<*> { - AndroidResearchManager.fireRegistrationEvent() - val set = ObjectArraySet() val added = LinkedList() val futures = ArrayList>() diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearchDescription.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearchDescription.kt new file mode 100644 index 000000000..1a5c51867 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearchDescription.kt @@ -0,0 +1,133 @@ +package ru.dbotthepony.mc.otm.android + +import com.mojang.datafixers.util.Either +import com.mojang.serialization.Codec +import com.mojang.serialization.codecs.RecordCodecBuilder +import net.minecraft.ChatFormatting +import net.minecraft.network.chat.Component +import net.minecraftforge.eventbus.api.IEventBus +import net.minecraftforge.registries.DeferredRegister +import ru.dbotthepony.mc.otm.OverdriveThatMatters +import ru.dbotthepony.mc.otm.client.ShiftPressedCond +import ru.dbotthepony.mc.otm.config.AndroidConfig +import ru.dbotthepony.mc.otm.core.TextComponent +import ru.dbotthepony.mc.otm.core.TranslatableComponent +import ru.dbotthepony.mc.otm.core.getValue +import ru.dbotthepony.mc.otm.core.util.formatPower +import ru.dbotthepony.mc.otm.data.ComponentCodec +import ru.dbotthepony.mc.otm.data.SingletonCodec +import ru.dbotthepony.mc.otm.data.simpleCodec +import ru.dbotthepony.mc.otm.registry.RegistryDelegate + +object AndroidResearchDescriptions { + private val registrar = DeferredRegister.create(AndroidResearchDescription.registryKey, OverdriveThatMatters.MOD_ID) + + init { + registrar.register("plain") { PlainAndroidResearchDescription } + } + + internal fun register(bus: IEventBus) { + registrar.register(bus) + } + + val ENDER_TELEPORTER: AndroidResearchDescription.Singleton by registrar.register("ender_teleporter") { + AndroidResearchDescription.singleton { + TranslatableComponent("otm.gui.power_cost_per_use", AndroidConfig.EnderTeleporter.ENERGY_COST.formatPower(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.YELLOW)) } + } + + val FALL_DAMPENERS: AndroidResearchDescription.Leveled by registrar.register("fall_dampeners") { + AndroidResearchDescription.Leveled { _, _, level -> TranslatableComponent("otm.fall_dampeners.description", TextComponent("%.1f".format((AndroidConfig.FALL_DAMAGE_REDUCTION_PER_LEVEL * level).toFloat().coerceAtLeast(0f).coerceAtMost(1f) * 100f)).withStyle(ChatFormatting.YELLOW)) } + } + + val ITEM_MAGNET: AndroidResearchDescription.Singleton by registrar.register("item_magnet") { + AndroidResearchDescription.singleton { TranslatableComponent("otm.gui.power_cost_per_tick", AndroidConfig.Magnet.POWER_DRAW.formatPower(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.YELLOW)) } + } + + val JUMP_BOOST: AndroidResearchDescription.Singleton by registrar.register("jump_boost") { + AndroidResearchDescription.singleton { TranslatableComponent("otm.gui.power_cost_per_use", AndroidConfig.JumpBoost.ENERGY_COST.formatPower(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.YELLOW)) } + } + + val SHOCKWAVE: AndroidResearchDescription.Singleton by registrar.register("shockwave") { + AndroidResearchDescription.singleton { TranslatableComponent("otm.gui.power_cost_per_use", AndroidConfig.Shockwave.ENERGY_COST.formatPower(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.YELLOW)) } + } +} + +/** + * Instance, representing ready to use description populator + */ +interface AndroidResearchDescription { + /** + * Type, representing raw description populator in registry + */ + interface Type { + val codec: Codec + } + + abstract class Singleton : AndroidResearchDescription, Type { + override val codec = SingletonCodec(this) + override val type: Type<*> + get() = this + } + + class Leveled(val callback: (research: AndroidResearch, lines: MutableList, level: Int) -> Unit) : Type { + inner class Instance(val level: Int) : AndroidResearchDescription { + override fun addLines(research: AndroidResearch, lines: MutableList) { + callback.invoke(research, lines, level) + } + + override val type: Type<*> + get() = this@Leveled + } + + override val codec: Codec by lazy { + RecordCodecBuilder.create { + it.group(Codec.INT.fieldOf("level").forGetter(Instance::level)).apply(it, ::Instance) + } + } + } + + fun addLines(research: AndroidResearch, lines: MutableList) + val type: Type<*> + + companion object { + private val delegate = RegistryDelegate>("android_research_description") + + val registry by delegate + val registryKey get() = delegate.key + + val CODEC: Codec by lazy { + registry.codec.dispatch({ it.type }, { it.codec }) + } + + internal fun register(bus: IEventBus) { + bus.addListener(delegate::build) + } + + fun singleton(callback: (research: AndroidResearch) -> Component): Singleton { + return object : Singleton() { + override fun addLines(research: AndroidResearch, lines: MutableList) { + lines.add(callback.invoke(research)) + } + } + } + } +} + +object PlainAndroidResearchDescription : AndroidResearchDescription.Type { + data class Instance(val line: Component) : AndroidResearchDescription { + override fun addLines(research: AndroidResearch, lines: MutableList) { + lines.add(line.copy()) + } + + override val type: PlainAndroidResearchDescription + get() = PlainAndroidResearchDescription + } + + fun make(line: Component) = Instance(line) + + override val codec: Codec by lazy { + Codec + .either(ComponentCodec, simpleCodec(::Instance, Instance::line, ComponentCodec)) + .xmap({ c -> c.map({ Instance(it) }, { it }) }, { c -> Either.left(c.line) }) + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearchManager.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearchManager.kt index bddc41cd8..363d6e12d 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearchManager.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearchManager.kt @@ -6,7 +6,6 @@ import com.google.gson.JsonElement import com.google.gson.JsonObject import net.minecraft.client.server.IntegratedServer import net.minecraft.network.FriendlyByteBuf -import net.minecraft.network.chat.Component import net.minecraft.resources.ResourceLocation import net.minecraft.server.packs.resources.ResourceManager import net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener @@ -23,8 +22,6 @@ import ru.dbotthepony.mc.otm.NULLABLE_MINECRAFT_SERVER import ru.dbotthepony.mc.otm.SERVER_IS_LIVE import ru.dbotthepony.mc.otm.capability.matteryPlayer import ru.dbotthepony.mc.otm.client.minecraft -import ru.dbotthepony.mc.otm.core.TranslatableComponent -import ru.dbotthepony.mc.otm.data.SerializedFunctionRegistry import ru.dbotthepony.mc.otm.network.MatteryPacket import ru.dbotthepony.mc.otm.network.RegistryNetworkChannel import ru.dbotthepony.mc.otm.network.enqueueWork @@ -33,47 +30,7 @@ import ru.dbotthepony.mc.otm.onceServer import java.util.LinkedList import java.util.function.Supplier -typealias AndroidResultTransformer = SerializedFunctionRegistry.BoundFunction, Unit> -typealias ComponentSupplier = SerializedFunctionRegistry.BoundFunction - object AndroidResearchManager : SimpleJsonResourceReloadListener(GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create(), "otm_android_research"), Iterable { - /** - * Feel free to register functions inside this thing from anywhere in your code - * (registration and querying is completely thread safe). - * - * Just make sure client and server has the same set of functions defined. - */ - val featureResultTransformers = SerializedFunctionRegistry, Unit>() - - /** - * Feel free to register functions inside this thing from anywhere in your code - * (registration and querying is completely thread safe). - * - * Just make sure client and server has the same set of functions defined. - */ - val descriptionFuncs = SerializedFunctionRegistry() - - fun descriptionFunc(name: ResourceLocation, base: String, vararg argument: Supplier): ComponentSupplier { - return descriptionFuncs.register(name) {-> - return@register TranslatableComponent(base, *argument.map { it.get() }.toTypedArray()) - }.bind() - } - - private var firedRegistrationEvent = false - - /** - * Event-style registration of serializable functions, for those who prefer/need it - * - * Fired *once* on [MinecraftForge.EVENT_BUS] before loading android research - */ - object RegisterFuncsEvent : Event() { - val manager get() = AndroidResearchManager - val featureResults by ::featureResultTransformers - val descriptionFunctions by ::descriptionFuncs - - fun descriptionFunc(name: ResourceLocation, base: String, vararg argument: Supplier): ComponentSupplier = AndroidResearchManager.descriptionFunc(name, base, *argument) - } - const val DIRECTORY = "otm_android_research" private val LOGGER = LogManager.getLogger() @@ -97,23 +54,11 @@ object AndroidResearchManager : SimpleJsonResourceReloadListener(GsonBuilder().s var researchMap: Map = mapOf() private set - internal fun fireRegistrationEvent() { - if (!firedRegistrationEvent) { - try { - MinecraftForge.EVENT_BUS.post(RegisterFuncsEvent) - } finally { - firedRegistrationEvent = true - } - } - } - override fun apply( jsonElementMap: Map, manager: ResourceManager, profiler: ProfilerFiller ) { - fireRegistrationEvent() - val builder = ImmutableMap.builder() for ((k, v) in jsonElementMap) { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearchResult.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearchResult.kt new file mode 100644 index 000000000..39e43b686 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearchResult.kt @@ -0,0 +1,180 @@ +package ru.dbotthepony.mc.otm.android + +import com.mojang.datafixers.util.Either +import com.mojang.serialization.Codec +import com.mojang.serialization.codecs.RecordCodecBuilder +import net.minecraft.resources.ResourceLocation +import net.minecraftforge.eventbus.api.IEventBus +import net.minecraftforge.registries.DeferredRegister +import org.apache.logging.log4j.LogManager +import ru.dbotthepony.mc.otm.OverdriveThatMatters +import ru.dbotthepony.mc.otm.core.getValue +import ru.dbotthepony.mc.otm.data.SingletonCodec +import ru.dbotthepony.mc.otm.registry.AndroidFeatures +import ru.dbotthepony.mc.otm.registry.MRegistry +import ru.dbotthepony.mc.otm.registry.RegistryDelegate + +object AndroidResearchResults { + private val registrar = DeferredRegister.create(AndroidResearchResult.registryKey, OverdriveThatMatters.MOD_ID) + + init { + registrar.register("feature") { AndroidResearchResult.Feature.Companion } + registrar.register("feature_level") { AndroidResearchResult.FeatureLevel.Companion } + } + + private object NanobotsArmorStrength : AndroidResearchResult.Singleton { + override val codec: Codec = SingletonCodec(this) + override val type: AndroidResearchResult.Type<*> + get() = this + + override fun onResearched(research: AndroidResearch) { + val feature = research.capability.getFeature(AndroidFeatures.NANOBOTS_ARMOR) ?: return + feature.strength++ + } + + override fun onRefunded(research: AndroidResearch) { + val feature = research.capability.getFeature(AndroidFeatures.NANOBOTS_ARMOR) ?: return + feature.strength-- + } + } + + private object NanobotsArmorSpeed : AndroidResearchResult.Singleton { + override val codec: Codec = SingletonCodec(this) + override val type: AndroidResearchResult.Type<*> + get() = this + + override fun onResearched(research: AndroidResearch) { + val feature = research.capability.getFeature(AndroidFeatures.NANOBOTS_ARMOR) ?: return + feature.speed++ + } + + override fun onRefunded(research: AndroidResearch) { + val feature = research.capability.getFeature(AndroidFeatures.NANOBOTS_ARMOR) ?: return + feature.speed-- + } + } + + val NANOBOTS_ARMOR_STRENGTH: AndroidResearchResult.Singleton<*> by registrar.register("nanobots_armor_strength") { NanobotsArmorStrength } + val NANOBOTS_ARMOR_SPEED: AndroidResearchResult.Singleton<*> by registrar.register("nanobots_armor_speed") { NanobotsArmorSpeed } + + internal fun register(bus: IEventBus) { + registrar.register(bus) + } +} + +interface AndroidResearchResult { + interface Type { + val codec: Codec + } + + interface Singleton> : Type, AndroidResearchResult + + /** + * Adds specific android feature [id] to target, does nothing if target already has specified feature + */ + class Feature(val id: ResourceLocation, val optional: Boolean = false) : AndroidResearchResult { + val feature = MRegistry.ANDROID_FEATURES.getValue(id) ?: if (optional) null else throw NoSuchElementException("Unknown android feature $id") + + override val type: Type<*> + get() = Companion + + override fun onResearched(research: AndroidResearch) { + research.capability.addFeature(feature ?: return) + } + + override fun onRefunded(research: AndroidResearch) { + research.capability.removeFeature(feature ?: return) + } + + companion object : Type { + override val codec: Codec by lazy { + Codec + .either(ResourceLocation.CODEC, RecordCodecBuilder.create { // KT-52757 + it.group( + ResourceLocation.CODEC.fieldOf("id").forGetter(Feature::id), + Codec.BOOL.optionalFieldOf("optional", false).forGetter(Feature::optional) + ).apply(it, ::Feature) + }) + .xmap( + { c -> c.map({ Feature(it) }, { it }) }, + { c -> if (c.optional) Either.left(c.id) else Either.right(c) } + ) + } + } + } + + /** + * Increases level of specific android feature [id] by specified amount [levels] + */ + class FeatureLevel(val id: ResourceLocation, val optional: Boolean = false, val levels: Int = 1) : AndroidResearchResult { + val feature = MRegistry.ANDROID_FEATURES.getValue(id) ?: if (optional) null else throw NoSuchElementException("Unknown android feature $id") + + override val type: Type<*> + get() = Companion + + override fun onResearched(research: AndroidResearch) { + val get = research.capability.getFeature(feature ?: return) + + if (get == null) { + LOGGER.warn("Unable to advance level of android feature $id for ${research.ply} because they have no such android feature.") + } else { + get.level += levels + } + } + + override fun onRefunded(research: AndroidResearch) { + val get = research.capability.getFeature(feature ?: return) + + if (get == null) { + LOGGER.warn("Unable to decrease level of android feature $id for ${research.ply} because they have no such android feature.") + } else { + get.level += levels + } + } + + companion object : Type { + override val codec: Codec by lazy { + Codec + .either(ResourceLocation.CODEC, RecordCodecBuilder.create { // KT-52757 + it.group( + ResourceLocation.CODEC.fieldOf("id").forGetter(FeatureLevel::id), + Codec.BOOL.optionalFieldOf("optional", false).forGetter(FeatureLevel::optional), + Codec.INT.optionalFieldOf("levels", 1).forGetter(FeatureLevel::levels), + ).apply(it, ::FeatureLevel) + }) + .xmap( + { c -> c.map({ FeatureLevel(it) }, { it }) }, + { c -> if (c.optional && c.levels == 1) Either.left(c.id) else Either.right(c) } + ) + } + } + } + + val type: Type<*> + + /** + * Called when research is applied + */ + fun onResearched(research: AndroidResearch) {} + + /** + * Called when research is refunded + */ + fun onRefunded(research: AndroidResearch) {} + + companion object { + private val LOGGER = LogManager.getLogger() + private val delegate = RegistryDelegate>("android_research_result") + + val registry by delegate + val registryKey get() = delegate.key + + val CODEC: Codec by lazy { + registry.codec.dispatch({ it.type }, { it.codec }) + } + + internal fun register(bus: IEventBus) { + bus.addListener(delegate::build) + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearchType.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearchType.kt index bf8003e45..2d150b697 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearchType.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearchType.kt @@ -1,13 +1,13 @@ package ru.dbotthepony.mc.otm.android import com.google.common.collect.ImmutableList -import com.google.common.collect.Streams import com.google.gson.JsonArray import com.google.gson.JsonElement import com.google.gson.JsonObject import com.google.gson.JsonPrimitive import com.google.gson.JsonSyntaxException import com.google.gson.internal.bind.TypeAdapters +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet import net.minecraft.network.FriendlyByteBuf import net.minecraft.network.chat.Component import net.minecraft.network.chat.ComponentContents @@ -28,10 +28,12 @@ import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.isActuallyEmpty import ru.dbotthepony.mc.otm.core.registryName import ru.dbotthepony.mc.otm.core.set -import ru.dbotthepony.mc.otm.core.toImmutableList import ru.dbotthepony.mc.otm.core.collect.stream +import ru.dbotthepony.mc.otm.core.fromJsonStrict +import ru.dbotthepony.mc.otm.core.toJsonStrict +import ru.dbotthepony.mc.otm.core.util.readJson +import ru.dbotthepony.mc.otm.core.util.writeJson import ru.dbotthepony.mc.otm.isClient -import ru.dbotthepony.mc.otm.registry.MRegistry import java.util.LinkedList import java.util.stream.Stream import kotlin.collections.ArrayList @@ -39,7 +41,7 @@ import kotlin.collections.HashSet private fun findPrerequisites( initial: Collection, - add: MutableSet = HashSet(), + add: MutableSet = ObjectOpenHashSet(), top: Boolean = true ): Set { for (value in initial) { @@ -55,7 +57,7 @@ private fun findPrerequisites( private fun findAllPrerequisites( initial: Collection, - add: MutableSet = HashSet(), + add: MutableSet = ObjectOpenHashSet(), ): Set { for (value in initial) { add.add(value) @@ -67,7 +69,7 @@ private fun findAllPrerequisites( private fun findAllChildren( initial: Collection, - add: MutableSet = HashSet(), + add: MutableSet = ObjectOpenHashSet(), ): Set { for (value in initial) { add.add(value) @@ -83,10 +85,9 @@ class AndroidResearchType( blockedBy: Collection, items: Collection>, - features: Collection, + results: Collection, - descriptionLines: Collection, - descriptionSuppliers: Collection = listOf(), + description: Collection, val experienceLevels: Int = 0, private val customName: Component? = null, @@ -145,74 +146,6 @@ class AndroidResearchType( } } - data class FeatureReference( - val id: ResourceLocation, - val level: Int = 0, - val isRigid: Boolean, - val transformersUp: Collection = listOf(), - val transformersDown: Collection = listOf(), - ) { - fun toJson(): JsonObject { - return JsonObject().also { - it["id"] = JsonPrimitive(id.toString()) - it["level"] = JsonPrimitive(level) - it["is_rigid"] = JsonPrimitive(isRigid) - it["functions_up"] = JsonArray().also { - for (transformer in transformersUp) { - it.add(transformer.toJson()) - } - } - - it["functions_down"] = JsonArray().also { - for (transformer in transformersDown) { - it.add(transformer.toJson()) - } - } - } - } - - fun toNetwork(buff: FriendlyByteBuf) { - buff.writeUtf(id.toString()) - buff.writeVarInt(level) - buff.writeBoolean(isRigid) - buff.writeCollection(transformersUp) { a, b -> b.toNetwork(a) } - buff.writeCollection(transformersDown) { a, b -> b.toNetwork(a) } - } - - companion object { - fun fromNetwork(buff: FriendlyByteBuf): FeatureReference { - return FeatureReference( - ResourceLocation(buff.readUtf()), - buff.readVarInt(), - buff.readBoolean(), - buff.readCollection({ LinkedList() }, AndroidResearchManager.featureResultTransformers::fromNetwork).filterNotNull().toImmutableList(), - buff.readCollection({ LinkedList() }, AndroidResearchManager.featureResultTransformers::fromNetwork).filterNotNull().toImmutableList() - ) - } - - fun fromJson(value: JsonElement): FeatureReference { - if (value is JsonPrimitive) { - return FeatureReference(ResourceLocation(value.asString), 0, true) - } else if (value is JsonObject) { - return FeatureReference( - ResourceLocation((value["id"] as? JsonPrimitive ?: throw JsonSyntaxException("Invalid `id` value")).asString), - (value["level"] as JsonPrimitive?)?.asInt ?: 0, - (value["is_rigid"] as JsonPrimitive?)?.asBoolean ?: true, - (value["functions_up"] as JsonArray? ?: JsonArray()).stream().map { AndroidResearchManager.featureResultTransformers.fromJson(it) }.filter { it != null }.collect(ImmutableList.toImmutableList()), - (value["functions_down"] as JsonArray? ?: JsonArray()).stream().map { AndroidResearchManager.featureResultTransformers.fromJson(it) }.filter { it != null }.collect(ImmutableList.toImmutableList()), - ) - } else { - throw JsonSyntaxException("Unknown element type ${value::class.qualifiedName}") - } - } - } - } - - data class ResolvedFeature( - val feature: AndroidFeatureType<*>, - val level: Int, - ) - val researchTreeDepth: Int by lazy { if (flatPrerequisites.isEmpty()) { return@lazy 0 @@ -248,31 +181,11 @@ class AndroidResearchType( ImmutableList.copyOf(this.blockedBy.mapNotNull { AndroidResearchManager[it.id].also { e -> if (e == null && it.isRigid) throw NoSuchElementException("Unable to find research ${it.id}") } }) } - val features: List = ImmutableList.copyOf(features) - - val resolvedFeatures: List by lazy { - ImmutableList.copyOf(features.mapNotNull { - MRegistry.ANDROID_FEATURES.getValue(it.id) - .let { e -> if (e == null && it.isRigid) - throw NoSuchElementException("Unable to find research ${it.id}") - else if (e != null) - ResolvedFeature(e, it.level) - else - null } }) - } - private val definedItems: List> = ImmutableList.copyOf(items) + val results: ImmutableList = ImmutableList.copyOf(results) val items: Stream> get() = definedItems.stream().filter { !it.first.isActuallyEmpty } - - private val descriptionLines: List = ImmutableList.copyOf(descriptionLines.map { it.copy() }) - - private val descriptionSuppliers: List = ImmutableList.copyOf(descriptionSuppliers) - - /** - * Stream containing copies of original [Component]s in list - */ - val description: Stream get() = Streams.concat(descriptionLines.stream().map { it.copy() }, descriptionSuppliers.stream().map { it.apply(Unit) }) + val description: ImmutableList = ImmutableList.copyOf(description) /** * Flat list of research preceding this research. @@ -469,14 +382,11 @@ class AndroidResearchType( fun toJson(): JsonElement { return JsonObject().also { - // it["id"] = JsonPrimitive(id.toString()) - it["prerequisites"] = JsonArray().also { for (value in prerequisites) it.add(value.toJson()) } it["blocked_by"] = JsonArray().also { for (value in blockedBy) it.add(value.toJson()) } it["required_items"] = JsonArray().also { for (item in definedItems) it.add(JsonObject().also { it["count"] = JsonPrimitive(item.second); it["ingredient"] = item.first.toJson() }) } - it["feature_result"] = JsonArray().also { for (feature in features) it.add(feature.toJson()) } - it["description"] = JsonArray().also { for (line in descriptionLines) it.add(Component.Serializer.toJsonTree(line)) } - it["description_funcs"] = JsonArray().also { for (line in descriptionSuppliers) it.add(line.toJson()) } + it["results"] = JsonArray().also { for (result in results) it.add(AndroidResearchResult.CODEC.toJsonStrict(result)) } + it["description"] = JsonArray().also { for (line in description) it.add(AndroidResearchDescription.CODEC.toJsonStrict(line)) } it["experience"] = JsonPrimitive(experienceLevels) if (skinIcon != null) { @@ -498,7 +408,6 @@ class AndroidResearchType( } fun validate() { - resolvedFeatures resolvedBlockedBy resolvedPrerequisites } @@ -508,9 +417,8 @@ class AndroidResearchType( buff.writeCollection(prerequisites) { a, b -> b.toNetwork(a) } buff.writeCollection(blockedBy) { a, b -> b.toNetwork(a) } buff.writeCollection(definedItems) { a, b -> b.first.toNetwork(a); a.writeVarInt(b.second) } - buff.writeCollection(features) { a, b -> b.toNetwork(a) } - buff.writeCollection(descriptionLines) { a, b -> a.writeComponent(b) } - buff.writeCollection(descriptionSuppliers) { a, b -> b.toNetwork(a) } + buff.writeCollection(results) { a, b -> a.writeJson(AndroidResearchResult.CODEC.toJsonStrict(b)) } + buff.writeCollection(description) { a, b -> a.writeJson(AndroidResearchDescription.CODEC.toJsonStrict(b)) } buff.writeVarInt(experienceLevels) buff.writeBoolean(customName != null) @@ -532,9 +440,8 @@ class AndroidResearchType( val prerequisites = buff.readCollection({ LinkedList() }, Reference::fromNetwork) val blockedBy = buff.readCollection({ LinkedList() }, Reference::fromNetwork) val items = buff.readCollection({ LinkedList() }, { Ingredient.fromNetwork(it) to it.readVarInt() }) - val features = buff.readCollection({ LinkedList() }, FeatureReference::fromNetwork) - val descriptionLines = buff.readCollection({ LinkedList() }, FriendlyByteBuf::readComponent) - val descriptionSuppliers = buff.readCollection({ LinkedList() }, { AndroidResearchManager.descriptionFuncs.fromNetwork(it) }) + val results = buff.readCollection({ LinkedList() }, { AndroidResearchResult.CODEC.fromJsonStrict(it.readJson()) }) + val description = buff.readCollection({ LinkedList() }, { AndroidResearchDescription.CODEC.fromJsonStrict(it.readJson()) }) val experienceLevels = buff.readVarInt() val customName = if (buff.readBoolean()) { @@ -566,9 +473,8 @@ class AndroidResearchType( prerequisites = prerequisites, blockedBy = blockedBy, items = items, - features = features, - descriptionLines = descriptionLines, - descriptionSuppliers = descriptionSuppliers.filterNotNull(), + results = results, + description = description, experienceLevels = experienceLevels, customName = customName, iconText = iconTextValue, @@ -587,7 +493,6 @@ class AndroidResearchType( val items = value["required_items"] as JsonArray? ?: JsonArray() val features = value["feature_result"] as JsonArray? ?: JsonArray() val description = value["description"] as JsonArray? ?: JsonArray() - val description_funcs = value["description_funcs"] as JsonArray? ?: JsonArray() val experience = value["experience"]?.asInt ?: 0 val customName = value["custom_name"]?.let(Component.Serializer::fromJson) val iconText = value["icon_text"]?.let(Component.Serializer::fromJson) @@ -598,15 +503,12 @@ class AndroidResearchType( id = id, prerequisites = prerequisites.stream().map { Reference.fromJson(it) }.toList(), blockedBy = blocked_by.stream().map { Reference.fromJson(it) }.toList(), - features = features.stream().map { FeatureReference.fromJson(it) }.toList(), + results = features.stream().map { AndroidResearchResult.CODEC.fromJsonStrict(it) }.toList(), items = items.stream() .map { it as? JsonObject ?: throw JsonSyntaxException("One of items is not an JsonObject") } .map { Ingredient.fromJson(it["ingredient"] ?: throw JsonSyntaxException("Missing ingredient key")) to (it["count"]?.asInt ?: throw JsonSyntaxException("Missing count key")) } .toList(), - descriptionLines = description.stream().map { Component.Serializer.fromJson(it) }.toList() as List, - descriptionSuppliers = description_funcs.stream() - .map { AndroidResearchManager.descriptionFuncs.fromJson(it) ?: throw NullPointerException("$id is missing description supplier function or it is invalid! JSON: $it") } - .toList() as List, + description = description.stream().map { AndroidResearchDescription.CODEC.fromJsonStrict(it) }.toList(), experienceLevels = experience, customName = customName, iconText = iconText, @@ -621,18 +523,18 @@ class AndroidResearchType( val id: ResourceLocation, var experience: Int = 0, var customName: Component? = null, - var description: MutableList? = null, - var descriptionSuppliers: MutableList? = null, + description: MutableList? = null, var itemIcon: Item? = null, var skinIcon: AbstractMatterySprite? = null, var iconText: Component? = null, ) { + val description = ArrayList(description ?: listOf()) + val results = ArrayList() + private val items = ArrayList>() private val prerequisites = ArrayList() private val blockers = ArrayList() - private val features = ArrayList() - fun withIconText(icon: Component? = null): Builder { this.iconText = icon return this @@ -661,19 +563,20 @@ class AndroidResearchType( return this } - fun withDescription(): Builder { - this.description = mutableListOf(TranslatableComponent("android_research.${id.namespace}.${id.path}.description")) + fun clearDescription(): Builder { + this.description.clear() return this } - fun withDescription(range: IntRange): Builder { - val result = ArrayList() + fun withDescription(): Builder { + return withDescription(TranslatableComponent("android_research.${id.namespace}.${id.path}.description")) + } + fun withDescription(range: IntRange): Builder { for (i in range) { - result.add(TranslatableComponent("android_research.${id.namespace}.${id.path}.description$i")) + withDescription(TranslatableComponent("android_research.${id.namespace}.${id.path}.description$i")) } - this.description = result return this } @@ -683,73 +586,21 @@ class AndroidResearchType( } fun withDescription(vararg description: Component): Builder { - this.description = description.toMutableList() + for (component in description) + withDescription(PlainAndroidResearchDescription.Instance(component)) + return this } fun withDescription(description: Collection): Builder { - this.description = ArrayList(description.size).also { it.addAll(description) } + for (component in description) + withDescription(PlainAndroidResearchDescription.Instance(component)) + return this } - fun appendDescription(range: IntRange): Builder { - val result = this.description ?: ArrayList() - - for (i in range) { - result.add(TranslatableComponent("android_research.${id.namespace}.${id.path}.description$i")) - } - - this.description = result - return this - } - - fun appendDescription(description: Component): Builder { - this.description = (this.description ?: mutableListOf()).also { it.add(description) } - return this - } - - fun appendDescription(vararg description: Component): Builder { - this.description = (this.description ?: mutableListOf()).also { it.addAll(description) } - return this - } - - fun appendDescription(description: Collection): Builder { - this.description = (this.description ?: mutableListOf()).also { it.addAll(description) } - return this - } - - fun withDescription(vararg description: ComponentSupplier): Builder { - this.descriptionSuppliers = description.toMutableList() - return this - } - - fun withDescriptionSupplier(description: Collection): Builder { - this.descriptionSuppliers = ArrayList(description.size).also { it.addAll(description) } - return this - } - - fun appendDescription(description: ComponentSupplier): Builder { - this.descriptionSuppliers = (this.descriptionSuppliers ?: mutableListOf()).also { it.add(description) } - return this - } - - fun appendDescriptionSupplier(description: ComponentSupplier): Builder { - this.descriptionSuppliers = (this.descriptionSuppliers ?: mutableListOf()).also { it.add(description) } - return this - } - - fun appendDescription(vararg description: ComponentSupplier): Builder { - this.descriptionSuppliers = (this.descriptionSuppliers ?: mutableListOf()).also { it.addAll(description) } - return this - } - - fun appendDescriptionSupplier(vararg description: ComponentSupplier): Builder { - this.descriptionSuppliers = (this.descriptionSuppliers ?: mutableListOf()).also { it.addAll(description) } - return this - } - - fun appendDescriptionSupplier(description: Collection): Builder { - this.descriptionSuppliers = (this.descriptionSuppliers ?: mutableListOf()).also { it.addAll(description) } + fun withDescription(vararg description: AndroidResearchDescription): Builder { + this.description.addAll(description) return this } @@ -776,43 +627,25 @@ class AndroidResearchType( fun addPrerequisite(type: AndroidResearchType, rigid: Boolean = true) = addPrerequisite(type.id, rigid) fun addBlocker(type: AndroidResearchType, rigid: Boolean = true) = addBlocker(type.id, rigid) - @JvmOverloads - fun addFeatureResult( - id: ResourceLocation, - level: Int = 0, - rigid: Boolean = false, - transformersUp: Collection = listOf(), - transformersDown: Collection = listOf(), - ): Builder { - features.add(FeatureReference(id, level, rigid, transformersUp, transformersDown)) + fun addResult(result: AndroidResearchResult): Builder { + results.add(result) return this } - @JvmOverloads - fun addFeatureResult( - feature: AndroidFeatureType<*>, - level: Int = 0, - rigid: Boolean = true, - transformersUp: Collection = listOf(), - transformersDown: Collection = listOf(), - ): Builder { - features.add(FeatureReference(feature.registryName ?: throw NullPointerException("Feature $feature does not have registry name"), level, rigid, transformersUp, transformersDown)) - return this + fun addFeatureResult(id: ResourceLocation, optional: Boolean = false): Builder { + return addResult(AndroidResearchResult.Feature(id, optional)) } - fun addFeatureResult( - id: ResourceLocation, - rigid: Boolean = false, - transformersUp: Collection = listOf(), - transformersDown: Collection = listOf(), - ): Builder { - features.add(FeatureReference(id, 0, rigid, transformersUp, transformersDown)) - return this + fun addFeatureResult(feature: AndroidFeatureType<*>, optional: Boolean = false): Builder { + return addFeatureResult(feature.registryName!!, optional) } - fun addFeatureResult(ref: FeatureReference): Builder { - features.add(ref) - return this + fun addFeatureLevel(id: ResourceLocation, optional: Boolean = false, levels: Int = 1): Builder { + return addResult(AndroidResearchResult.FeatureLevel(id, optional, levels)) + } + + fun addFeatureLevel(feature: AndroidFeatureType<*>, optional: Boolean = false, levels: Int = 1): Builder { + return addFeatureLevel(feature.registryName!!, optional, levels) } fun addItem(cost: ItemStack): Builder { @@ -844,9 +677,8 @@ class AndroidResearchType( prerequisites = prerequisites, blockedBy = blockers, items = items, - features = features, - descriptionLines = description ?: listOf(), - descriptionSuppliers = descriptionSuppliers ?: listOf(), + results = results, + description = description, experienceLevels = experience, customName = customName, skinIcon = skinIcon?.toJson(), diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/android/feature/EnderTeleporterFeature.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/android/feature/EnderTeleporterFeature.kt index b8a30b8f5..5e5931bbb 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/android/feature/EnderTeleporterFeature.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/android/feature/EnderTeleporterFeature.kt @@ -2,7 +2,6 @@ package ru.dbotthepony.mc.otm.android.feature import com.mojang.blaze3d.systems.RenderSystem import com.mojang.blaze3d.vertex.PoseStack -import net.minecraft.ChatFormatting import net.minecraft.client.Camera import net.minecraft.client.gui.GuiGraphics import net.minecraft.client.renderer.LevelRenderer @@ -28,25 +27,22 @@ import net.minecraftforge.event.entity.living.LivingDeathEvent import ru.dbotthepony.mc.otm.NULLABLE_MINECRAFT_SERVER import ru.dbotthepony.mc.otm.OverdriveThatMatters import ru.dbotthepony.mc.otm.android.AndroidActiveFeature -import ru.dbotthepony.mc.otm.android.AndroidResearchManager import ru.dbotthepony.mc.otm.capability.MatteryPlayerCapability import ru.dbotthepony.mc.otm.capability.energy.extractEnergyExact import ru.dbotthepony.mc.otm.capability.matteryPlayer -import ru.dbotthepony.mc.otm.client.ShiftPressedCond import ru.dbotthepony.mc.otm.client.render.DynamicBufferSource import ru.dbotthepony.mc.otm.client.render.ResearchIcons -import ru.dbotthepony.mc.otm.client.render.sprite import ru.dbotthepony.mc.otm.client.render.linesIgnoreZRenderType +import ru.dbotthepony.mc.otm.client.render.sprite import ru.dbotthepony.mc.otm.config.AndroidConfig +import ru.dbotthepony.mc.otm.core.genericPositions +import ru.dbotthepony.mc.otm.core.holder +import ru.dbotthepony.mc.otm.core.isFall import ru.dbotthepony.mc.otm.core.math.Vector import ru.dbotthepony.mc.otm.core.math.asVector import ru.dbotthepony.mc.otm.core.math.component1 import ru.dbotthepony.mc.otm.core.math.component2 import ru.dbotthepony.mc.otm.core.math.component3 -import ru.dbotthepony.mc.otm.core.util.formatPower -import ru.dbotthepony.mc.otm.core.genericPositions -import ru.dbotthepony.mc.otm.core.holder -import ru.dbotthepony.mc.otm.core.isFall import ru.dbotthepony.mc.otm.core.math.plus import ru.dbotthepony.mc.otm.core.math.rotateXDegrees import ru.dbotthepony.mc.otm.core.math.rotateYDegrees @@ -54,7 +50,6 @@ import ru.dbotthepony.mc.otm.core.math.shortestDistanceBetween import ru.dbotthepony.mc.otm.core.math.times import ru.dbotthepony.mc.otm.milliTime import ru.dbotthepony.mc.otm.registry.AndroidFeatures -import ru.dbotthepony.mc.otm.registry.MNames import ru.dbotthepony.mc.otm.triggers.EnderTeleporterFallDeathTrigger import java.util.* import kotlin.math.sin @@ -399,12 +394,6 @@ class EnderTeleporterFeature(capability: MatteryPlayerCapability) : AndroidActiv val SPRITE = ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/item/black_hole.png").sprite(0f, 0f, 16f, 16f, 16f, 16f) - val POWER_COST_DESCRIPTION = - AndroidResearchManager.descriptionFunc( - ResourceLocation(OverdriveThatMatters.MOD_ID, MNames.ENDER_TELEPORTER), - "otm.gui.power_cost_per_use", - { AndroidConfig.EnderTeleporter.ENERGY_COST.formatPower(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.YELLOW) }) - fun onEntityDeath(event: LivingDeathEvent) { val android = event.entity.matteryPlayer ?: return val server = NULLABLE_MINECRAFT_SERVER ?: return diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/android/feature/FallDampenersFeature.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/android/feature/FallDampenersFeature.kt index 5cc0c71e7..dbf0004d8 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/android/feature/FallDampenersFeature.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/android/feature/FallDampenersFeature.kt @@ -35,10 +35,4 @@ class FallDampenersFeature(capability: MatteryPlayerCapability) : AndroidFeature } } } - - companion object { - val DESCRIPTION = AndroidResearchManager.descriptionFuncs.register(ResourceLocation(OverdriveThatMatters.MOD_ID, MNames.FALL_DAMPENERS)) { level: Int -> - TranslatableComponent("otm.fall_dampeners.description", TextComponent("%.1f".format((AndroidConfig.FALL_DAMAGE_REDUCTION_PER_LEVEL * level).toFloat().coerceAtLeast(0f).coerceAtMost(1f) * 100f)).withStyle(ChatFormatting.YELLOW)) - } - } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/android/feature/ItemMagnetFeature.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/android/feature/ItemMagnetFeature.kt index 4c7160245..46e9c4477 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/android/feature/ItemMagnetFeature.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/android/feature/ItemMagnetFeature.kt @@ -132,12 +132,4 @@ class ItemMagnetFeature(capability: MatteryPlayerCapability) : AndroidSwitchable override fun renderIcon(graphics: GuiGraphics, x: Float, y: Float, width: Float, height: Float) { ResearchIcons.ICON_ITEM_MAGNET.render(graphics, x, y, width, height) } - - companion object { - val POWER_COST_DESCRIPTION = - AndroidResearchManager.descriptionFunc(ResourceLocation( - OverdriveThatMatters.MOD_ID, MNames.ITEM_MAGNET), - "otm.gui.power_cost_per_tick", - { AndroidConfig.Magnet.POWER_DRAW.formatPower(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.YELLOW) }) - } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/android/feature/JumpBoostFeature.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/android/feature/JumpBoostFeature.kt index 555bd86e6..4d3044802 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/android/feature/JumpBoostFeature.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/android/feature/JumpBoostFeature.kt @@ -125,13 +125,4 @@ class JumpBoostFeature(capability: MatteryPlayerCapability) : AndroidSwitchableF RenderSystem.setShaderColor(1f, 1f, 1f, 1f) } } - - - companion object { - val POWER_COST_DESCRIPTION = - AndroidResearchManager.descriptionFunc( - ResourceLocation(OverdriveThatMatters.MOD_ID, MNames.JUMP_BOOST), - "otm.gui.power_cost_per_use", - { AndroidConfig.JumpBoost.ENERGY_COST.formatPower(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.YELLOW) }) - } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/android/feature/NanobotsArmorFeature.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/android/feature/NanobotsArmorFeature.kt index b9a17f54a..8dc1f81db 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/android/feature/NanobotsArmorFeature.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/android/feature/NanobotsArmorFeature.kt @@ -106,33 +106,5 @@ class NanobotsArmorFeature(android: MatteryPlayerCapability) : AndroidFeature(An 0.45f, 0.6f, ) - - val STRENGTH_TRANSFORMER_UP = AndroidResearchManager.featureResultTransformers.register(ResourceLocation(OverdriveThatMatters.MOD_ID, "nanobots_armor_strength_up")) - { level: Int -> - if (second is NanobotsArmorFeature && (second as NanobotsArmorFeature).strength == level - 1) { - (second as NanobotsArmorFeature).strength = level - } - } - - val STRENGTH_TRANSFORMER_DOWN = AndroidResearchManager.featureResultTransformers.register(ResourceLocation(OverdriveThatMatters.MOD_ID, "nanobots_armor_strength_down")) - { level: Int -> - if (second is NanobotsArmorFeature && (second as NanobotsArmorFeature).strength == level) { - (second as NanobotsArmorFeature).strength = level - 1 - } - } - - val SPEED_TRANSFORMER_UP = AndroidResearchManager.featureResultTransformers.register(ResourceLocation(OverdriveThatMatters.MOD_ID, "nanobots_armor_speed_up")) - { level: Int -> - if (second is NanobotsArmorFeature && (second as NanobotsArmorFeature).speed == level - 1) { - (second as NanobotsArmorFeature).speed = level - } - } - - val SPEED_TRANSFORMER_DOWN = AndroidResearchManager.featureResultTransformers.register(ResourceLocation(OverdriveThatMatters.MOD_ID, "nanobots_armor_speed_down")) - { level: Int -> - if (second is NanobotsArmorFeature && (second as NanobotsArmorFeature).speed == level) { - (second as NanobotsArmorFeature).speed = level - 1 - } - } } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/android/feature/ShockwaveFeature.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/android/feature/ShockwaveFeature.kt index cc415b297..ce0fbb20c 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/android/feature/ShockwaveFeature.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/android/feature/ShockwaveFeature.kt @@ -251,12 +251,4 @@ class ShockwaveFeature(capability: MatteryPlayerCapability) : AndroidSwitchableF RenderSystem.setShaderColor(1f, 1f, 1f, 1f) } } - - companion object { - val POWER_COST_DESCRIPTION = - AndroidResearchManager.descriptionFunc( - ResourceLocation(OverdriveThatMatters.MOD_ID, MNames.SHOCKWAVE), - "otm.gui.power_cost_per_use", - { AndroidConfig.Shockwave.ENERGY_COST.formatPower(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.YELLOW) }) - } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/data/Codecs.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/data/Codecs.kt new file mode 100644 index 000000000..ef688c49c --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/data/Codecs.kt @@ -0,0 +1,33 @@ +package ru.dbotthepony.mc.otm.data + +import com.mojang.serialization.Codec +import com.mojang.serialization.codecs.RecordCodecBuilder +import kotlin.reflect.KProperty1 + + +fun simpleCodec(factory: (T1) -> V, field1: KProperty1, codec1: Codec): Codec { + return RecordCodecBuilder.create { + it.group( + codec1.fieldOf(field1.name).forGetter(field1::get) + ).apply(it, factory) + } +} + +fun simpleCodec(factory: (T1, T2) -> V, field1: KProperty1, codec1: Codec, field2: KProperty1, codec2: Codec): Codec { + return RecordCodecBuilder.create { + it.group( + codec1.fieldOf(field1.name).forGetter(field1::get), + codec2.fieldOf(field2.name).forGetter(field2::get), + ).apply(it, factory) + } +} + +fun simpleCodec(factory: (T1, T2, T3) -> V, field1: KProperty1, codec1: Codec, field2: KProperty1, codec2: Codec, field3: KProperty1, codec3: Codec): Codec { + return RecordCodecBuilder.create { + it.group( + codec1.fieldOf(field1.name).forGetter(field1::get), + codec2.fieldOf(field2.name).forGetter(field2::get), + codec3.fieldOf(field3.name).forGetter(field3::get), + ).apply(it, factory) + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/data/ComponentCodec.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/data/ComponentCodec.kt new file mode 100644 index 000000000..9efaeb74f --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/data/ComponentCodec.kt @@ -0,0 +1,25 @@ +package ru.dbotthepony.mc.otm.data + +import com.google.gson.JsonSyntaxException +import com.mojang.datafixers.util.Pair +import com.mojang.serialization.Codec +import com.mojang.serialization.DataResult +import com.mojang.serialization.DynamicOps +import com.mojang.serialization.JsonOps +import net.minecraft.network.chat.Component + +object ComponentCodec : Codec { + override fun encode(input: Component, ops: DynamicOps, prefix: T): DataResult { + return DataResult.success(JsonOps.INSTANCE.convertTo(ops, Component.Serializer.toJsonTree(input))) + } + + override fun decode(ops: DynamicOps, input: T): DataResult> { + val value = ops.convertTo(JsonOps.INSTANCE, input) + + try { + return DataResult.success(Pair(Component.Serializer.fromJson(value), ops.empty())) + } catch (err: JsonSyntaxException) { + return DataResult.error { err.message } + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/data/SerializedFunctionRegistry.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/data/SerializedFunctionRegistry.kt deleted file mode 100644 index 0a916f07b..000000000 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/data/SerializedFunctionRegistry.kt +++ /dev/null @@ -1,225 +0,0 @@ -package ru.dbotthepony.mc.otm.data - -import com.google.gson.Gson -import com.google.gson.JsonDeserializationContext -import com.google.gson.JsonDeserializer -import com.google.gson.JsonElement -import com.google.gson.JsonObject -import com.google.gson.JsonPrimitive -import com.google.gson.JsonSerializationContext -import com.google.gson.JsonSerializer -import com.google.gson.JsonSyntaxException -import com.google.gson.TypeAdapter -import com.google.gson.internal.bind.TypeAdapters -import com.google.gson.stream.JsonReader -import com.google.gson.stream.JsonWriter -import io.netty.buffer.ByteBufInputStream -import io.netty.buffer.ByteBufOutputStream -import it.unimi.dsi.fastutil.io.FastByteArrayInputStream -import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream -import net.minecraft.nbt.NbtAccounter -import net.minecraft.network.FriendlyByteBuf -import net.minecraft.resources.ResourceLocation -import ru.dbotthepony.mc.otm.core.util.readType -import ru.dbotthepony.mc.otm.core.util.readVarIntLE -import ru.dbotthepony.mc.otm.core.nbt.set -import ru.dbotthepony.mc.otm.core.set -import ru.dbotthepony.mc.otm.core.util.writeType -import ru.dbotthepony.mc.otm.core.util.writeVarIntLE -import java.io.DataInputStream -import java.io.DataOutputStream -import java.lang.reflect.Type -import java.util.Base64 -import java.util.Collections -import java.util.LinkedList - -class SerializedFunctionRegistry(val gson: Gson = Gson()) : JsonSerializer>, JsonDeserializer>, TypeAdapter>() { - fun interface AnonymousFunction { - fun invoke(receiver: R, arguments: List): T - } - - data class BoundFunction( - val function: Function, - val arguments: List - ) : java.util.function.Function { - fun toJson(): JsonElement { - return JsonObject().also { - it["function"] = function.toJson() - val stream = FastByteArrayOutputStream() - val dataStream = DataOutputStream(stream) - - stream.writeVarIntLE(arguments.size) - - for (argument in arguments) { - dataStream.writeType(argument) - } - - it["arguments"] = JsonPrimitive(Base64.getEncoder().encode(stream.array.copyOfRange(0, stream.length)).toString(Charsets.UTF_8)) - } - } - - fun toNetwork(buff: FriendlyByteBuf) { - function.toNetwork(buff) - - val stream = DataOutputStream(ByteBufOutputStream(buff)) - stream.writeVarIntLE(arguments.size) - for (argument in arguments) { - stream.writeType(argument) - } - } - - override fun apply(t: R): T { - return function.body.invoke(t, arguments) - } - } - - data class Function( - val id: ResourceLocation, - val body: AnonymousFunction, - val registry: SerializedFunctionRegistry - ) { - fun bind(vararg arguments: Any?): BoundFunction { - validate(arguments.iterator().withIndex()) - return BoundFunction(this, Collections.unmodifiableList(LinkedList().also { it.addAll(arguments) })) - } - - fun bind(arguments: List): BoundFunction { - validate(arguments.iterator().withIndex()) - return BoundFunction(this, Collections.unmodifiableList(LinkedList().also { it.addAll(arguments) })) - } - - fun toJson(): JsonElement { - return JsonPrimitive(id.toString()) - } - - fun toNetwork(buff: FriendlyByteBuf) { - buff.writeUtf(id.toString()) - } - - companion object { - private fun validate(iterator: Iterator>) { - try { - val stream = DataOutputStream(FastByteArrayOutputStream()) - - for ((i, argument) in iterator) { - try { - stream.writeType(argument) - } catch(err: Exception) { - throw IllegalArgumentException("Argument at $i can not be serialized", err) - } - } - } catch(err: Exception) { - throw IllegalArgumentException("Argument list validation failed", err) - } - } - } - } - - private val map = HashMap>() - - fun register(id: ResourceLocation, function: AnonymousFunction): Function { - synchronized(map) { - return map.computeIfAbsent(id) { Function(id, function, this) } - } - } - - fun register(id: ResourceLocation, function: R.() -> T): Function { - return register(id, AnonymousFunction { r, args -> - check(args.isEmpty()) { "Invalid amount of arguments. No arguments are required" } - function.invoke(r) - }) - } - - inline fun register(id: ResourceLocation, noinline function: R.(A) -> T): Function { - return register(id, AnonymousFunction { r, args -> - check(args.size == 1) { "Invalid amount of arguments. 1 is required" } - function.invoke( - r, - args[0] as? A ?: throw ClassCastException("Argument at 0 is supposed to be ${A::class.qualifiedName}, ${args[0]?.let { it::class.qualifiedName }} given") - ) - }) - } - - inline fun register(id: ResourceLocation, noinline function: R.(A, B) -> T): Function { - return register(id, AnonymousFunction { r, args -> - check(args.size == 2) { "Invalid amount of arguments. 2 is required" } - - function.invoke( - r, - args[0] as? A ?: throw ClassCastException("Argument at 0 is supposed to be ${A::class.qualifiedName}, ${args[0]?.let { it::class.qualifiedName }} given"), - args[1] as? B ?: throw ClassCastException("Argument at 1 is supposed to be ${B::class.qualifiedName}, ${args[1]?.let { it::class.qualifiedName }} given"), - ) - }) - } - - inline fun register(id: ResourceLocation, noinline function: R.(A, B, C) -> T): Function { - return register(id, AnonymousFunction { r, args -> - check(args.size == 3) { "Invalid amount of arguments. 3 is required" } - - function.invoke( - r, - args[0] as? A ?: throw ClassCastException("Argument at 0 is supposed to be ${A::class.qualifiedName}, ${args[0]?.let { it::class.qualifiedName }} given"), - args[1] as? B ?: throw ClassCastException("Argument at 1 is supposed to be ${B::class.qualifiedName}, ${args[1]?.let { it::class.qualifiedName }} given"), - args[2] as? C ?: throw ClassCastException("Argument at 2 is supposed to be ${B::class.qualifiedName}, ${args[2]?.let { it::class.qualifiedName }} given"), - ) - }) - } - - fun get(id: ResourceLocation): Function? { - synchronized(map) { - return map[id] - } - } - - fun fromNetwork(buff: FriendlyByteBuf): BoundFunction? { - val id = ResourceLocation(buff.readUtf()) - val stream = DataInputStream(ByteBufInputStream(buff)) - - val arguments = LinkedList() - val sizeLimit = NbtAccounter(1L shl 18 /* 256 KiB */) - - for (i in 0 until stream.readVarIntLE(sizeLimit)) { - arguments.add(stream.readType(sizeLimit)) - } - - return map[id]?.bind(arguments) - } - - fun fromJson(value: JsonElement): BoundFunction? { - if (value !is JsonObject) { - return null - } - - val id = value["function"]?.asString ?: return null - val argumentString = value["arguments"]?.asString ?: return null - val stream = DataInputStream(FastByteArrayInputStream(Base64.getDecoder().decode(argumentString))) - val arguments = LinkedList() - val sizeLimit = NbtAccounter(1L shl 18 /* 256 KiB */) - - for (i in 0 until stream.readVarIntLE(sizeLimit)) { - arguments.add(stream.readType(sizeLimit)) - } - - return map[ResourceLocation(id)]?.bind(arguments) - } - - override fun serialize(src: BoundFunction, typeOfSrc: Type, context: JsonSerializationContext): JsonElement { - return src.toJson() - } - - override fun deserialize( - json: JsonElement, - typeOfT: Type, - context: JsonDeserializationContext - ): BoundFunction { - return fromJson(json) ?: throw JsonSyntaxException("Function is invalid") - } - - override fun write(out: JsonWriter, value: BoundFunction) { - TypeAdapters.JSON_ELEMENT.write(out, value.toJson()) - } - - override fun read(`in`: JsonReader): BoundFunction { - return fromJson(TypeAdapters.JSON_ELEMENT.read(`in`)) ?: throw JsonSyntaxException("Function is invalid") - } -} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/data/SingletonCodec.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/data/SingletonCodec.kt new file mode 100644 index 000000000..eb2ade209 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/data/SingletonCodec.kt @@ -0,0 +1,16 @@ +package ru.dbotthepony.mc.otm.data + +import com.mojang.datafixers.util.Pair +import com.mojang.serialization.Codec +import com.mojang.serialization.DataResult +import com.mojang.serialization.DynamicOps + +class SingletonCodec(val value: V) : Codec { + override fun encode(input: V, ops: DynamicOps, prefix: T): DataResult { + return DataResult.success(ops.empty()) + } + + override fun decode(ops: DynamicOps, input: T? /* Так то, оно должно быть null */): DataResult> { + return DataResult.success(Pair(value, ops.empty())) + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/AndroidFeatures.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/AndroidFeatures.kt index d729b079d..c1ee11123 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/AndroidFeatures.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/AndroidFeatures.kt @@ -10,20 +10,20 @@ import ru.dbotthepony.mc.otm.android.feature.* object AndroidFeatures { private val registry = DeferredRegister.create(MRegistry.ANDROID_FEATURES_KEY, OverdriveThatMatters.MOD_ID) - val AIR_BAGS: AndroidFeatureType<*> by registry.register(MNames.AIR_BAGS) { AndroidFeatureType(::DummyAndroidFeature) } - val STEP_ASSIST: AndroidFeatureType<*> by registry.register(MNames.STEP_ASSIST) { AndroidFeatureType(::StepAssistFeature) } - val LIMB_OVERCLOCKING: AndroidFeatureType<*> by registry.register(MNames.LIMB_OVERCLOCKING) { AndroidFeatureType(::LimbOverclockingFeature) } - val ATTACK_BOOST: AndroidFeatureType<*> by registry.register(MNames.ATTACK_BOOST) { AndroidFeatureType(::AttackBoostFeature) } - val NANOBOTS_REGENERATION: AndroidFeatureType<*> by registry.register(MNames.NANOBOTS_REGENERATION) { AndroidFeatureType(::NanobotsRegenerationFeature) } - val NANOBOTS_ARMOR: AndroidFeatureType<*> by registry.register(MNames.NANOBOTS_ARMOR) { AndroidFeatureType(::NanobotsArmorFeature) } - val EXTENDED_REACH: AndroidFeatureType<*> by registry.register(MNames.EXTENDED_REACH) { AndroidFeatureType(::ExtendedReachFeature) } - val NIGHT_VISION: AndroidFeatureType<*> by registry.register(MNames.NIGHT_VISION) { AndroidFeatureType(::NightVisionFeature) } - val SHOCKWAVE: AndroidFeatureType<*> by registry.register(MNames.SHOCKWAVE) { AndroidFeatureType(::ShockwaveFeature) } - val ITEM_MAGNET: AndroidFeatureType<*> by registry.register(MNames.ITEM_MAGNET) { AndroidFeatureType(::ItemMagnetFeature) } - val FALL_DAMPENERS: AndroidFeatureType<*> by registry.register(MNames.FALL_DAMPENERS) { AndroidFeatureType(::FallDampenersFeature) } - val PHANTOM_ATTRACTOR: AndroidFeatureType<*> by registry.register(MNames.PHANTOM_ATTRACTOR) { AndroidFeatureType(::PhantomAttractorFeature) } - val JUMP_BOOST: AndroidFeatureType<*> by registry.register(MNames.JUMP_BOOST) { AndroidFeatureType(::JumpBoostFeature) } - val ENDER_TELEPORTER: AndroidFeatureType<*> by registry.register(MNames.ENDER_TELEPORTER) { AndroidFeatureType(::EnderTeleporterFeature) } + val AIR_BAGS: AndroidFeatureType by registry.register(MNames.AIR_BAGS) { AndroidFeatureType(::DummyAndroidFeature) } + val STEP_ASSIST: AndroidFeatureType by registry.register(MNames.STEP_ASSIST) { AndroidFeatureType(::StepAssistFeature) } + val LIMB_OVERCLOCKING: AndroidFeatureType by registry.register(MNames.LIMB_OVERCLOCKING) { AndroidFeatureType(::LimbOverclockingFeature) } + val ATTACK_BOOST: AndroidFeatureType by registry.register(MNames.ATTACK_BOOST) { AndroidFeatureType(::AttackBoostFeature) } + val NANOBOTS_REGENERATION: AndroidFeatureType by registry.register(MNames.NANOBOTS_REGENERATION) { AndroidFeatureType(::NanobotsRegenerationFeature) } + val NANOBOTS_ARMOR: AndroidFeatureType by registry.register(MNames.NANOBOTS_ARMOR) { AndroidFeatureType(::NanobotsArmorFeature) } + val EXTENDED_REACH: AndroidFeatureType by registry.register(MNames.EXTENDED_REACH) { AndroidFeatureType(::ExtendedReachFeature) } + val NIGHT_VISION: AndroidFeatureType by registry.register(MNames.NIGHT_VISION) { AndroidFeatureType(::NightVisionFeature) } + val SHOCKWAVE: AndroidFeatureType by registry.register(MNames.SHOCKWAVE) { AndroidFeatureType(::ShockwaveFeature) } + val ITEM_MAGNET: AndroidFeatureType by registry.register(MNames.ITEM_MAGNET) { AndroidFeatureType(::ItemMagnetFeature) } + val FALL_DAMPENERS: AndroidFeatureType by registry.register(MNames.FALL_DAMPENERS) { AndroidFeatureType(::FallDampenersFeature) } + val PHANTOM_ATTRACTOR: AndroidFeatureType by registry.register(MNames.PHANTOM_ATTRACTOR) { AndroidFeatureType(::PhantomAttractorFeature) } + val JUMP_BOOST: AndroidFeatureType by registry.register(MNames.JUMP_BOOST) { AndroidFeatureType(::JumpBoostFeature) } + val ENDER_TELEPORTER: AndroidFeatureType by registry.register(MNames.ENDER_TELEPORTER) { AndroidFeatureType(::EnderTeleporterFeature) } internal fun register(bus: IEventBus) { registry.register(bus) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MRegistry.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MRegistry.kt index 1acaf7a3b..de5c7a32d 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MRegistry.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MRegistry.kt @@ -16,13 +16,13 @@ import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent import net.minecraftforge.registries.NewRegistryEvent import ru.dbotthepony.mc.otm.OverdriveThatMatters +import ru.dbotthepony.mc.otm.android.AndroidResearchResult +import ru.dbotthepony.mc.otm.android.AndroidResearchResults import ru.dbotthepony.mc.otm.android.AndroidFeatureType +import ru.dbotthepony.mc.otm.android.AndroidResearchDescription +import ru.dbotthepony.mc.otm.android.AndroidResearchDescriptions import ru.dbotthepony.mc.otm.android.feature.EnderTeleporterFeature -import ru.dbotthepony.mc.otm.android.feature.FallDampenersFeature -import ru.dbotthepony.mc.otm.android.feature.ItemMagnetFeature -import ru.dbotthepony.mc.otm.android.feature.JumpBoostFeature import ru.dbotthepony.mc.otm.android.feature.NanobotsArmorFeature -import ru.dbotthepony.mc.otm.android.feature.ShockwaveFeature import ru.dbotthepony.mc.otm.block.decorative.CargoCrateBlock import ru.dbotthepony.mc.otm.block.decorative.TritaniumPressurePlate import ru.dbotthepony.mc.otm.capability.matteryEnergy @@ -208,6 +208,10 @@ object MRegistry { MCreativeTabs.initialize(bus) DecimalProvider.register(bus) + AndroidResearchDescription.register(bus) + AndroidResearchDescriptions.register(bus) + AndroidResearchResult.register(bus) + AndroidResearchResults.register(bus) MBlocks.register(bus) MBlockEntities.register(bus) @@ -224,10 +228,6 @@ object MRegistry { // call static constructors NanobotsArmorFeature.Companion - ShockwaveFeature.Companion - ItemMagnetFeature.Companion - FallDampenersFeature.Companion - JumpBoostFeature.Companion EnderTeleporterFeature.Companion }