diff --git a/api/src/main/kotlin/com/possible_triangle/skygrid/api/events/SkygridElements.kt b/api/src/main/kotlin/com/possible_triangle/skygrid/api/events/SkygridElements.kt new file mode 100644 index 0000000..2f17108 --- /dev/null +++ b/api/src/main/kotlin/com/possible_triangle/skygrid/api/events/SkygridElements.kt @@ -0,0 +1,42 @@ +package com.possible_triangle.skygrid.api.events + +import com.possible_triangle.skygrid.platform.Services +import kotlinx.serialization.KSerializer +import kotlinx.serialization.serializer +import kotlin.reflect.KClass + +interface RegisterElementEvent { + + companion object { + val EVENT = Services.EVENTS.createEvent(RegisterElementEvent::class) + } + + fun register( + parent: KClass, + subclass: KClass, + serializer: KSerializer, + ) + +} + +interface RegisterElementBuilder { + fun subclass(clazz: KClass, serializer: KSerializer) +} + +inline fun RegisterElementEvent.register( + parent: KClass, + subclass: KClass, +) = register(parent, subclass, serializer()) + +inline fun RegisterElementBuilder.subclass(clazz: KClass) = + subclass(clazz, serializer()) + + +inline fun RegisterElementEvent.register( + parent: KClass, + builder: RegisterElementBuilder.() -> Unit, +) = object : RegisterElementBuilder { + override fun subclass(clazz: KClass, serializer: KSerializer) { + register(parent, clazz, serializer) + } +}.apply(builder) \ No newline at end of file diff --git a/common/src/main/kotlin/com/possible_triangle/skygrid/platform/Services.kt b/api/src/main/kotlin/com/possible_triangle/skygrid/platform/Services.kt similarity index 70% rename from common/src/main/kotlin/com/possible_triangle/skygrid/platform/Services.kt rename to api/src/main/kotlin/com/possible_triangle/skygrid/platform/Services.kt index 0fe8976..44b030d 100644 --- a/common/src/main/kotlin/com/possible_triangle/skygrid/platform/Services.kt +++ b/api/src/main/kotlin/com/possible_triangle/skygrid/platform/Services.kt @@ -1,7 +1,7 @@ package com.possible_triangle.skygrid.platform -import com.possible_triangle.skygrid.SkygridMod import com.possible_triangle.skygrid.platform.services.IConfig +import com.possible_triangle.skygrid.platform.services.IEvents import com.possible_triangle.skygrid.platform.services.IPlatformHelper import java.util.* @@ -9,13 +9,12 @@ object Services { val PLATFORM = load(IPlatformHelper::class.java) val CONFIG = load(IConfig::class.java) + val EVENTS = load(IEvents::class.java) private fun load(clazz: Class): T { - val loadedService: T = ServiceLoader.load(clazz) + return ServiceLoader.load(clazz) .findFirst() .orElseThrow { NullPointerException("Failed to load service for ${clazz.name}") } - SkygridMod.LOGGER.debug("Loaded $loadedService for service $clazz") - return loadedService } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/possible_triangle/skygrid/platform/services/IConfig.kt b/api/src/main/kotlin/com/possible_triangle/skygrid/platform/services/IConfig.kt similarity index 100% rename from common/src/main/kotlin/com/possible_triangle/skygrid/platform/services/IConfig.kt rename to api/src/main/kotlin/com/possible_triangle/skygrid/platform/services/IConfig.kt diff --git a/api/src/main/kotlin/com/possible_triangle/skygrid/platform/services/IEvents.kt b/api/src/main/kotlin/com/possible_triangle/skygrid/platform/services/IEvents.kt new file mode 100644 index 0000000..ed8c6f9 --- /dev/null +++ b/api/src/main/kotlin/com/possible_triangle/skygrid/platform/services/IEvents.kt @@ -0,0 +1,18 @@ +package com.possible_triangle.skygrid.platform.services + +import kotlin.reflect.KClass + +interface EventInvoker { + fun invoke(event: TEvent) + fun addListener(callback: EventCallback) +} + +fun interface EventCallback { + fun accept(event: T) +} + +interface IEvents { + + fun createEvent(clazz: KClass): EventInvoker + +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/possible_triangle/skygrid/platform/services/IPlatformHelper.kt b/api/src/main/kotlin/com/possible_triangle/skygrid/platform/services/IPlatformHelper.kt similarity index 92% rename from common/src/main/kotlin/com/possible_triangle/skygrid/platform/services/IPlatformHelper.kt rename to api/src/main/kotlin/com/possible_triangle/skygrid/platform/services/IPlatformHelper.kt index 4a4d814..65e4e2a 100644 --- a/common/src/main/kotlin/com/possible_triangle/skygrid/platform/services/IPlatformHelper.kt +++ b/api/src/main/kotlin/com/possible_triangle/skygrid/platform/services/IPlatformHelper.kt @@ -1,7 +1,6 @@ package com.possible_triangle.skygrid.platform.services import net.minecraft.resources.ResourceLocation -import net.minecraft.tags.TagKey import net.minecraft.world.level.block.Block import kotlin.properties.ReadOnlyProperty diff --git a/common/src/main/kotlin/com/possible_triangle/skygrid/SkygridMod.kt b/common/src/main/kotlin/com/possible_triangle/skygrid/SkygridMod.kt index f1dc5ee..1ecb7bb 100644 --- a/common/src/main/kotlin/com/possible_triangle/skygrid/SkygridMod.kt +++ b/common/src/main/kotlin/com/possible_triangle/skygrid/SkygridMod.kt @@ -2,9 +2,11 @@ package com.possible_triangle.skygrid import com.possible_triangle.skygrid.api.SkygridConstants.MOD_ID import com.possible_triangle.skygrid.api.SkygridConstants.MOD_NAME +import com.possible_triangle.skygrid.api.events.RegisterElementEvent import com.possible_triangle.skygrid.block.StiffAir import com.possible_triangle.skygrid.platform.Services import com.possible_triangle.skygrid.world.SkygridChunkGenerator +import com.possible_triangle.skygrid.xml.registerDefaultElements import com.possible_triangle.skygrid.xml.resources.DimensionConfigs import com.possible_triangle.skygrid.xml.resources.Presets import net.minecraft.ChatFormatting @@ -23,12 +25,18 @@ object SkygridMod { val STIFF_AIR by Services.PLATFORM.createBlock("stiff_air") { StiffAir() } fun init() { + registerEvents() Presets.register() DimensionConfigs.register() } + private fun registerEvents() { + RegisterElementEvent.EVENT.addListener { + it.registerDefaultElements() + } + } + fun setup() { - LOGGER.info("Skygrid booting") Registry.register(Registry.CHUNK_GENERATOR, ResourceLocation(MOD_ID, MOD_ID), SkygridChunkGenerator.CODEC) } diff --git a/common/src/main/kotlin/com/possible_triangle/skygrid/xml/XMLModule.kt b/common/src/main/kotlin/com/possible_triangle/skygrid/xml/XMLModule.kt new file mode 100644 index 0000000..6bf4671 --- /dev/null +++ b/common/src/main/kotlin/com/possible_triangle/skygrid/xml/XMLModule.kt @@ -0,0 +1,108 @@ +package com.possible_triangle.skygrid.xml + +import com.possible_triangle.skygrid.api.events.RegisterElementEvent +import com.possible_triangle.skygrid.api.events.register +import com.possible_triangle.skygrid.api.events.subclass +import com.possible_triangle.skygrid.api.xml.DeserializationException +import com.possible_triangle.skygrid.api.xml.elements.* +import com.possible_triangle.skygrid.api.xml.elements.extras.Cardinal +import com.possible_triangle.skygrid.api.xml.elements.extras.Offset +import com.possible_triangle.skygrid.api.xml.elements.extras.Side +import com.possible_triangle.skygrid.api.xml.elements.filters.ExceptFilter +import com.possible_triangle.skygrid.api.xml.elements.filters.ModFilter +import com.possible_triangle.skygrid.api.xml.elements.filters.NameFilter +import com.possible_triangle.skygrid.api.xml.elements.filters.TagFilter +import com.possible_triangle.skygrid.api.xml.elements.providers.* +import com.possible_triangle.skygrid.api.xml.elements.transformers.CyclePropertyTransformer +import com.possible_triangle.skygrid.api.xml.elements.transformers.SetPropertyTransformer +import kotlinx.serialization.KSerializer +import kotlinx.serialization.modules.PolymorphicModuleBuilder +import kotlinx.serialization.modules.SerializersModule +import kotlinx.serialization.modules.SerializersModuleBuilder +import kotlinx.serialization.modules.polymorphic +import nl.adaptivity.xmlutil.ExperimentalXmlUtilApi +import nl.adaptivity.xmlutil.serialization.UnknownChildHandler +import nl.adaptivity.xmlutil.serialization.XML +import nl.adaptivity.xmlutil.serialization.XmlSerializationPolicy +import kotlin.reflect.KClass + +private typealias Sub = Pair, KSerializer> + +private fun SerializersModuleBuilder.collectRegisteredElements() { + val collected = hashMapOf, MutableCollection>>() + + RegisterElementEvent.EVENT.invoke(object : RegisterElementEvent { + override fun register( + parent: KClass, + subclass: KClass, + serializer: KSerializer, + ) { + collected.getOrPut(parent, ::hashSetOf).add(subclass to serializer) + } + }) + + fun PolymorphicModuleBuilder.subclass(it: Sub<*>) { + subclass(it.first as KClass, it.second as KSerializer) + } + + fun registerPolymorphic(parent: KClass, subclasses: Collection>) { + polymorphic(parent) { + subclasses.forEach { + subclass(it) + } + } + } + + collected.forEach { (parent, subclasses) -> + registerPolymorphic(parent, subclasses) + } +} + +fun RegisterElementEvent.registerDefaultElements() { + register(BlockProvider::class) { + subclass(BlockList::class) + subclass(Fallback::class) + subclass(Tag::class) + subclass(SingleBlock::class) + subclass(Reference::class) + } + + register(Transformer::class) { + subclass(SetPropertyTransformer::class) + subclass(CyclePropertyTransformer::class) + } + + register(FilterOperator::class) { + subclass(ExceptFilter::class) + } + + register(Extra::class) { + subclass(Side::class) + subclass(Cardinal::class) + subclass(Offset::class) + } + + register(Filter::class) { + subclass(NameFilter::class) + subclass(ModFilter::class) + subclass(TagFilter::class) + } +} + +@ExperimentalXmlUtilApi +fun createXMLModule() = XML(SerializersModule { + collectRegisteredElements() +}) { + defaultPolicy { + indentString = " ".repeat(3) + autoPolymorphic = true + encodeDefault = XmlSerializationPolicy.XmlEncodeDefault.NEVER + unknownChildHandler = UnknownChildHandler { input, _, descriptor, name, candidates -> + throw DeserializationException( + input.locationInfo, + "${descriptor.tagName}/${name ?: ""}", + candidates + ) + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/possible_triangle/skygrid/xml/XMLResource.kt b/common/src/main/kotlin/com/possible_triangle/skygrid/xml/XMLResource.kt index e8993ef..ae13a91 100644 --- a/common/src/main/kotlin/com/possible_triangle/skygrid/xml/XMLResource.kt +++ b/common/src/main/kotlin/com/possible_triangle/skygrid/xml/XMLResource.kt @@ -1,24 +1,12 @@ package com.possible_triangle.skygrid.xml +import com.google.common.base.Suppliers import com.possible_triangle.skygrid.SkygridMod.LOGGER import com.possible_triangle.skygrid.api.SkygridConstants import com.possible_triangle.skygrid.api.xml.DeserializationException -import com.possible_triangle.skygrid.api.xml.elements.* -import com.possible_triangle.skygrid.api.xml.elements.extras.Cardinal -import com.possible_triangle.skygrid.api.xml.elements.extras.Offset -import com.possible_triangle.skygrid.api.xml.elements.extras.Side -import com.possible_triangle.skygrid.api.xml.elements.filters.ExceptFilter -import com.possible_triangle.skygrid.api.xml.elements.filters.ModFilter -import com.possible_triangle.skygrid.api.xml.elements.filters.NameFilter -import com.possible_triangle.skygrid.api.xml.elements.filters.TagFilter -import com.possible_triangle.skygrid.api.xml.elements.providers.* -import com.possible_triangle.skygrid.api.xml.elements.transformers.CyclePropertyTransformer -import com.possible_triangle.skygrid.api.xml.elements.transformers.SetPropertyTransformer import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.KSerializer -import kotlinx.serialization.modules.SerializersModule -import kotlinx.serialization.modules.polymorphic -import kotlinx.serialization.modules.subclass +import kotlinx.serialization.StringFormat import net.minecraft.resources.ResourceLocation import net.minecraft.server.MinecraftServer import net.minecraft.server.packs.resources.Resource @@ -26,9 +14,6 @@ import net.minecraft.server.packs.resources.ResourceManager import net.minecraft.server.packs.resources.SimplePreparableReloadListener import net.minecraft.util.profiling.ProfilerFiller import nl.adaptivity.xmlutil.ExperimentalXmlUtilApi -import nl.adaptivity.xmlutil.serialization.UnknownChildHandler -import nl.adaptivity.xmlutil.serialization.XML -import nl.adaptivity.xmlutil.serialization.XmlSerializationPolicy import java.io.File abstract class XMLResource(val path: String, private val serializer: () -> KSerializer) : @@ -37,49 +22,7 @@ abstract class XMLResource(val path: String, private val serializer: () -> KS @ExperimentalXmlUtilApi @ExperimentalSerializationApi companion object { - val LOADER = XML(SerializersModule { - polymorphic(BlockProvider::class) { - subclass(BlockList::class) - subclass(Fallback::class) - subclass(Tag::class) - subclass(SingleBlock::class) - subclass(Reference::class) - } - - polymorphic(Transformer::class) { - subclass(SetPropertyTransformer::class) - subclass(CyclePropertyTransformer::class) - } - - polymorphic(FilterOperator::class) { - subclass(ExceptFilter::class) - } - - polymorphic(Extra::class) { - subclass(Side::class) - subclass(Cardinal::class) - subclass(Offset::class) - } - - polymorphic(Filter::class) { - subclass(NameFilter::class) - subclass(ModFilter::class) - subclass(TagFilter::class) - } - }) { - defaultPolicy { - indentString = " ".repeat(3) - autoPolymorphic = true - encodeDefault = XmlSerializationPolicy.XmlEncodeDefault.NEVER - unknownChildHandler = UnknownChildHandler { input, _, descriptor, name, candidates -> - throw DeserializationException( - input.locationInfo, - "${descriptor.tagName}/${name ?: ""}", - candidates - ) - } - } - } + val LOADER: StringFormat get() = Suppliers.memoize(::createXMLModule).get() private val RESOURCES = arrayListOf>() diff --git a/common/src/test/kotlin/com/possible_triangle/skygrid/test/TestExtension.kt b/common/src/test/kotlin/com/possible_triangle/skygrid/test/TestExtension.kt index 9b539db..537557a 100644 --- a/common/src/test/kotlin/com/possible_triangle/skygrid/test/TestExtension.kt +++ b/common/src/test/kotlin/com/possible_triangle/skygrid/test/TestExtension.kt @@ -1,5 +1,6 @@ package com.possible_triangle.skygrid.test +import com.possible_triangle.skygrid.SkygridMod import com.possible_triangle.skygrid.test.mocks.WorldMock import net.minecraft.SharedConstants import net.minecraft.server.Bootstrap @@ -20,6 +21,7 @@ class TestExtension : BeforeAllCallback, BeforeEachCallback { SharedConstants.tryDetectVersion() Bootstrap.bootStrap() Bootstrap.validate() + SkygridMod.init() } override fun beforeEach(context: ExtensionContext?) { diff --git a/common/src/test/kotlin/com/possible_triangle/skygrid/test/mocks/EventsMock.kt b/common/src/test/kotlin/com/possible_triangle/skygrid/test/mocks/EventsMock.kt new file mode 100644 index 0000000..ba1953a --- /dev/null +++ b/common/src/test/kotlin/com/possible_triangle/skygrid/test/mocks/EventsMock.kt @@ -0,0 +1,26 @@ +package com.possible_triangle.skygrid.test.mocks + +import com.possible_triangle.skygrid.platform.services.EventCallback +import com.possible_triangle.skygrid.platform.services.EventInvoker +import com.possible_triangle.skygrid.platform.services.IEvents +import kotlin.reflect.KClass + +class EventsMock : IEvents { + + class SimpleEventBus : EventInvoker { + private val listeners = hashSetOf>() + + override fun invoke(event: TEvent) { + listeners.forEach { + it.accept(event) + } + } + + override fun addListener(callback: EventCallback) { + listeners.add(callback) + } + } + + override fun createEvent(clazz: KClass) = SimpleEventBus() + +} \ No newline at end of file diff --git a/common/src/test/resources/META-INF/services/com.possible_triangle.skygrid.platform.services.IEvents b/common/src/test/resources/META-INF/services/com.possible_triangle.skygrid.platform.services.IEvents new file mode 100644 index 0000000..260ac16 --- /dev/null +++ b/common/src/test/resources/META-INF/services/com.possible_triangle.skygrid.platform.services.IEvents @@ -0,0 +1 @@ +com.possible_triangle.skygrid.test.mocks.EventsMock \ No newline at end of file diff --git a/common/src/test/resources/snapshot.properties b/common/src/test/resources/snapshot.properties index f7dd9b9..cd7a76c 100644 --- a/common/src/test/resources/snapshot.properties +++ b/common/src/test/resources/snapshot.properties @@ -1,6 +1,7 @@ -serializer=au.com.origin.snapshots.serializers.ToStringSnapshotSerializer -comparator=au.com.origin.snapshots.comparators.PlainTextEqualsComparator -reporters=au.com.origin.snapshots.reporters.PlainTextSnapshotReporter +serializer=au.com.origin.snapshots.serializers.v1.ToStringSnapshotSerializer +comparator=au.com.origin.snapshots.comparators.v1.PlainTextEqualsComparator +reporters=au.com.origin.snapshots.reporters.v1.PlainTextSnapshotReporter snapshot-dir=__snapshots__ output-dir=../src/test/kotlin -ci-env-var=CI \ No newline at end of file +ci-env-var=CI +update-snapshot=none \ No newline at end of file diff --git a/fabric/src/main/kotlin/com/possible_triangle/skygrid/fabric/platform/FabricEvents.kt b/fabric/src/main/kotlin/com/possible_triangle/skygrid/fabric/platform/FabricEvents.kt new file mode 100644 index 0000000..da32349 --- /dev/null +++ b/fabric/src/main/kotlin/com/possible_triangle/skygrid/fabric/platform/FabricEvents.kt @@ -0,0 +1,27 @@ +package com.possible_triangle.skygrid.fabric.platform + +import com.possible_triangle.skygrid.platform.services.EventCallback +import com.possible_triangle.skygrid.platform.services.EventInvoker +import com.possible_triangle.skygrid.platform.services.IEvents +import net.fabricmc.fabric.api.event.EventFactory +import kotlin.reflect.KClass + +class FabricEvents : IEvents { + + override fun createEvent(clazz: KClass): EventInvoker { + val type = EventCallback::class.java + val fabricEvent = EventFactory.createArrayBacked>(type) { listeners -> + EventCallback { event -> + listeners.forEach { callback -> + callback.accept(event) + } + } + } + + return object : EventInvoker { + override fun invoke(event: TEvent) = fabricEvent.invoker().accept(event) + override fun addListener(callback: EventCallback) = fabricEvent.register(callback) + } + } + +} \ No newline at end of file diff --git a/fabric/src/main/resources/META-INF/services/com.possible_triangle.skygrid.platform.services.IEvents b/fabric/src/main/resources/META-INF/services/com.possible_triangle.skygrid.platform.services.IEvents new file mode 100644 index 0000000..3ed9f1c --- /dev/null +++ b/fabric/src/main/resources/META-INF/services/com.possible_triangle.skygrid.platform.services.IEvents @@ -0,0 +1 @@ +com.possible_triangle.skygrid.fabric.platform.FabricEvents \ No newline at end of file diff --git a/forge/src/main/kotlin/com/possible_triangle/skygrid/forge/platform/ForgeEvents.kt b/forge/src/main/kotlin/com/possible_triangle/skygrid/forge/platform/ForgeEvents.kt new file mode 100644 index 0000000..0001c93 --- /dev/null +++ b/forge/src/main/kotlin/com/possible_triangle/skygrid/forge/platform/ForgeEvents.kt @@ -0,0 +1,28 @@ +package com.possible_triangle.skygrid.forge.platform + +import com.possible_triangle.skygrid.platform.services.EventCallback +import com.possible_triangle.skygrid.platform.services.EventInvoker +import com.possible_triangle.skygrid.platform.services.IEvents +import net.minecraftforge.eventbus.api.GenericEvent +import thedarkcolour.kotlinforforge.forge.FORGE_BUS +import kotlin.reflect.KClass + +class ForgeEvents : IEvents { + + private class ForgeEvent(val event: TEvent) : GenericEvent() + + override fun createEvent(clazz: KClass): EventInvoker { + return object : EventInvoker { + override fun invoke(event: TEvent) { + FORGE_BUS.post(ForgeEvent(event)) + } + + override fun addListener(callback: EventCallback) { + FORGE_BUS.addGenericListener(clazz.java) { event: ForgeEvent -> + callback.accept(event.event) + } + } + } + } + +} \ No newline at end of file diff --git a/forge/src/main/kotlin/com/possible_triangle/skygrid/forge/platform/ForgePlatformHelper.kt b/forge/src/main/kotlin/com/possible_triangle/skygrid/forge/platform/ForgePlatformHelper.kt index 2713b3d..609158a 100644 --- a/forge/src/main/kotlin/com/possible_triangle/skygrid/forge/platform/ForgePlatformHelper.kt +++ b/forge/src/main/kotlin/com/possible_triangle/skygrid/forge/platform/ForgePlatformHelper.kt @@ -1,10 +1,10 @@ package com.possible_triangle.skygrid.forge.platform +import com.possible_triangle.skygrid.forge.SkygridForge.BLOCKS +import com.possible_triangle.skygrid.platform.services.IPlatformHelper import net.minecraft.resources.ResourceLocation import net.minecraft.world.level.block.Block import net.minecraftforge.fml.loading.FMLLoader -import com.possible_triangle.skygrid.forge.SkygridForge.BLOCKS -import com.possible_triangle.skygrid.platform.services.IPlatformHelper import thedarkcolour.kotlinforforge.forge.registerObject import javax.annotation.Nonnull import kotlin.properties.ReadOnlyProperty diff --git a/forge/src/main/resources/META-INF/services/com.possible_triangle.skygrid.platform.services.IEvents b/forge/src/main/resources/META-INF/services/com.possible_triangle.skygrid.platform.services.IEvents new file mode 100644 index 0000000..c1d0d49 --- /dev/null +++ b/forge/src/main/resources/META-INF/services/com.possible_triangle.skygrid.platform.services.IEvents @@ -0,0 +1 @@ +com.possible_triangle.skygrid.forge.platform.ForgeEvents \ No newline at end of file