Skip to content

Conversation

DevMunky
Copy link
Contributor

@DevMunky DevMunky commented Jul 11, 2025

Implement Kotlin Serialization to replace google.Gson.

Allows much more flexibility for final data format, such as CBOR or Protobuf.

Summary by CodeRabbit

  • New Features

    • Migrated all JSON serialization and deserialization from Gson to Kotlinx Serialization for improved consistency and type safety.
    • Centralized dependency and plugin version management using a new version catalog and dependencies file.
    • Added Kotlinx Serialization support annotations to core data classes and interfaces, including sound handling and extension metadata.
  • Refactor

    • Simplified and standardized serializers by delegating to Kotlinx Serialization implementations, reducing manual code.
    • Updated build scripts and project settings to utilize version catalogs and improved plugin management.
    • Replaced custom Gson-based serializers with Kotlinx Serialization contextual serializers for Bukkit types and internal data models.
    • Streamlined extension loader to use Kotlinx Serialization and changed extension collections from lists to sets.
  • Chores

    • Updated Gradle wrapper scripts and configuration for improved compatibility and maintainability.
    • Added SPDX license identifiers to build scripts.

* Added dependencies.toml and version catalogue.

* Added Serializers for easy data class serialization for non-kotlin classes, like

Took 3 hours 42 minutes
Copy link
Contributor

coderabbitai bot commented Jul 11, 2025

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This update migrates the entire codebase from Gson-based JSON serialization to Kotlinx Serialization, affecting both core and paper modules. It introduces a centralized version catalog for dependencies and plugins, updates Gradle configuration and wrapper scripts, and refactors numerous serializers to use concise, delegated Kotlinx Serialization implementations. Several previously custom serializers are removed or replaced by standard or generated serializers. Serialization annotations are added broadly to data classes and sealed interfaces.

Changes

