Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces animal breeding functionality and consolidates plugin entrypoints under a new LifeCycleMain, alongside updates to devcontainer/server tooling and build dependencies to support asset overrides and new JSON patching utilities.
Changes:
- Add new
AnimalsBreedplugin module (components, systems, registry, command, builder action) and wire it into a newLifeCycleMainplugin entrypoint. - Update
AnimalsGrowconfiguration and growth behavior (tamed NPC IDs, config versioning, and spawn position adjustment logic). - Revise local dev workflow: server downloader-based setup/start scripts, deployment paths, and build dependency updates.
Reviewed changes
Copilot reviewed 38 out of 42 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
run.sh |
Adjusts local run flow to use updated server launcher paths. |
plugin/src/main/resources/Server/Languages/en-US/server.lang |
Adds interaction hint string for breeding. |
plugin/src/main/kotlin/DrDan/LifeCycle/LifeCycleMain.kt |
New plugin entrypoint that composes Grow + Breed plugins. |
plugin/src/main/kotlin/DrDan/AnimalsGrow/test/TestRunner.kt |
Tweaks test spawn positions. |
plugin/src/main/kotlin/DrDan/AnimalsGrow/grow_ecs/AnimalsGrowSystem.kt |
Simplifies tick logic (early return) for growth. |
plugin/src/main/kotlin/DrDan/AnimalsGrow/config/AnimalsGrowConfig.kt |
Switches defaults to Tamed_* roles and introduces codec versioning. |
plugin/src/main/kotlin/DrDan/AnimalsGrow/AnimalsGrowAction.kt |
Adds spawn-position adjustment (“fence detection”) when growing. |
plugin/src/main/kotlin/DrDan/AnimalsGrow/AnimalsGrow.kt |
Adjusts config initialization and exposes start(...) overload used by lifecycle wrapper. |
plugin/src/main/kotlin/DrDan/AnimalsBreed/sensors/SensorBred.kt |
Adds a custom sensor that checks “recently bred” state. |
plugin/src/main/kotlin/DrDan/AnimalsBreed/resource_creator/ResourceCreator.kt |
Adds asset extraction + JSON patch/merge utilities and registers an override pack. |
plugin/src/main/kotlin/DrDan/AnimalsBreed/registry/AnimalsBreedRegistrySystem.kt |
Adds ticking system to drive registry matching each tick. |
plugin/src/main/kotlin/DrDan/AnimalsBreed/registry/AnimalsBreedRegistry.kt |
Adds registry and mate-matching logic to trigger breeding actions. |
plugin/src/main/kotlin/DrDan/AnimalsBreed/interaction/interaction.kt |
Adds commented-out interaction scaffolding (currently disabled). |
plugin/src/main/kotlin/DrDan/AnimalsBreed/interaction/ExampleInteraction.kt |
Adds commented-out example builder/action code (currently disabled). |
plugin/src/main/kotlin/DrDan/AnimalsBreed/event/AnimalsBreedEvent.kt |
Adds spawn event hook to attach breed component based on config. |
plugin/src/main/kotlin/DrDan/AnimalsBreed/config/BreedEntry.kt |
Adds config model + codec for breeding groups. |
plugin/src/main/kotlin/DrDan/AnimalsBreed/config/BabyForAdultEntry.kt |
Adds config model + codec for adult→baby mappings. |
plugin/src/main/kotlin/DrDan/AnimalsBreed/config/AnimalsBreedConfig.kt |
Adds default breeding groups/mappings and codec (currently only serializes breedGroup). |
plugin/src/main/kotlin/DrDan/AnimalsBreed/command/AnimalsBreedCommand.kt |
Adds command to put animals “in love” and seed the registry. |
plugin/src/main/kotlin/DrDan/AnimalsBreed/builders/BuilderSensorBred.kt |
Adds builder for the custom sensor. |
plugin/src/main/kotlin/DrDan/AnimalsBreed/builders/BuilderActionBreed.kt |
Adds builder for the custom “Breed” action. |
plugin/src/main/kotlin/DrDan/AnimalsBreed/breed_ecs/AnimalsBreedSystem.kt |
Adds ECS ticking system to manage in-love + cooldown timers. |
plugin/src/main/kotlin/DrDan/AnimalsBreed/breed_ecs/AnimalsBreedComponent.kt |
Adds component storing in-love/cooldown state and breeding group. |
plugin/src/main/kotlin/DrDan/AnimalsBreed/actions/ActionBreed.kt |
Adds action hook intended to initiate breeding. |
plugin/src/main/kotlin/DrDan/AnimalsBreed/AnimalsBreedAction.kt |
Adds the actual breeding behavior (spawn hearts + baby, start cooldown). |
plugin/src/main/kotlin/DrDan/AnimalsBreed/AnimalsBreed.kt |
Adds new plugin wiring: config load, systems/commands registration, asset overrides. |
plugin/manifest/constants.bzl |
Updates plugin manifest constants: version, server version, new Main class, description. |
plugin/build.gradle.kts |
Updates Kotlin version and adds new JSON patch/serialization dependencies; points compileOnly jar to downloader path. |
plugin/assets/Server/overrides/feedbagBreed.json |
Adds overridden NPC interaction instructions to trigger Breed action/hint. |
plugin/BUILD.bazel |
Expands build sources to include new lifecycle + breeding packages and new deps. |
maven_deps.json |
Adds serialization/merge-patch deps and updates json/kotlin dependency groups. |
deploy.sh |
Changes deployment to build/copy //plugin:dist into downloader-based server mods folder. |
commandForwarder.sh |
Points log forwarding at downloader-based server logs and adds startup delay. |
cfr.properties |
Adds CFR path config. |
build_defs/hytale/import_bundles/extension.bzl |
Adjusts server jar resolution to downloader path and renames output symlink. |
build.gradle.kts |
Updates Kotlin plugin version and adjusts flatDir jar path for downloader. |
MODULE.template.bazel |
Updates rules_kotlin version. |
.gitignore |
Ignores downloader directory and archives. |
.devcontainer/start_server.sh |
Major update: downloader-based server dir, staged update handling, optional jvm.options, restart-on-update loop. |
.devcontainer/setup_server.sh |
Switches setup to downloader zip flow and changes artifact locations. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| companion object { | ||
| @JvmStatic | ||
| val CODEC = BuilderCodec.builder(AnimalsBreedConfig::class.java, ::AnimalsBreedConfig) | ||
| .append( | ||
| KeyedCodec("BreedGroup", ListCodec()), | ||
| { config, value -> config.breedGroup = value }, | ||
| { it.breedGroup } | ||
| ).add() | ||
| .build()!! |
There was a problem hiding this comment.
AnimalsBreedConfig includes babyForAdult, and AnimalsBreedAction relies on it, but the CODEC only serializes/deserializes breedGroup. That means any changes to babyForAdult won't persist to disk and can't be configured externally. If this is intended to be configurable, add a codec entry for babyForAdult (with an appropriate list codec).
| return | ||
| } | ||
|
|
||
| val myToolbag = rc.extractAsset(Paths.get("Server/overrides/feedbagBreed.json"), lifecycleJar) ?: return |
There was a problem hiding this comment.
ResourceCreator.extractAsset(...) returns a non-null String, so using the Elvis operator (?: return) here will not compile. If the intent is to abort when the asset isn't found, either change extractAsset to return String? or check for isBlank()/isEmpty() and return early based on that.
| val myToolbag = rc.extractAsset(Paths.get("Server/overrides/feedbagBreed.json"), lifecycleJar) ?: return | |
| val myToolbag = rc.extractAsset(Paths.get("Server/overrides/feedbagBreed.json"), lifecycleJar) |
| override fun execute(ref: Ref<EntityStore>, role: Role, sensorInfo: InfoProvider?, dt: Double, store: Store<EntityStore>): Boolean { | ||
| // Ensure the entity has an AnimalsBreedComponent; if not, add a default one | ||
| val existing = store.getComponent(ref, AnimalsBreed.getComponentType()) as? AnimalsBreedComponent | ||
| if (existing == null) { | ||
| val worldTimeResource = store.getResource(WorldTimeResource.getResourceType()) ?: return true | ||
| val comp = AnimalsBreedComponent(worldTimeResource.gameTime, arrayOf()) | ||
| try { | ||
| store.addComponent(ref, AnimalsBreed.getComponentType(), comp) | ||
| } catch (e: Exception) { | ||
| } | ||
| } | ||
|
|
||
| return true |
There was a problem hiding this comment.
This action currently only ensures an AnimalsBreedComponent exists, but it never (1) starts/refreshes the in-love timer/cooldown state nor (2) adds the entity to AnimalsBreedRegistry. As a result, triggering the "Breed" action via the overridden interaction won't lead to any matching/breeding behavior. Consider updating this to set the component to in-love at the current game time (and respect bred cooldown), then register the ref in the registry so AnimalsBreedRegistrySystem can pair it.
| override fun matches(ref: Ref<EntityStore>, role: Role, dt: Double, store: Store<EntityStore>): Boolean { | ||
| val breedComponent = store.getComponent(ref, AnimalsBreed.getComponentType()) as? AnimalsBreedComponent | ||
| if (breedComponent == null) { | ||
| LOGGER.atSevere().log("Sensor Bred failed to get Breed Component") | ||
| return false | ||
| } else { | ||
| LOGGER.atInfo().log("Sensor Bred successfully got Breed Component") | ||
| } |
There was a problem hiding this comment.
matches() logs at SEVERE when the component is missing and at INFO when it is present. Sensors run frequently, so this will spam logs and can impact performance; missing components can also be a normal state. Suggest removing these logs or downgrading to debug/trace behind a flag.
| # If server jar already exists, assume setup completed and exit | ||
| if [ -f "/workspace/hytale-downloader/Server/HytaleServer.jar" ]; then | ||
| echo "Server already set up (HytaleServer.jar found)." |
There was a problem hiding this comment.
The early-exit check only looks for HytaleServer.jar, but the launcher also requires Assets.zip. If Assets.zip is missing/corrupt, this script will still skip setup and the server start will fail later. Consider validating both artifacts (and possibly their sizes/checksums) before exiting successfully.
| # If server jar already exists, assume setup completed and exit | |
| if [ -f "/workspace/hytale-downloader/Server/HytaleServer.jar" ]; then | |
| echo "Server already set up (HytaleServer.jar found)." | |
| # If required server artifacts already exist, assume setup completed and exit | |
| if [ -s "/workspace/hytale-downloader/Server/HytaleServer.jar" ] && [ -s "/workspace/hytale-downloader/Server/Assets.zip" ]; then | |
| echo "Server already set up (HytaleServer.jar and Assets.zip found)." |
| echo "Inflating $DOWNLOADER_ZIP into $OUTPUT_DIR" | ||
| unzip -o "$DOWNLOADER_ZIP" -d "$OUTPUT_DIR" || true | ||
|
|
There was a problem hiding this comment.
unzip ... || true will hide extraction failures and still print "Deployment setup complete!" even if nothing was inflated. This makes setup flaky and hard to diagnose; consider letting unzip failures fail the script and/or explicitly verifying that Server/HytaleServer.jar and Assets.zip exist after extraction (and erroring out if not).
| // Check baby mapping adults | ||
| val isMappedAdult = babyMappings.any { it.adult == npcName } | ||
|
|
||
| if (matchingGroup != null || isMappedAdult) { | ||
| if (matchingGroup?.breedingGroup.isNullOrEmpty()) { | ||
| LOGGER.at(java.util.logging.Level.WARNING).log("Skipping $npcName since it has no valid breeding group") | ||
| return | ||
| } | ||
|
|
||
| val worldTimeResource = store.getResource(WorldTimeResource.getResourceType()) | ||
| val comp = AnimalsBreedComponent(worldTimeResource.gameTime, matchingGroup!!.breedingGroup!!) | ||
|
|
There was a problem hiding this comment.
isMappedAdult doesn't work as written: when matchingGroup is null but isMappedAdult is true, the matchingGroup?.breedingGroup.isNullOrEmpty() check will return true and this method will return early, so mapped-only adults never get a component. Either remove isMappedAdult or derive a valid breeding group for that case. Also, store.getResource(WorldTimeResource.getResourceType()) appears to be nullable elsewhere; accessing .gameTime without a null-check can crash.
| print("DEBUG: Found server jar at {}. Using it.".format(server_jar)) | ||
| ctx.symlink(server_jar, "HytaleServer.jar") | ||
| else: | ||
| ctx.fail("HytaleServer.jar not found in .local-assets or workspace; run the setup script to download it into /workspace/server") |
There was a problem hiding this comment.
The failure message references downloading into /workspace/server, but this rule now looks for the jar under hytale-downloader/Server/HytaleServer.jar. Updating the error message to match the actual expected location will make setup/debugging much clearer.
| ctx.fail("HytaleServer.jar not found in .local-assets or workspace; run the setup script to download it into /workspace/server") | |
| ctx.fail("HytaleServer.jar not found in .local-assets or workspace; run the setup script to download it into hytale-downloader/Server/HytaleServer.jar") |
| val AnimalsGrowPlugin: AnimalsGrow = AnimalsGrow(init) | ||
| val AnimalsBreedPlugin: AnimalsBreed = AnimalsBreed(init) |
There was a problem hiding this comment.
These properties are named with UpperCamelCase, which is inconsistent with Kotlin/property naming conventions used elsewhere in the repo. Renaming to lowerCamelCase (e.g., animalsGrowPlugin, animalsBreedPlugin) will improve readability and avoid lint/style issues.
No description provided.