File(s) Change Summary
engine/build.gradle.kts, engine/settings.gradle.kts, engine/dependencies.toml, engine/gradle/wrapper/gradle-wrapper.properties, engine/gradlew, engine/gradlew.bat Migrated build configuration to use version catalogs, updated Gradle wrapper and scripts, and added centralized dependency management.
engine/engine-core/src/main/kotlin/com/typewritermc/core/books/pages/PageType.kt, engine/engine-core/src/main/kotlin/com/typewritermc/core/entries/Entry.kt, engine/engine-core/src/main/kotlin/com/typewritermc/core/entries/EntryReference.kt, engine/engine-core/src/main/kotlin/com/typewritermc/core/entries/Library.kt, engine/engine-core/src/main/kotlin/com/typewritermc/core/entries/Page.kt, engine/engine-core/src/main/kotlin/com/typewritermc/core/interaction/Interaction.kt, engine/engine-core/src/main/kotlin/com/typewritermc/core/utils/Casting.kt, engine/engine-core/src/main/kotlin/com/typewritermc/core/utils/Generic.kt Replaced Gson annotations and logic with Kotlinx Serialization; added or updated serialization annotations and logic throughout core entities.
engine/engine-core/src/main/kotlin/com/typewritermc/core/serialization/AlgebraicSerialization.kt, engine/engine-core/src/main/kotlin/com/typewritermc/core/utils/RuntimeTypeAdapterFactory.kt Removed custom Gson-based type adapter factories for algebraic and polymorphic types.
engine/engine-core/src/main/kotlin/com/typewritermc/core/serialization/DataSerializer.kt Refactored DataSerializer interface and related utilities to use Kotlinx Serialization; removed Gson context and added new serializer utilities and extension functions.
engine/engine-core/src/main/kotlin/com/typewritermc/core/serialization/Serializers.kt Added new internal utilities for generating serializers for data classes of varying arity using Kotlinx Serialization.
engine/engine-core/src/main/kotlin/com/typewritermc/core/utils/Extensions.kt Added a toSnakeCase string extension function.
engine/engine-loader/src/main/kotlin/com/typewritermc/loader/ExtensionLoader.kt Migrated extension metadata serialization from Gson to Kotlinx Serialization, updated collection types, and added serialization annotations to data classes and enums.
engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/TypewriterPaperPlugin.kt Updated dependency injection to provide Kotlinx Serialization's Json format instead of Gson.
engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/entry/entries/VariableEntry.kt Migrated variable context and Var interface serialization from Gson to Kotlinx Serialization; added serialization annotations.
engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/* Removed many explicit Gson-based serializers; replaced others with delegated Kotlinx Serialization serializers; renamed properties from type to clazz where applicable; commented out obsolete serializers.
engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/utils/BukkitDataParser.kt Replaced Gson-based Bukkit type serializers with Kotlinx Serialization equivalents; added contextual serializers for Bukkit types; removed custom Gson adapters.
engine/engine-core/src/main/kotlin/com/typewritermc/core/utils/point/Position.kt, engine/engine-core/src/main/kotlin/com/typewritermc/core/utils/point/World.kt, engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/entry/entity/SkinProperty.kt, engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/utils/Sound.kt Added @Serializable annotations to data classes and sealed interfaces to enable Kotlinx Serialization support.

Sequence Diagram(s)

sequenceDiagram
    participant App
    participant Serializer
    participant JsonFormat

    App->>Serializer: Request serialization/deserialization
    Serializer->>JsonFormat: Encode/Decode using KSerializer
    JsonFormat-->>Serializer: Serialized/Deserialized data
    Serializer-->>App: Return result
Loading

Poem

🐇
Goodbye to Gson, so old and so wise,
Hello to Kotlinx, a sparkling surprise!
Serializers now dance with less code to write,
Version catalogs keep dependencies tight.
With JSON in hand and a spring in my hop,
This bunny’s serialization will never stop!
🥕

✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai auto-generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 15

🔭 Outside diff range comments (11)
engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/EntryReferenceSerializer.kt (1)

1-38: Remove unused imports and commented code

Since the Ref class is now handled by @Serializable annotation (as indicated by the comment), this entire file appears to be obsolete. Either:

  1. Delete this file entirely if it's no longer needed, or
  2. Remove all the commented code and unused imports

All imports in this file are now unused after commenting out the implementation.

If keeping the file, apply this diff to clean it up:

-package com.typewritermc.engine.paper.loader.serializers
-
-import com.google.common.reflect.TypeToken
-import com.google.gson.JsonDeserializationContext
-import com.google.gson.JsonElement
-import com.google.gson.JsonPrimitive
-import com.google.gson.JsonSerializationContext
-import com.typewritermc.core.entries.Entry
-import com.typewritermc.core.entries.Ref
-import com.typewritermc.core.serialization.DataSerializer
-import java.lang.reflect.ParameterizedType
-import java.lang.reflect.Type
-import kotlin.reflect.KClass
-// should be handled by @Serializable
-//class EntryReferenceSerializer : DataSerializer<Ref<*>> {
-//    override val clazz: Type = Ref::class.java
-//
-//    override fun serialize(src: Ref<*>?, typeOfSrc: Type?, context: JsonSerializationContext?): JsonElement {
-//        return JsonPrimitive(src?.id ?: "")
-//    }
-//
-//    override fun deserialize(
-//        json: JsonElement?,
-//        typeOfT: Type?,
-//        context: JsonDeserializationContext?
-//    ): Ref<*> {
-//        val subType = (typeOfT as ParameterizedType).actualTypeArguments[0]
-//        val clazz = TypeToken.of(subType).rawType
-//        val klass = clazz.kotlin as KClass<Entry>
-//
-//        if (json?.isJsonNull == true) {
-//            return Ref("", klass)
-//        }
-//
-//        val id = json?.asString ?: ""
-//        return Ref(id, klass)
-//    }
-//}
+// This file is no longer needed as Ref is now handled by @Serializable annotation

Or preferably, delete the entire file.

engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/SoundIdSerializer.kt (1)

3-12: Remove unused imports

After refactoring to use delegated serialization, the following imports are no longer used and should be removed:

  • Lines 3-6: Gson-related imports
  • Line 12: java.lang.reflect.Type
-import com.google.gson.JsonDeserializationContext
-import com.google.gson.JsonElement
-import com.google.gson.JsonObject
-import com.google.gson.JsonSerializationContext
 import com.typewritermc.engine.paper.utils.DefaultSoundId
 import com.typewritermc.engine.paper.utils.EntrySoundId
 import com.typewritermc.engine.paper.utils.SoundId
 import com.typewritermc.core.serialization.DataSerializer
 import com.typewritermc.core.serialization.DataSerializer.Companion.toDataSerializer
-import java.lang.reflect.Type
engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/VectorSerializer.kt (1)

3-11: Remove unused imports

After refactoring to use delegated serialization, the following imports are no longer used and should be removed:

  • Lines 3-6: Gson-related imports
  • Line 11: java.lang.reflect.Type
-import com.google.gson.JsonDeserializationContext
-import com.google.gson.JsonElement
-import com.google.gson.JsonObject
-import com.google.gson.JsonSerializationContext
 import com.typewritermc.core.serialization.DataSerializer
 import com.typewritermc.core.serialization.serializer
 import kotlinx.serialization.builtins.serializer
 import org.bukkit.util.Vector
-import java.lang.reflect.Type
engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/WorldSerializer.kt (1)

3-13: Remove unused imports

After refactoring to use delegated serialization, the following imports are no longer used and should be removed:

  • Lines 3-6: Gson-related imports
  • Lines 10-11: logErrorIfNull and server utilities
  • Line 12: java.lang.reflect.Type
  • Line 13: java.util.*
-import com.google.gson.JsonDeserializationContext
-import com.google.gson.JsonElement
-import com.google.gson.JsonPrimitive
-import com.google.gson.JsonSerializationContext
 import com.typewritermc.core.serialization.DataSerializer
 import com.typewritermc.core.serialization.DataSerializer.Companion.toDataSerializer
 import com.typewritermc.core.utils.point.World
-import com.typewritermc.engine.paper.utils.logErrorIfNull
-import com.typewritermc.engine.paper.utils.server
-import java.lang.reflect.Type
-import java.util.*
engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/ColorSerializer.kt (1)

3-12: Remove unused imports

After refactoring to use delegated serialization, the following imports are no longer used and should be removed:

  • Lines 3-6: Gson-related imports
  • Line 12: java.lang.reflect.Type
-import com.google.gson.JsonDeserializationContext
-import com.google.gson.JsonElement
-import com.google.gson.JsonPrimitive
-import com.google.gson.JsonSerializationContext
 import com.typewritermc.engine.paper.utils.Color
 import com.typewritermc.core.serialization.DataSerializer
 import com.typewritermc.core.serialization.DataSerializer.Companion.toDataSerializer
 import com.typewritermc.core.serialization.xmap
 import kotlinx.serialization.builtins.serializer
-import java.lang.reflect.Type
engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/DurationSerializer.kt (1)

3-11: Remove unused Gson imports after migration.

The Gson-related imports are no longer needed after migrating to Kotlinx Serialization.

-import com.google.gson.JsonDeserializationContext
-import com.google.gson.JsonElement
-import com.google.gson.JsonPrimitive
-import com.google.gson.JsonSerializationContext
 import com.typewritermc.core.serialization.DataSerializer
 import com.typewritermc.core.serialization.DataSerializer.Companion.toDataSerializer
 import com.typewritermc.core.serialization.xmap
 import kotlinx.serialization.builtins.serializer
-import java.lang.reflect.Type
 import java.time.Duration
 import kotlin.time.toJavaDuration
 import kotlin.time.toKotlinDuration
engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/SkinPropertySerializer.kt (1)

3-15: Remove all unused imports after migration.

The delegation to SkinProperty.serializer().toDataSerializer() is correct, but the file has many unused imports that should be removed.

 package com.typewritermc.engine.paper.loader.serializers
 
-import com.google.gson.JsonDeserializationContext
-import com.google.gson.JsonElement
-import com.google.gson.JsonObject
-import com.google.gson.JsonSerializationContext
 import com.typewritermc.engine.paper.entry.entity.SkinProperty
 import com.typewritermc.core.serialization.DataSerializer
 import com.typewritermc.core.serialization.DataSerializer.Companion.toDataSerializer
-import com.typewritermc.core.serialization.serializer
-import kotlinx.serialization.builtins.nullable
-import kotlinx.serialization.builtins.serializer
-import java.lang.reflect.Type
 
 class SkinPropertySerializer : DataSerializer<SkinProperty> by SkinProperty.serializer().toDataSerializer()
engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/entry/entries/VariableEntry.kt (2)

69-75: Missing @serializable annotation on ConstVar class.

The ConstVar class is missing the @Serializable annotation, which is inconsistent with other Var implementations and could cause serialization issues.

+@Serializable
 class ConstVar<T : Any>(val value: T) : Var<T> {

109-119: Serialization Strategy for ComputeVar
Currently, any ComputeVar instance will cause VarSerializer to throw an IllegalStateException (“Could not serialize compute var”). You need to decide which of the following makes the intended behavior explicit:

  • If ComputeVar should never be serialized:
    • Document this in the class KDoc (e.g. “This implementation is not serializable.”).
    • Optionally annotate the compute property with @Transient or mark the class as non-serializable so downstream consumers know it’s excluded.
  • If you do need to persist ComputeVar logic in configs:
    • Introduce a metadata representation for the computation (e.g. a serializable descriptor or enum of built-in functions).
    • Implement a custom serializer that can read/write that descriptor and reconstruct the lambda at runtime.

Locations to address:

  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/VarSerializer.kt (handles ComputeVar by throwing)
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/entry/entries/VariableEntry.kt (definition of ComputeVar)
engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/PotionEffectTypeSerializer.kt (1)

3-6: Remove unused Gson imports.

These Gson-related imports are no longer used after the migration to Kotlinx Serialization.

-import com.google.gson.JsonDeserializationContext
-import com.google.gson.JsonElement
-import com.google.gson.JsonPrimitive
-import com.google.gson.JsonSerializationContext
 import com.typewritermc.core.serialization.DataSerializer
 import com.typewritermc.core.serialization.DataSerializer.Companion.toDataSerializer
 import com.typewritermc.core.serialization.xmap
 import kotlinx.serialization.builtins.serializer
 import net.kyori.adventure.key.Key
 import org.bukkit.Registry
 import org.bukkit.potion.PotionEffectType
-import java.lang.reflect.Type

Also applies to: 14-14

engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/ClosedRangeSerializer.kt (1)

3-6: Remove unused Gson imports and Java reflection types.

These imports are no longer needed after migrating to Kotlinx Serialization.

-import com.google.gson.JsonDeserializationContext
-import com.google.gson.JsonElement
-import com.google.gson.JsonObject
-import com.google.gson.JsonSerializationContext
 import com.typewritermc.core.serialization.DataSerializer
 import com.typewritermc.core.serialization.DataSerializer.Companion.toDataSerializer
 import com.typewritermc.engine.paper.plugin
 import kotlinx.serialization.builtins.serializer
 import kotlinx.serialization.descriptors.SerialDescriptor
 import kotlinx.serialization.descriptors.buildClassSerialDescriptor
 import kotlinx.serialization.encoding.Decoder
 import kotlinx.serialization.encoding.Encoder
 import kotlinx.serialization.json.Json
 import kotlinx.serialization.serializer
 import org.koin.core.component.get
 import org.koin.core.qualifier.named
-import java.lang.reflect.ParameterizedType
-import java.lang.reflect.Type
 import kotlin.reflect.KClass

Also applies to: 19-20

♻️ Duplicate comments (1)
engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/EntryInteractionContextKeySerializer.kt (1)

19-21: Same Type vs Class<*> mismatch as above

clazz should be a Class<*> just like the rest of the fleet to avoid instanceof casts:

- override val clazz: Type = EntryInteractionContextKey::class.java
+ override val clazz: Class<*> = EntryInteractionContextKey::class.java
🧹 Nitpick comments (11)
engine/engine-core/src/main/kotlin/com/typewritermc/core/entries/Entry.kt (1)

5-5: Unused import should be removed or applied.

The kotlinx.serialization.Serializable import is added but not used in the visible code. If this is preparation for future serialization, consider adding a TODO comment. Otherwise, remove the unused import.

-import kotlinx.serialization.Serializable
+// TODO: Add @Serializable annotation when implementing serialization
+import kotlinx.serialization.Serializable
engine/engine-core/src/main/kotlin/com/typewritermc/core/entries/Page.kt (1)

4-6: Good explanatory comment, but could be more specific.

The comment explaining why @Serializable is not used is helpful. Consider expanding it to explain what specific aspects of error reporting would be affected.

-// Could be @Serializable, but error reporting would be "worse"
+// Could be @Serializable, but would degrade error reporting quality
+// by losing detailed field-level validation messages during deserialization
engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/VarSerializer.kt (1)

15-16: clazz rename is fine, but the serializer is still Gson-bound – plan removal or bridge

Property rename aligns with the new DataSerializer contract, ✔️.
However, the whole class keeps hard references to Gson while the PR objective is “switch to Kotlin Serialization”. Carrying both stacks balloons the classpath and slows reflection scanning.

Consider one of:

- import com.google.gson.*
+ // kotlinx.serialization.json.* and bridging adapter (if strictly needed)

or implement a VarSerializer via KSerializer + DataSerializer.toDataSerializer() like other migrated types.

engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/GenericSerializer.kt (1)

1-7: Remove unused imports after migration to Kotlinx Serialization.

The migration to delegated serializer looks good. However, you can clean up the imports since only DataSerializer and toDataSerializer are needed now.

 package com.typewritermc.engine.paper.loader.serializers
 
 import com.typewritermc.core.serialization.DataSerializer
 import com.typewritermc.core.serialization.DataSerializer.Companion.toDataSerializer
 import com.typewritermc.core.utils.Generic
 
 class GenericSerializer : DataSerializer<Generic> by Generic.serializer().toDataSerializer()
engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/CronExpressionSerializer.kt (1)

14-14: Consider expanding the comment for clarity.

The comment // CronExpression.createDynamic is quite minimal. Consider expanding it to explain the purpose or signature of the method.

-// CronExpression.createDynamic
+// CronExpression.createDynamic(expression: String): CronExpression
engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/ClosedRangeSerializer.kt (1)

10-17: Remove unused Kotlinx Serialization imports.

Several Kotlinx Serialization imports are not used in this delegated implementation.

 import com.typewritermc.engine.paper.plugin
-import kotlinx.serialization.builtins.serializer
-import kotlinx.serialization.descriptors.SerialDescriptor
-import kotlinx.serialization.descriptors.buildClassSerialDescriptor
-import kotlinx.serialization.encoding.Decoder
-import kotlinx.serialization.encoding.Encoder
 import kotlinx.serialization.json.Json
 import kotlinx.serialization.serializer
 import org.koin.core.component.get
 import org.koin.core.qualifier.named
engine/engine-core/src/main/kotlin/com/typewritermc/core/entries/Library.kt (1)

94-94: Consider enabling the @serializable annotation check.

The commented-out require statement would ensure entry classes are properly annotated for Kotlinx Serialization. This check helps catch configuration errors early.

-        // require(clazz.kotlin.hasAnnotation<Serializable>()) { "Entry class '${clazz.name}' is not annotated with @Serializable." }
+        require(clazz.kotlin.hasAnnotation<Serializable>()) { "Entry class '${clazz.name}' is not annotated with @Serializable." }
engine/engine-core/src/main/kotlin/com/typewritermc/core/utils/Generic.kt (1)

58-58: Consider implementing recursive comparison for complex types.

The TODO comment indicates that recursive comparison for JsonObject and JsonArray might be beneficial for proper ordering semantics.

Would you like me to implement recursive comparison logic for JsonObject and JsonArray to provide more meaningful ordering?

engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/utils/BukkitDataParser.kt (1)

22-29: Consider implementing a centralized serialization module registry.

The TODO comment indicates awareness of the issue. A centralized approach would improve maintainability and avoid scattered serializer registrations across the codebase.

Would you like me to suggest an implementation for a centralized serialization module management system?

engine/engine-core/src/main/kotlin/com/typewritermc/core/serialization/Serializers.kt (2)

1-1: Document the rationale for suppressed warnings.

Consider adding a comment explaining why these suppressions are necessary, especially for future maintainers.


55-334: Consider code generation for repetitive serializer functions.

While the current implementation is correct and type-safe, the repetitive nature of these functions could be addressed through code generation (e.g., using KSP or Kotlin compiler plugins) to reduce maintenance burden and support more than 10 properties if needed.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b1d005b and 9416d31.

⛔ Files ignored due to path filters (1)
  • engine/gradle/wrapper/gradle-wrapper.jar is excluded by !**/*.jar
📒 Files selected for processing (40)
  • engine/build.gradle.kts (3 hunks)
  • engine/dependencies.toml (1 hunks)
  • engine/engine-core/src/main/kotlin/com/typewritermc/core/books/pages/PageType.kt (1 hunks)
  • engine/engine-core/src/main/kotlin/com/typewritermc/core/entries/Entry.kt (2 hunks)
  • engine/engine-core/src/main/kotlin/com/typewritermc/core/entries/EntryReference.kt (2 hunks)
  • engine/engine-core/src/main/kotlin/com/typewritermc/core/entries/Library.kt (4 hunks)
  • engine/engine-core/src/main/kotlin/com/typewritermc/core/entries/Page.kt (1 hunks)
  • engine/engine-core/src/main/kotlin/com/typewritermc/core/interaction/Interaction.kt (2 hunks)
  • engine/engine-core/src/main/kotlin/com/typewritermc/core/serialization/AlgebraicSerialization.kt (0 hunks)
  • engine/engine-core/src/main/kotlin/com/typewritermc/core/serialization/DataSerializer.kt (1 hunks)
  • engine/engine-core/src/main/kotlin/com/typewritermc/core/serialization/Serializers.kt (1 hunks)
  • engine/engine-core/src/main/kotlin/com/typewritermc/core/utils/Casting.kt (2 hunks)
  • engine/engine-core/src/main/kotlin/com/typewritermc/core/utils/Extensions.kt (2 hunks)
  • engine/engine-core/src/main/kotlin/com/typewritermc/core/utils/Generic.kt (1 hunks)
  • engine/engine-core/src/main/kotlin/com/typewritermc/core/utils/RuntimeTypeAdapterFactory.kt (0 hunks)
  • engine/engine-loader/src/main/kotlin/com/typewritermc/loader/ExtensionLoader.kt (9 hunks)
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/TypewriterPaperPlugin.kt (3 hunks)
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/entry/entries/VariableEntry.kt (5 hunks)
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/ClosedRangeSerializer.kt (1 hunks)
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/ColorSerializer.kt (1 hunks)
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/CoordinateSerializer.kt (1 hunks)
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/CronExpressionSerializer.kt (1 hunks)
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/DurationSerializer.kt (1 hunks)
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/EntryInteractionContextKeySerializer.kt (1 hunks)
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/EntryReferenceSerializer.kt (1 hunks)
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/GenericSerializer.kt (1 hunks)
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/OptionalSerializer.kt (1 hunks)
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/PositionSerializer.kt (1 hunks)
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/PotionEffectTypeSerializer.kt (1 hunks)
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/SkinPropertySerializer.kt (1 hunks)
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/SoundIdSerializer.kt (1 hunks)
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/SoundSourceSerializer.kt (1 hunks)
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/VarSerializer.kt (1 hunks)
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/VectorSerializer.kt (1 hunks)
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/WorldSerializer.kt (1 hunks)
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/utils/BukkitDataParser.kt (2 hunks)
  • engine/gradle/wrapper/gradle-wrapper.properties (1 hunks)
  • engine/gradlew (4 hunks)
  • engine/gradlew.bat (2 hunks)
  • engine/settings.gradle.kts (2 hunks)
💤 Files with no reviewable changes (2)
  • engine/engine-core/src/main/kotlin/com/typewritermc/core/serialization/AlgebraicSerialization.kt
  • engine/engine-core/src/main/kotlin/com/typewritermc/core/utils/RuntimeTypeAdapterFactory.kt
🧰 Additional context used
🧬 Code Graph Analysis (6)
engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/ColorSerializer.kt (1)
engine/engine-core/src/main/kotlin/com/typewritermc/core/serialization/Serializers.kt (10)
  • serializer (55-72)
  • serializer (75-94)
  • serializer (96-117)
  • serializer (119-142)
  • serializer (144-169)
  • serializer (171-198)
  • serializer (200-229)
  • serializer (231-262)
  • serializer (264-297)
  • serializer (299-334)
engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/PotionEffectTypeSerializer.kt (1)
engine/engine-core/src/main/kotlin/com/typewritermc/core/serialization/Serializers.kt (10)
  • serializer (55-72)
  • serializer (75-94)
  • serializer (96-117)
  • serializer (119-142)
  • serializer (144-169)
  • serializer (171-198)
  • serializer (200-229)
  • serializer (231-262)
  • serializer (264-297)
  • serializer (299-334)
engine/engine-core/src/main/kotlin/com/typewritermc/core/utils/Casting.kt (1)
engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/GenericSerializer.kt (1)
  • Generic (7-7)
engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/utils/BukkitDataParser.kt (1)
engine/engine-core/src/main/kotlin/com/typewritermc/core/serialization/Serializers.kt (10)
  • serializer (55-72)
  • serializer (75-94)
  • serializer (96-117)
  • serializer (119-142)
  • serializer (144-169)
  • serializer (171-198)
  • serializer (200-229)
  • serializer (231-262)
  • serializer (264-297)
  • serializer (299-334)
engine/engine-loader/src/main/kotlin/com/typewritermc/loader/ExtensionLoader.kt (1)
engine/engine-core/src/main/kotlin/com/typewritermc/core/serialization/DataSerializer.kt (1)
  • add (37-39)
engine/engine-core/src/main/kotlin/com/typewritermc/core/utils/Generic.kt (4)
engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/GenericSerializer.kt (1)
  • Generic (7-7)
engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/entry/entries/VariableEntry.kt (9)
  • get (22-25)
  • get (24-24)
  • get (54-57)
  • get (56-56)
  • get (60-67)
  • get (70-70)
  • get (83-86)
  • get (98-100)
  • get (112-114)
extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/variables/RandomVariableEntry.kt (1)
  • get (25-37)
extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/variables/InteractionContextVariable.kt (1)
  • get (32-45)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (40)
engine/gradlew.bat (3)

16-17: Good addition of SPDX license identifier.

Adding the SPDX license identifier improves license compliance and makes the license information machine-readable.


73-73: CLASSPATH clearing is a good practice.

Explicitly clearing the CLASSPATH environment variable before execution prevents potential conflicts from pre-existing classpath settings.


77-77: Java invocation method update looks correct.

The change from classpath-based to jar-based invocation (-jar flag) is a standard improvement that simplifies the Gradle wrapper startup process.

engine/dependencies.toml (1)

27-28: Good use of bundles for related dependencies.

Creating a bundle for Kotest libraries is a good practice that simplifies dependency management when multiple related libraries are used together.

engine/settings.gradle.kts (2)

1-7: Excellent implementation of version catalog management.

The dependencyResolutionManagement block is properly configured to use the centralized dependencies.toml file. This setup will provide consistent dependency management across the project.


24-24: New module inclusion looks appropriate.

The addition of engine-loader module is consistent with the project's modular structure.

engine/gradle/wrapper/gradle-wrapper.properties (1)

3-3: Verify Gradle wrapper downgrade & distribution change

The wrapper has been changed from 8.14.2-bin to 8.10-all (–4 minor versions, +≈60 MB archive). Please double-check:

• engine/gradle/wrapper/gradle-wrapper.properties – confirm this is the intended distribution
• build.gradle.kts – ensure all plugins (configuration-cache, Kotlin DSL 1.9+, KMP, etc.) compile under Gradle 8.10
• version catalog (e.g. gradle/libs.versions.toml) – verify that any cataloged plugins or dependency constraints remain compatible
• CI impact – validate download times and disk usage with the larger *-all bundle

If you don’t need the extra sources/tests in *-all, prefer the lighter ‑bin distro to keep CI download times and storage low.

engine/engine-core/src/main/kotlin/com/typewritermc/core/utils/Casting.kt (1)

82-83: LGTM! Clean migration from Gson to Kotlinx Serialization.

The function correctly retrieves the Json instance from Koin and uses encodeToJsonElement to convert the value to a JsonElement for the Generic constructor.

engine/engine-core/src/main/kotlin/com/typewritermc/core/utils/Extensions.kt (1)

9-24: LGTM! Solid camelCase to snake_case conversion.

The implementation correctly handles the conversion logic for different character types. The function properly converts camelCase identifiers to snake_case format as needed for the serialization migration.

engine/engine-core/src/main/kotlin/com/typewritermc/core/interaction/Interaction.kt (1)

63-63: LGTM! Proper use of contextual serialization.

The @Contextual annotation is correctly applied to the key property, enabling the serialization framework to use registered contextual serializers for the EntryContextKey type.

engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/TypewriterPaperPlugin.kt (1)

128-129: LGTM! Proper migration to Kotlinx Serialization DI setup.

The factory definitions correctly replace Gson with Kotlinx Serialization Json instances. The dataSerializer factory appropriately composes a serialization module from all registered serializers, and the bukkitDataParser uses the Bukkit-specific JSON format.

engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/SoundIdSerializer.kt (1)

14-14: Clean migration to Kotlinx Serialization

The delegated implementation correctly uses the generated serializer from @Serializable annotation on SoundId.

engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/VectorSerializer.kt (1)

13-19: Well-structured serializer using property accessors

The implementation correctly uses the generic serializer factory function with property accessors for each Vector component. This is a clean and maintainable approach.

engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/ColorSerializer.kt (1)

14-15: Elegant use of xmap for type conversion

The implementation correctly uses xmap to convert between Int and Color types, providing a clean bidirectional mapping.

engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/CoordinateSerializer.kt (1)

9-16: Well-structured declarative serializer implementation.

The migration to Kotlinx Serialization is well done. Using nullable serializers with default values ensures backward compatibility and robustness when handling incomplete coordinate data.

engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/SoundSourceSerializer.kt (1)

10-10: LGTM: Clean import addition for Kotlinx Serialization.

The import of toDataSerializer extension function supports the migration to delegated serialization.

engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/CronExpressionSerializer.kt (2)

5-7: LGTM: Proper imports for Kotlinx Serialization utilities.

The imports for toDataSerializer, xmap, and serializer are correct for the delegated implementation pattern.


9-12: Approve CronExpressionSerializer Implementation

Excellent use of xmap for type transformation. The migration to use String.serializer().xmap() is well-implemented:

  • { expression } cleanly extracts the string representation from CronExpression
  • { CronExpression.createDynamic(this) } correctly invokes the companion object factory
  • Declaring CronExpressionSerializer as an object is appropriate since no state is maintained

This pattern is much cleaner and more maintainable than manual Gson serialization.

engine/gradlew (5)

18-19: LGTM: Added SPDX license identifier.

The addition of the SPDX license identifier improves license compliance and follows modern best practices.


89-89: LGTM: Improved APP_HOME resolution method.

The change from pwd -P to cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" provides better compatibility across different shell environments and handles edge cases more robustly.


117-117: LGTM: Cleared CLASSPATH variable.

Setting CLASSPATH to an escaped empty string is appropriate when switching to -jar invocation method.


208-208: LGTM: Minor comment improvement.

The comment refinement improves clarity.


216-216: LGTM: Switched to jar invocation method.

The change from -classpath with main class to -jar with the gradle-wrapper.jar is a standard Gradle wrapper update pattern and is correct.

engine/build.gradle.kts (4)

3-3: LGTM: Migrated to version catalog for Kotlin JVM plugin.

Using libs.versions.kotlin.get() centralizes version management and improves maintainability.


6-7: LGTM: Proper version catalog usage for plugins.

The use of alias(libs.plugins.shadow) and alias(libs.plugins.kotlin.serialization) with version catalog references is correct and follows Gradle best practices.


34-35: LGTM: Consistent plugin application in subprojects.

Using rootProject.libs.plugins.*.get().pluginId ensures consistent plugin versions across all subprojects.


43-54: Version Catalog Verification: All dependencies present

I’ve confirmed that engine/dependencies.toml defines aliases for every dependency used in engine/build.gradle.kts (koin, kotlin-serialization, kotlinx-coroutines, mockk, and the kotest bundle). The migration to centralized version catalog references is complete—no further changes needed.

engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/entry/entries/VariableEntry.kt (6)

11-13: LGTM: Proper imports for Kotlinx Serialization migration.

The imports for @Serializable, Json, and serializer are correct for the migration from Gson to Kotlinx Serialization.


33-33: LGTM: Dependency injection updated for Kotlinx Serialization.

The change from gson: Gson to json: Json with the named qualifier "dataSerializer" is consistent with the broader migration pattern.


36-36: LGTM: Proper API migration to Kotlinx Serialization.

The change from gson.fromJson to json.decodeFromJsonElement(json.serializersModule.serializer(klass), data.data) is correct for Kotlinx Serialization. The use of serializersModule.serializer(klass) dynamically obtains the appropriate serializer at runtime.


54-54: LGTM: Added @serializable to sealed interface.

Adding @Serializable to the Var<T> sealed interface enables automatic serialization for all implementations.


77-77: LGTM: Added @serializable to BackedVar class.

The @Serializable annotation on BackedVar is correct and necessary for the Kotlinx Serialization migration.


93-93: LGTM: Added @serializable to MappedVar class.

The @Serializable annotation on MappedVar is correct and necessary for the Kotlinx Serialization migration.

engine/engine-core/src/main/kotlin/com/typewritermc/core/serialization/DataSerializer.kt (1)

1-53: Well-structured migration to Kotlinx Serialization!

The refactored DataSerializer interface and utility functions provide a clean abstraction over Kotlinx Serialization. The xmap function is particularly elegant for bidirectional type mapping.

engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/utils/BukkitDataParser.kt (2)

35-38: LGTM!

Clean implementation using Bukkit's built-in serialization methods with Base64 encoding.


40-44: LGTM!

Proper UUID serialization using most and least significant bits.

engine/engine-core/src/main/kotlin/com/typewritermc/core/serialization/Serializers.kt (1)

12-53: Well-implemented core serialization logic.

The function properly handles encoding/decoding with comprehensive null-safety checks for required fields. The unchecked cast is unavoidable due to type erasure and is handled safely.

engine/engine-loader/src/main/kotlin/com/typewritermc/loader/ExtensionLoader.kt (3)

115-122: LGTM!

The migration to Kotlinx Serialization's buildJsonArray is implemented correctly.


195-319: Properly annotated for Kotlinx Serialization.

All data classes and enums are correctly annotated with @Serializable and @SerialName following Kotlinx Serialization conventions.


28-29: No functional issues with switching extensions to Set

  • Extension is declared as a Kotlin data class, so equals()/hashCode() are correctly generated.
  • No code relies on positional access (first(), elementAt(), indexOf(), etc.).
  • Iteration-based lookups (e.g. firstNotNullOfOrNull) still work as expected: the map’s keySet preserves insertion order.

Comment on lines +2 to +11
kotlin = "2.0.21"

kotlinSerialization = "1.8.1"
coroutines = "1.10.2"
koin = "4.0.4"

kotest = "5.9.1"

shadow = "8.1.7"
mockk = "1.13.16"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify that dependency versions are current and secure.

The version catalog looks well-structured, but please verify that all specified versions are the latest stable releases and free from known security vulnerabilities.


🌐 Web query:

Are the following library versions current and secure: Kotlin 2.0.21, Kotlinx Serialization 1.8.1, Kotlinx Coroutines 1.10.2, Koin 4.0.4, Kotest 5.9.1, Shadow Plugin 8.1.7, and Mockk 1.13.16?

💡 Result:

Here is a review of the listed library versions as of July 11, 2025, addressing whether they are current and considered secure:

Kotlin 2.0.21

  • Not current or supported. The most recent stable version is 2.2.0 (released June 23, 2025), and the 2.0 branch's support ended November 27, 2024. Using 2.0.21 means missing out on active security and bug fixes[4][7].
  • Recommendation: Upgrade to at least 2.2.0 for support and security[4][7].

Kotlinx Serialization 1.8.1

  • Not current. The library had a release with 1.8.x as of June 27, 2025, but it references 1.8.0-RC as a base; the latest stable version may be 1.8.1 or a minor increment, but check for a possible 1.8.2 or higher given recent activity[2].
  • Recommendation: Use at least 1.8.x, but verify if a newer patch is available for the best support[2].

Kotlinx Coroutines 1.10.2

  • Insufficient information in the search results to confirm if 1.10.2 is the latest. Coroutines frequently update, especially to track changes in Kotlin itself. Official documentation or the project's release page should be checked to confirm if a newer 1.10.x or 1.11.x is available.

Koin 4.0.4

  • No coverage in search results for version recency. Historically, major Koin upgrades have followed Kotlin’s breaking changes. Koin 4.0.4 is likely outdated, as Koin 3.x was already available in 2023, with Koin 4 having appeared since then.
  • Recommendation: Check the official Koin release page to confirm, but an upgrade is highly likely needed.

Kotest 5.9.1

  • Current version. Kotest 5.9.1 is listed as the latest stable release as of June 5, 2024, with only alpha (6.0.0.Mx) versions being newer, so 5.9.1 is current and supported[6].
  • Secure and recommended for production use.

Shadow Plugin 8.1.7

  • No direct confirmation from search results. Shadow Plugin 8.x was the latest major version as of 2024. Check the plugin's release repository for any 8.1.x patch releases beyond 8.1.7.
  • Recommendation: Ensure you are at least on the latest 8.1.x for bug and security fixes.

Mockk 1.13.16

  • No release data in results. As of 2024, Mockk 1.13.x was current. Recommend confirming on the official repository, but 1.13.16 is likely recent and acceptable.

Summary Table

Library Your Version Latest/Supported (as of July 2025) Status Recommendation
Kotlin 2.0.21 2.2.0 Outdated Upgrade required
Kotlinx Serialization 1.8.1 1.8.x (possibly newer) Likely current Confirm latest
Kotlinx Coroutines 1.10.2 Unknown Uncertain Check repository
Koin 4.0.4 Unknown, likely higher Outdated Upgrade likely
Kotest 5.9.1 5.9.1 Current OK
Shadow Plugin 8.1.7 Possibly 8.1.7 or higher Likely current Confirm latest
Mockk 1.13.16 Possibly latest Likely current Confirm latest

Security and Support Notes

  • Kotlin language versions only get fixes on the latest major release[4], so running 2.0.21 leaves your project unsupported and potentially vulnerable.
  • For all libraries, regularly check their repositories for new releases and changelogs, especially as they may be closely tied to Kotlin version compatibility.

Action Items

  • Upgrade Kotlin to 2.2.0.
  • Review and update other dependencies as needed, prioritizing Koin and any library with insufficient version information.
  • For project security and maintainability, always align your Kotlin and major dependencies with supported, latest releases.

Citations:


Upgrade Outdated Dependencies in engine/dependencies.toml

A number of your catalog versions are outdated or unverified. Please update or confirm the latest stable releases:

• File: engine/dependencies.toml (lines 2–11)
• Action: bump versions or verify latest patches

  • Kotlin

    • Current: 2.0.21
    • Upgrade to ≥ 2.2.0 (2.0.x is unsupported since Nov 2024; 2.2.0 adds critical fixes)
    • Diff example:
      - kotlin = "2.0.21"
      + kotlin = "2.2.0"
  • Kotlinx Serialization

    • Current: 1.8.1
    • Verify if a newer 1.8.x patch exists; update to the highest stable 1.8.* release
  • Kotlinx Coroutines

    • Current: 1.10.2
    • Check the official repo for any 1.10.x or 1.11.x updates and bump accordingly
  • Koin

    • Current: 4.0.4
    • Likely outdated—upgrade to the latest 4.x release (consult Koin’s release page)
  • Kotest

    • Current: 5.9.1
    • ✅ Already on latest stable
  • Shadow Plugin

    • Current: 8.1.7
    • Confirm no newer 8.1.x patch; update if available
  • Mockk

    • Current: 1.13.16
    • Confirm latest 1.13.x patch and update if a newer release exists

Keeping these aligned with supported, secure versions will ensure you receive bug fixes and security patches.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
kotlin = "2.0.21"
kotlinSerialization = "1.8.1"
coroutines = "1.10.2"
koin = "4.0.4"
kotest = "5.9.1"
shadow = "8.1.7"
mockk = "1.13.16"
kotlin = "2.2.0"
kotlinSerialization = "1.8.1"
coroutines = "1.10.2"
koin = "4.0.4"
kotest = "5.9.1"
shadow = "8.1.7"
mockk = "1.13.16"
🤖 Prompt for AI Agents
In engine/dependencies.toml lines 2 to 11, several dependency versions are
outdated or unverified. Update Kotlin to version 2.2.0 or higher, verify and
bump Kotlinx Serialization to the latest 1.8.x patch, check and update Kotlinx
Coroutines to the latest 1.10.x or 1.11.x release, upgrade Koin to the latest
4.x version, confirm and update Shadow plugin to the newest 8.1.x patch if
available, and verify Mockk is at the latest 1.13.x patch, updating if
necessary. Leave Kotest at 5.9.1 as it is already the latest stable.

Comment on lines 12 to 14
class OptionalSerializer : DataSerializer<Optional<*>> {
override val type: Type = Optional::class.java
override val clazz: Type = Optional::class.java

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Inconsistent use of Type vs Class<*> for clazz

Other serializers expose clazz: Class<*>; here it is Type.
This breaks the common reflection path (isAssignableFrom, java) and may fail Serializers.register() look-ups.

- override val clazz: Type = Optional::class.java
+ override val clazz: Class<*> = Optional::class.java
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
class OptionalSerializer : DataSerializer<Optional<*>> {
override val type: Type = Optional::class.java
override val clazz: Type = Optional::class.java
class OptionalSerializer : DataSerializer<Optional<*>> {
override val clazz: Class<*> = Optional::class.java
// … rest of implementation …
}
🤖 Prompt for AI Agents
In
engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/OptionalSerializer.kt
around lines 12 to 14, the property clazz is declared as Type but should be
Class<*> to maintain consistency with other serializers. Change the type of
clazz from Type to Class<*> and assign it Optional::class.java to ensure
compatibility with reflection methods like isAssignableFrom and proper
functioning of Serializers.register() look-ups.

Comment on lines +3 to 17
import kotlinx.serialization.SerialName

enum class PageType(val id: String) {
@SerializedName("sequence")
@SerialName("sequence")
SEQUENCE("sequence"),

@SerializedName("static")
@SerialName("static")
STATIC("static"),

@SerializedName("cinematic")
@SerialName("cinematic")
CINEMATIC("cinematic"),

@SerializedName("manifest")
@SerialName("manifest")
MANIFEST("manifest"),
;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Missing @Serializable – compilation will fail

@SerialName requires the enum itself to be annotated with @Serializable.
Without it, the Kotlinx compiler plugin reports:

SerialName annotation is meaningless on an enum without @Serializable

Fix:

-import kotlinx.serialization.SerialName
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
 ...
- enum class PageType(val id: String) {
+ @Serializable
+ enum class PageType(val id: String) {
🤖 Prompt for AI Agents
In
engine/engine-core/src/main/kotlin/com/typewritermc/core/books/pages/PageType.kt
lines 3 to 17, the enum PageType uses @SerialName annotations but is missing the
@Serializable annotation on the enum itself. To fix this, add the @Serializable
annotation above the enum declaration to ensure proper serialization and avoid
compilation errors.

Comment on lines +17 to +21
@Serializable
class Ref<E : Entry>(
val id: String,
private val klass: KClass<E>,
private var cache: E? = null,
@Transient private var cache: E? = null,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify KClass serialization handling.

The @Serializable annotation looks correct, and marking cache as @Transient is appropriate since it's a runtime optimization. However, the klass property of type KClass<E> is not serializable by default in Kotlinx Serialization. This could cause runtime serialization failures.

Please verify that the KClass property is properly handled by running the following script to check if there's a custom serializer for this class:


🏁 Script executed:

#!/bin/bash
# Description: Check for custom serializers for Ref class and KClass handling

# Search for custom serializers for Ref class
echo "=== Searching for Ref serializers ==="
rg -A 10 -B 2 "class.*Ref.*Serializer"

# Search for KClass serialization handling
echo "=== Searching for KClass serialization ==="
rg -A 5 -B 2 "KClass.*serializer"

# Check if there are any contextual serializers for KClass
echo "=== Searching for contextual KClass serializers ==="
rg -A 5 -B 2 "@Contextual.*KClass|KClass.*@Contextual"

Length of output: 2378


Add a serializer for the KClass property in Ref

The klass: KClass<E> constructor property isn’t serializable by default in Kotlinx Serialization—this will lead to runtime failures when (de)serializing Ref. You’ll need to either:

  • Annotate klass with @Contextual and register the KClass serializer in your SerializersModule, or
  • Provide a custom serializer via @Serializable(with = YourKClassSerializer::class)

Suggested change in engine/engine-core/src/main/kotlin/com/typewritermc/core/entries/EntryReference.kt:

 import kotlinx.serialization.Serializable
+import kotlinx.serialization.Contextual

 @Serializable
 class Ref<E : Entry>(
     val id: String,
-    private val klass: KClass<E>,
+    @Contextual
+    private val klass: KClass<E>,
     @Transient private var cache: E? = null,
 )

• Don’t forget to add in your module setup:

SerializersModule {
    contextual(KClass::class, KClassSerializer)
}
🤖 Prompt for AI Agents
In
engine/engine-core/src/main/kotlin/com/typewritermc/core/entries/EntryReference.kt
around lines 17 to 21, the klass property of type KClass<E> is not serializable
by default, causing runtime serialization errors. Fix this by annotating klass
with @Contextual and ensure that the KClass serializer is registered in your
SerializersModule setup with contextual(KClass::class, KClassSerializer).
Alternatively, implement and use a custom serializer for KClass and annotate
klass with @Serializable(with = YourKClassSerializer::class).

return World(bukkitWorld.uid.toString())
}
}
class WorldSerializer : DataSerializer<World> by World.serializer().toDataSerializer()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify World deserialization behavior

The previous implementation handled both UUID and world name lookups with fallback logic and error logging. Ensure that World.serializer() provides equivalent functionality for resolving world references.


🏁 Script executed:

#!/bin/bash
# Description: Check the World class implementation to verify serialization behavior

# Find the World class definition
fd -e kt "World.kt" | xargs rg -A 20 "^@Serializable.*class World"

# Check if there's a custom serializer defined for World
ast-grep --pattern '@Serializable($_)
class World {
  $$$
}'

Length of output: 129


Continuing verification by inspecting the serializer adapter and WorldSerializer implementation:


🏁 Script executed:

#!/usr/bin/env bash
set -e

# 1. Locate and display the WorldSerializer file
FILE=$(fd -e kt WorldSerializer.kt | head -n1)
echo "=== $FILE ==="
sed -n '1,200p' "$FILE"

# 2. Find the toDataSerializer extension that bridges KSerializer → DataSerializer
rg -n "fun .*toDataSerializer" -A5 -B5 || true

# 3. Locate the DataSerializer interface to see its read/write contract
rg -n "interface DataSerializer" -A5 -B5 || true

# 4. Search for any custom KSerializer<World> definitions
rg -n "KSerializer<World>" -A5 -B5 || true

# 5. Look for an extension or factory named serializer on World
rg -n "World\\.serializer" -A5 -B5 || true

Length of output: 5832


Continuing verification by inspecting the core World definition for custom serialization logic:


🏁 Script executed:

#!/usr/bin/env bash
set -e

# 1. Locate the World.kt file
FILE=$(fd -e kt World.kt | grep 'core/utils/point/World.kt' | head -n1)
echo "=== $FILE ==="
sed -n '1,200p' "$FILE"

# 2. Check for @Serializable on World
echo; echo "=== @Serializable lines ==="
rg -n '@Serializable' -A2 -B2 "$FILE" || true

# 3. Look for custom serializer implementations in World
echo; echo "=== Companion object & serializer hints ==="
rg -n 'companion object' -A5 -B5 "$FILE" || true
rg -n 'object Serializer' -A5 -B5 "$FILE" || true

Length of output: 1287


Restore custom World deserialization logic

The current WorldSerializer simply delegates to the default World.serializer(), which only reads/writes the identifier property and drops the previous UUID-and-name lookup plus error logging. You need to reintroduce a custom serializer that:

  • Writes the world as a single string (identifier).
  • On read, attempts UUID.fromString(id)server().getWorld(uuid), falls back to server().getWorld(name), and logs errors via logErrorIfNull.

Locations to update:

  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/WorldSerializer.kt

Suggested diff:

- class WorldSerializer : DataSerializer<World> by World.serializer().toDataSerializer()
+ class WorldSerializer : DataSerializer<World> {
+     override val clazz = World::class
+     override val descriptor = PrimitiveSerialDescriptor("World", PrimitiveKind.STRING)
+
+     override fun serialize(encoder: Encoder, value: World) {
+         encoder.encodeString(value.identifier)
+     }
+
+     override fun deserialize(decoder: Decoder): World {
+         val idOrName = decoder.decodeString()
+         val world = runCatching { UUID.fromString(idOrName) }
+             .mapCatching { server().getWorld(it) }
+             .getOrNull()
+             ?: server().getWorld(idOrName)
+         return logErrorIfNull(world, "World not found: $idOrName")
+     }
+ }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
class WorldSerializer : DataSerializer<World> by World.serializer().toDataSerializer()
class WorldSerializer : DataSerializer<World> {
override val clazz = World::class
override val descriptor = PrimitiveSerialDescriptor("World", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: World) {
encoder.encodeString(value.identifier)
}
override fun deserialize(decoder: Decoder): World {
val idOrName = decoder.decodeString()
val world = runCatching { UUID.fromString(idOrName) }
.mapCatching { server().getWorld(it) }
.getOrNull()
?: server().getWorld(idOrName)
return logErrorIfNull(world, "World not found: $idOrName")
}
}
🤖 Prompt for AI Agents
In
engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/WorldSerializer.kt
at line 15, replace the current delegation to World.serializer() with a custom
serializer implementation. This serializer should write the world as a single
string using its identifier, and on reading, it should attempt to parse the
string as a UUID to retrieve the world via server().getWorld(uuid). If that
fails, it should fall back to server().getWorld(name) using the string as a
name. Ensure to log errors using logErrorIfNull when the world cannot be found.
This restores the previous custom deserialization logic that includes UUID and
name lookup with error logging.

Comment on lines +96 to 99
// Might work.
val serial = jsonFormat.serializersModule.serializer(clazz)
val entry = jsonFormat.decodeFromJsonElement(serial, obj) as Entry
entryValidation(entry, pageName, blueprintId)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Address uncertainty in serialization approach.

The comment "Might work" suggests uncertainty about the serialization approach. The unchecked cast to Entry could fail at runtime.

Consider adding explicit type checking:

-            // Might work.
             val serial = jsonFormat.serializersModule.serializer(clazz)
-            val entry = jsonFormat.decodeFromJsonElement(serial, obj) as Entry
+            val decoded = jsonFormat.decodeFromJsonElement(serial, obj)
+            val entry = decoded as? Entry 
+                ?: throw IllegalArgumentException("Decoded object is not an Entry: ${decoded::class.simpleName}")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Might work.
val serial = jsonFormat.serializersModule.serializer(clazz)
val entry = jsonFormat.decodeFromJsonElement(serial, obj) as Entry
entryValidation(entry, pageName, blueprintId)
val serial = jsonFormat.serializersModule.serializer(clazz)
val decoded = jsonFormat.decodeFromJsonElement(serial, obj)
val entry = decoded as? Entry
?: throw IllegalArgumentException("Decoded object is not an Entry: ${decoded::class.simpleName}")
entryValidation(entry, pageName, blueprintId)
🤖 Prompt for AI Agents
In engine/engine-core/src/main/kotlin/com/typewritermc/core/entries/Library.kt
around lines 96 to 99, the current serialization approach uses an unchecked cast
to Entry which may cause runtime failures. Replace the unchecked cast by adding
explicit type checking after deserialization to ensure the object is indeed an
Entry instance before proceeding. If the type does not match, handle the error
gracefully, such as by throwing a clear exception or logging an error, to avoid
unexpected crashes.

Comment on lines +38 to 39
return jsonFormat.decodeFromJsonElement(jsonFormat.serializersModule.serializer(klass),data) as T?
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add null-safe handling for deserialization.

The unchecked casts to T? could throw ClassCastException if the deserialization produces an incompatible type.

     fun <T> get(klass: Class<T>): T? {
-        return jsonFormat.decodeFromJsonElement(jsonFormat.serializersModule.serializer(klass),data) as T?
+        return try {
+            jsonFormat.decodeFromJsonElement(jsonFormat.serializersModule.serializer(klass), data) as? T
+        } catch (e: Exception) {
+            null
+        }
     }

     fun <T : Any> get(klass: KClass<T>): T? {
-        return jsonFormat.decodeFromJsonElement(jsonFormat.serializersModule.serializer(klass.starProjectedType),data) as T?
+        return try {
+            jsonFormat.decodeFromJsonElement(jsonFormat.serializersModule.serializer(klass.starProjectedType), data) as? T
+        } catch (e: Exception) {
+            null
+        }
     }

Also applies to: 42-43

🤖 Prompt for AI Agents
In engine/engine-core/src/main/kotlin/com/typewritermc/core/utils/Generic.kt
around lines 38-39 and 42-43, the deserialization uses unchecked casts to T?
which can cause ClassCastException if the result type is incompatible. Modify
the code to safely handle nulls and type mismatches by checking the deserialized
object's type before casting or using safe cast operators, ensuring the method
returns null or a valid instance without throwing exceptions.

Comment on lines +31 to +33
fun createBukkitDataParser(): Gson = GsonBuilder()
.create()

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Function no longer provides Bukkit data parsing functionality.

The function name createBukkitDataParser() is misleading as it now returns a plain Gson instance without any Bukkit-specific serialization configuration. Consider either removing this function or updating its name/documentation to reflect its current behavior.

🤖 Prompt for AI Agents
In
engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/utils/BukkitDataParser.kt
around lines 31 to 33, the function createBukkitDataParser() returns a plain
Gson instance without any Bukkit-specific configuration, making its name
misleading. To fix this, either rename the function to something like
createGson() to reflect its generic behavior or update the function to include
Bukkit-specific serialization setup if that functionality is still needed.
Alternatively, remove the function if it is no longer used or necessary.

Comment on lines 46 to 62
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Potential IndexOutOfBoundsException in world fallback logic.

The fallback to Bukkit.getWorlds()[0] could throw an exception if no worlds are loaded (e.g., during testing or server shutdown).

Apply this diff to add safer fallback logic:

-        if (worldObj == null) {
-            logger.warning("Failed to find world '$world' while deserializing location '$this'")
-            Location(Bukkit.getWorlds()[0], x, y, z, yaw ?: 0f, pitch ?: 0f)
-        } else {
-            Location(worldObj, x, y, z, yaw ?: 0f, pitch ?: 0f)
-        }
+        if (worldObj == null) {
+            logger.warning("Failed to find world '$world' while deserializing location")
+            val fallbackWorld = Bukkit.getWorlds().firstOrNull()
+            if (fallbackWorld == null) {
+                throw IllegalStateException("No worlds available for location deserialization")
+            }
+            Location(fallbackWorld, x, y, z, yaw ?: 0f, pitch ?: 0f)
+        } else {
+            Location(worldObj, x, y, z, yaw ?: 0f, pitch ?: 0f)
+        }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
object LocationSerializer : KSerializer<Location> by serializer(
"x", { x }, Double.serializer(),
"y", { y }, Double.serializer(),
"z", { z }, Double.serializer(),
"yaw", { yaw }, Float.serializer().nullable,
"pitch", { pitch }, Float.serializer().nullable,
"world", { world.uuid }, UUIDSerializer,
{ x, y, z, yaw, pitch, world ->
val worldObj = Bukkit.getWorld(world)
if (worldObj == null) {
logger.warning("Failed to find world '$world' while deserializing location '$this'")
Location(Bukkit.getWorlds()[0], x, y, z, yaw ?: 0f, pitch ?: 0f)
} else {
Location(worldObj, x, y, z, yaw ?: 0f, pitch ?: 0f)
}
}
}
)
object LocationSerializer : KSerializer<Location> by serializer(
"x", { x }, Double.serializer(),
"y", { y }, Double.serializer(),
"z", { z }, Double.serializer(),
"yaw", { yaw }, Float.serializer().nullable,
"pitch", { pitch }, Float.serializer().nullable,
"world", { world.uuid }, UUIDSerializer,
{ x, y, z, yaw, pitch, world ->
val worldObj = Bukkit.getWorld(world)
if (worldObj == null) {
logger.warning("Failed to find world '$world' while deserializing location")
val fallbackWorld = Bukkit.getWorlds().firstOrNull()
if (fallbackWorld == null) {
throw IllegalStateException("No worlds available for location deserialization")
}
Location(fallbackWorld, x, y, z, yaw ?: 0f, pitch ?: 0f)
} else {
Location(worldObj, x, y, z, yaw ?: 0f, pitch ?: 0f)
}
}
)
🤖 Prompt for AI Agents
In
engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/utils/BukkitDataParser.kt
between lines 46 and 62, the fallback logic uses Bukkit.getWorlds()[0] which can
throw an IndexOutOfBoundsException if no worlds are loaded. To fix this, check
if Bukkit.getWorlds() is not empty before accessing the first element; if it is
empty, handle the case safely by either returning null, throwing a controlled
exception, or providing a sensible default to avoid the exception.

Comment on lines 39 to 47
@OptIn(ExperimentalSerializationApi::class)
fun readJsonTexts(json: List<String>): Map<Extension, String> = json.associateBy { extensionJson ->
try {
gson.fromJson(extensionJson, Extension::class.java)
} catch (e: JsonSyntaxException) {
jsonFormat.decodeFromString(jsonFormat.serializersModule.getContextual(Extension::class)!!, extensionJson)
} catch (e: SerializationException) {
logger.severe("Error while loading extension meta: ${e.message}")
null
}
}.filter { it.key != null } as Map<Extension, String>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Potential NullPointerException from non-null assertion.

The use of getContextual(Extension::class)!! will throw NPE if the Extension serializer isn't registered in the serializersModule.

Apply this diff to add proper error handling:

-            jsonFormat.decodeFromString(jsonFormat.serializersModule.getContextual(Extension::class)!!, extensionJson)
+            val serializer = jsonFormat.serializersModule.getContextual(Extension::class)
+                ?: throw IllegalStateException("Extension serializer not found in serializersModule")
+            jsonFormat.decodeFromString(serializer, extensionJson)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@OptIn(ExperimentalSerializationApi::class)
fun readJsonTexts(json: List<String>): Map<Extension, String> = json.associateBy { extensionJson ->
try {
gson.fromJson(extensionJson, Extension::class.java)
} catch (e: JsonSyntaxException) {
jsonFormat.decodeFromString(jsonFormat.serializersModule.getContextual(Extension::class)!!, extensionJson)
} catch (e: SerializationException) {
logger.severe("Error while loading extension meta: ${e.message}")
null
}
}.filter { it.key != null } as Map<Extension, String>
@OptIn(ExperimentalSerializationApi::class)
fun readJsonTexts(json: List<String>): Map<Extension, String> = json.associateBy { extensionJson ->
try {
val serializer = jsonFormat.serializersModule.getContextual(Extension::class)
?: throw IllegalStateException("Extension serializer not found in serializersModule")
jsonFormat.decodeFromString(serializer, extensionJson)
} catch (e: SerializationException) {
logger.severe("Error while loading extension meta: ${e.message}")
null
}
}.filter { it.key != null } as Map<Extension, String>
🤖 Prompt for AI Agents
In
engine/engine-loader/src/main/kotlin/com/typewritermc/loader/ExtensionLoader.kt
around lines 39 to 47, the code uses a non-null assertion (!!) on
getContextual(Extension::class), which can cause a NullPointerException if the
serializer is not registered. To fix this, first retrieve the serializer into a
variable and check if it is null. If null, log an error and skip decoding. Only
proceed with decoding if the serializer is present, thus avoiding the NPE and
adding proper error handling.

Took 11 minutes
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/DataSerializer.kt (2)

34-34: Address the TODO for serialization module management.

The TODO comment indicates a need for better module organization. Consider creating a structured approach for managing serializers, possibly grouping them by domain or type category.

Would you like me to help design a better serialization module structure or open an issue to track this improvement?


83-88: Consider adding validation for cron expressions.

The CronExpression.createDynamic() method may throw exceptions for invalid cron expressions. Consider wrapping the creation in a try-catch to provide more descriptive error messages.

String.serializer().xmap(
    { expression },
-    { CronExpression.createDynamic(this) }
+    { 
+        try {
+            CronExpression.createDynamic(this)
+        } catch (e: Exception) {
+            error("Invalid cron expression '$this': ${e.message}")
+        }
+    }
).toDataSerializer()
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9416d31 and 1d6f429.

📒 Files selected for processing (18)
  • engine/engine-core/src/main/kotlin/com/typewritermc/core/utils/point/Position.kt (1 hunks)
  • engine/engine-core/src/main/kotlin/com/typewritermc/core/utils/point/World.kt (1 hunks)
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/entry/entity/SkinProperty.kt (1 hunks)
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/DataSerializer.kt (1 hunks)
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/ClosedRangeSerializer.kt (0 hunks)
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/ColorSerializer.kt (0 hunks)
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/CoordinateSerializer.kt (0 hunks)
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/CronExpressionSerializer.kt (0 hunks)
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/DurationSerializer.kt (0 hunks)
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/PositionSerializer.kt (0 hunks)
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/PotionEffectTypeSerializer.kt (0 hunks)
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/SkinPropertySerializer.kt (0 hunks)
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/SoundIdSerializer.kt (0 hunks)
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/SoundSourceSerializer.kt (0 hunks)
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/VectorSerializer.kt (0 hunks)
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/WorldSerializer.kt (0 hunks)
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/utils/BukkitDataParser.kt (2 hunks)
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/utils/Sound.kt (4 hunks)
💤 Files with no reviewable changes (12)
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/DurationSerializer.kt
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/VectorSerializer.kt
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/CronExpressionSerializer.kt
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/ClosedRangeSerializer.kt
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/SkinPropertySerializer.kt
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/PotionEffectTypeSerializer.kt
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/WorldSerializer.kt
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/ColorSerializer.kt
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/SoundSourceSerializer.kt
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/PositionSerializer.kt
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/CoordinateSerializer.kt
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/serializers/SoundIdSerializer.kt
✅ Files skipped from review due to trivial changes (4)
  • engine/engine-core/src/main/kotlin/com/typewritermc/core/utils/point/World.kt
  • engine/engine-core/src/main/kotlin/com/typewritermc/core/utils/point/Position.kt
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/utils/Sound.kt
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/entry/entity/SkinProperty.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/utils/BukkitDataParser.kt
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (2)
engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/DataSerializer.kt (2)

65-72: LGTM!

The vector serializer implementation is clean and properly maps the x, y, z fields.


73-82: Well-designed nullable field handling.

The coordinate serializer effectively handles partial data by using nullable fields with sensible defaults. This provides flexibility for scenarios where not all coordinate components are specified.

Comment on lines 47 to 52
single<DataSerializer<*>>(named("potionEffectType")) {
String.serializer().xmap<String, PotionEffectType>(
{ key.asString() },
{ Registry.EFFECT.get(Key.key(this)) ?: error("Unknown potion type '$this'") }
).toDataSerializer()
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add namespace validation for potion effect keys.

The current implementation assumes the input string is in the correct namespaced format. Consider adding validation or using NamespacedKey.fromString() for more robust parsing.

String.serializer().xmap<String, PotionEffectType>(
    { key.asString() },
-    { Registry.EFFECT.get(Key.key(this)) ?: error("Unknown potion type '$this'") }
+    { 
+        val key = try {
+            Key.key(this)
+        } catch (e: Exception) {
+            error("Invalid potion effect key format '$this'")
+        }
+        Registry.EFFECT.get(key) ?: error("Unknown potion type '$this'")
+    }
).toDataSerializer()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
single<DataSerializer<*>>(named("potionEffectType")) {
String.serializer().xmap<String, PotionEffectType>(
{ key.asString() },
{ Registry.EFFECT.get(Key.key(this)) ?: error("Unknown potion type '$this'") }
).toDataSerializer()
}
single<DataSerializer<*>>(named("potionEffectType")) {
String.serializer().xmap<String, PotionEffectType>(
{ key.asString() },
{
val key = try {
Key.key(this)
} catch (e: Exception) {
error("Invalid potion effect key format '$this'")
}
Registry.EFFECT.get(key) ?: error("Unknown potion type '$this'")
}
).toDataSerializer()
}
🤖 Prompt for AI Agents
In
engine/engine-paper/src/main/kotlin/com/typewritermc/engine/paper/loader/DataSerializer.kt
around lines 47 to 52, the code currently converts strings to PotionEffectType
without validating the namespace format of the keys. To fix this, update the
deserialization logic to parse the string keys using NamespacedKey.fromString()
which validates the namespace format, and handle any parsing errors
appropriately before retrieving the effect from the Registry. This ensures only
valid namespaced keys are accepted and improves robustness.

@DevMunky DevMunky marked this pull request as draft July 11, 2025 04:57
@LegendsOfXania
Copy link
Contributor

I have the impression that EntityCinematicArtifact still uses Gson

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants