Skip to content

Add QuestItemAudienceEntry to enforce quest item retention#408

Closed
RenaudRl wants to merge 14 commits intogabber235:developfrom
RenaudRl:questitemaudience
Closed

Add QuestItemAudienceEntry to enforce quest item retention#408
RenaudRl wants to merge 14 commits intogabber235:developfrom
RenaudRl:questitemaudience

Conversation

@RenaudRl
Copy link
Copy Markdown
Contributor

@RenaudRl RenaudRl commented Nov 29, 2025

Introduces QuestItemAudienceEntry, an audience entry that forces players to keep a specific quest item in their inventory. Prevents dropping, moving to containers, and losing the item on death, ensuring players retain the item throughout the quest.

Summary by CodeRabbit

  • New Features

    • Added Quest Item Audience to ensure players always retain a designated quest item.
    • Automatically grants the quest item on join and restores it on respawn.
    • Handles full inventories by replacing items and notifying players.
    • Removes quest items when players leave and clears state on disposal.
  • Bug Fixes

    • Prevents dropping, crafting-slot moves, container transfers (including shift-click), and death drops of the quest item.

✏️ Tip: You can customize this high-level summary in your review settings.

Introduces QuestItemAudienceEntry, an audience entry that forces players to keep a specific quest item in their inventory. Prevents dropping, moving to containers, and losing the item on death, ensuring players retain the item throughout the quest.
Changed the color for the 'quest_item_audience' entry from GREEN to MEDIUM_SEA_GREEN to improve visual distinction.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Nov 29, 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.

  • 🔍 Trigger a full review

Walkthrough

Adds a QuestItemAudience entry and display that enforce a specific quest item: assign it when a player joins, block drops/moves/crafting shifts/container transfers, remove it from death drops and restore on respawn, and clean up on player leave or dispose.

Changes

Cohort / File(s) Summary
Quest Item Audience Feature
extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
Introduces QuestItemAudienceEntry (public entry with item: Var<Item>) and QuestItemAudienceDisplay (public display implementing AudienceDisplay & Bukkit Listener) that: register listener on initialize; assign quest item on player add (handles full inventories by replacing/dropping items and notifying player); remove quest item on player remove; cancel item drops for the quest item; block crafting-slot/click/container/shift-click moves; strip quest item from death drops and restore on respawn; unregister listener and clear state on dispose.

Sequence Diagram

sequenceDiagram
    actor Player
    participant Audience as QuestItemAudienceDisplay
    participant Inventory as BukkitInventory
    participant Events as BukkitEventSystem

    rect rgb(220,235,255)
    Note over Player,Audience: Player added to audience
    Player->>Audience: onPlayerAdd(player)
    Audience->>Inventory: ensure quest item in inventory (handle full inventory)
    Audience-->>Player: notify if replacement occurred
    end

    rect rgb(255,235,220)
    Note over Player,Events: Interaction prevention
    Player->>Events: drop/click/shift-click/craft attempts
    Events->>Audience: onItemDrop/onInventoryClick
    Audience-->>Events: cancel/prevent action for quest item
    end

    rect rgb(220,255,230)
    Note over Player,Events: Death & respawn
    Player->>Events: PlayerDeath
    Events->>Audience: onPlayerDeath
    Audience->>Inventory: remove quest item from drops
    Player->>Player: Respawn
    Audience->>Inventory: re-add quest item to player
    end

    rect rgb(245,230,255)
    Note over Player,Audience: Player removed from audience / dispose
    Player->>Audience: onPlayerRemove(player) / dispose()
    Audience->>Inventory: remove all quest item instances
    Audience->>Audience: clear per-player tracking and unregister listeners
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

  • Inspect inventory click logic for shift-click, container transfer, and crafting-slot edge cases.
  • Verify death handler reliably removes from drops and re-adds on respawn.
  • Check listener registration/unregistration and per-player tracking concurrency.
  • Confirm correct item equality, null-safety, and notifications when replacing items.

Poem

I nibble code like crunchy hay,
A quest item kept, I guard the way.
Through drops and death I hop and mend,
I hide it safe and coerce the bend,
Your pouch restored — your faithful friend. 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: introducing QuestItemAudienceEntry to enforce quest item retention, which is the core objective of the PR.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • 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

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
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: 3

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f0eab8c and c0e0534.

📒 Files selected for processing (1)
  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
extensions/*Extension/**

📄 CodeRabbit inference engine (extensions/AGENTS.md)

When adding a new extension, create its project directory with a name ending in "Extension" (e.g., MyPluginExtension)

Files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
extensions/{*Extension/src/**/*.kt,*Extension/**/*.kts}

📄 CodeRabbit inference engine (extensions/AGENTS.md)

Use 4 spaces for indentation and wrap lines at 120 characters in Kotlin code and Gradle Kotlin scripts

Files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
extensions/*Extension/src/**/*.kt

📄 CodeRabbit inference engine (extensions/AGENTS.md)

extensions/*Extension/src/**/*.kt: Keep functions short and focused; prefer guard clauses over nested conditionals
Document public APIs with KDoc, focusing on when to use them rather than restating method behavior
Avoid inline comments; refactor unclear code instead of adding explanatory comments

Files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
🧠 Learnings (10)
📚 Learning: 2025-08-24T11:10:07.264Z
Learnt from: CR
Repo: gabber235/Typewriter PR: 0
File: .github/instructions/entity-extension.add-entity.instructions.md:0-0
Timestamp: 2025-08-24T11:10:07.264Z
Learning: Applies to extensions/EntityExtension/src/main/kotlin/com/typewritermc/entity/entries/entity/minecraft/*Entity.kt : Ensure imports/types compile: Ref, Var, ConstVar, OnlyTags, and correct colors/icons/description in Entry

Applied to files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
📚 Learning: 2025-08-24T11:11:05.217Z
Learnt from: CR
Repo: gabber235/Typewriter PR: 0
File: .github/instructions/entity-extension.instructions.md:0-0
Timestamp: 2025-08-24T11:11:05.217Z
Learning: Applies to extensions/EntityExtension/src/main/kotlin/com/typewritermc/entity/entries/**/*.kt : Annotate all entry classes with Entry(id, description, Colors, icon); use Tags on definition entries; use OnlyTags on data lists to constrain allowed data types

Applied to files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
📚 Learning: 2025-08-24T11:11:05.217Z
Learnt from: CR
Repo: gabber235/Typewriter PR: 0
File: .github/instructions/entity-extension.instructions.md:0-0
Timestamp: 2025-08-24T11:11:05.217Z
Learning: Applies to extensions/EntityExtension/src/main/kotlin/com/typewritermc/entity/entries/**/*.kt : InteractEntityObjective text may include <entity> placeholder; obtain positions by querying active AudienceEntityDisplay instances

Applied to files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
📚 Learning: 2025-08-24T11:10:07.264Z
Learnt from: CR
Repo: gabber235/Typewriter PR: 0
File: .github/instructions/entity-extension.add-entity.instructions.md:0-0
Timestamp: 2025-08-24T11:10:07.264Z
Learning: Applies to extensions/EntityExtension/src/main/kotlin/com/typewritermc/entity/entries/entity/minecraft/*Entity.kt : If interaction support is needed, ensure EntityInteractEventEntry filters can target the new definition and InteractEntityObjective can resolve positions

Applied to files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
📚 Learning: 2025-08-24T11:10:07.264Z
Learnt from: CR
Repo: gabber235/Typewriter PR: 0
File: .github/instructions/entity-extension.add-entity.instructions.md:0-0
Timestamp: 2025-08-24T11:10:07.264Z
Learning: Applies to extensions/EntityExtension/src/main/kotlin/com/typewritermc/entity/entries/entity/minecraft/*Entity.kt : Keep visual and structural parity with similar entities (icons, colors, descriptions, class structure) like AllayEntity.kt or ArmorStandEntity.kt

Applied to files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
📚 Learning: 2025-08-24T11:11:05.217Z
Learnt from: CR
Repo: gabber235/Typewriter PR: 0
File: .github/instructions/entity-extension.instructions.md:0-0
Timestamp: 2025-08-24T11:11:05.217Z
Learning: Applies to extensions/EntityExtension/src/main/kotlin/com/typewritermc/entity/entries/**/*.kt : Choose correct activity scope: SharedEntityActivityEntry for shared behavior/position, IndividualEntityActivityEntry for per-viewer behavior/position

Applied to files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
📚 Learning: 2025-08-24T11:11:05.217Z
Learnt from: CR
Repo: gabber235/Typewriter PR: 0
File: .github/instructions/entity-extension.instructions.md:0-0
Timestamp: 2025-08-24T11:11:05.217Z
Learning: Applies to extensions/EntityExtension/src/main/kotlin/com/typewritermc/entity/entries/**/*.kt : Instance entries must implement SimpleEntityInstance (or an Advanced variant) with fields: id, name, definition: Ref<...Definition>, spawnLocation, data, and activity Ref of the correct scope

Applied to files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
📚 Learning: 2025-08-24T11:11:05.217Z
Learnt from: CR
Repo: gabber235/Typewriter PR: 0
File: .github/instructions/entity-extension.instructions.md:0-0
Timestamp: 2025-08-24T11:11:05.217Z
Learning: Applies to extensions/EntityExtension/src/main/kotlin/com/typewritermc/entity/entries/**/*.kt : Access Var values via .get(player) where applicable in activities and entries

Applied to files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
📚 Learning: 2025-08-24T11:11:05.217Z
Learnt from: CR
Repo: gabber235/Typewriter PR: 0
File: .github/instructions/entity-extension.instructions.md:0-0
Timestamp: 2025-08-24T11:11:05.217Z
Learning: Applies to extensions/EntityExtension/src/main/kotlin/com/typewritermc/entity/entries/**/*.kt : LookAtEntityInteractionBoundEntry must force player look within radius, adjust movement speed via attribute modifiers, and remove modifiers on teardown

Applied to files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
📚 Learning: 2025-08-24T11:11:05.217Z
Learnt from: CR
Repo: gabber235/Typewriter PR: 0
File: .github/instructions/entity-extension.instructions.md:0-0
Timestamp: 2025-08-24T11:11:05.217Z
Learning: Applies to extensions/EntityExtension/src/main/kotlin/com/typewritermc/entity/entries/**/*.kt : Avoid mutating Bukkit entities directly; operate via WrapperEntity and Typewriter EntityProperty APIs

Applied to files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
🔇 Additional comments (5)
extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt (5)

32-57: LGTM!

Entry definition follows project conventions with proper annotation, KDoc documentation, and clean implementation of AudienceEntry.


59-67: LGTM!

Thread-safe ConcurrentHashMap and proper listener registration pattern.


90-100: LGTM!

Cleanup logic correctly removes all matching quest items from inventory.


102-110: LGTM!

Drop prevention correctly identifies and blocks quest item drops.


176-181: LGTM!

Proper cleanup with listener unregistration and state clearing.

…ic/entries/audience/QuestItemAudienceEntry.kt

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Copy Markdown
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

♻️ Duplicate comments (2)
extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt (2)

76-84: Critical: Assignment to defensive copy has no effect.

Line 81 assigns to storageContents[firstNonEmptySlot], which is a defensive copy of the inventory array. The assignment modifies the copy, not the actual inventory, so the quest item is never placed when the inventory is full.

Apply this diff to fix:

             if (firstNonEmptySlot >= 0) {
                 val droppedItem = player.inventory.storageContents[firstNonEmptySlot]
                 if (droppedItem != null) {
                     player.world.dropItemNaturally(player.location, droppedItem)
-                    player.inventory.storageContents[firstNonEmptySlot] = questItem
+                    player.inventory.setItem(firstNonEmptySlot, questItem)
                     player.sendMessage(inventoryFullMessage.parsePlaceholders(player).asMini())
                 }
             }

176-182: Respawn handling lacks safety checks.

The delayed task has multiple reliability gaps:

  1. Player may disconnect before respawning—event.entity reference could be stale
  2. Player may have been removed from the audience during the death screen
  3. addItem() may fail silently if inventory is full on respawn, losing quest items

Apply this diff to add validation and handle full inventory:

         // Re-add the quest item when the player respawns
+        val playerId = event.entity.uniqueId
         server.scheduler.runTaskLater(plugin, Runnable {
+            val player = server.getPlayer(playerId) ?: return@Runnable
+            if (!contains(player)) return@Runnable
             questItemStacks.forEach { item ->
-                event.entity.inventory.addItem(item)
+                val leftOver = player.inventory.addItem(item)
+                if (leftOver.isNotEmpty()) {
+                    // Drop at player location if inventory is full
+                    player.world.dropItemNaturally(player.location, item)
+                }
             }
         }, 1L)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c0e0534 and d74c3a2.

📒 Files selected for processing (1)
  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
extensions/*Extension/**

📄 CodeRabbit inference engine (extensions/AGENTS.md)

When adding a new extension, create its project directory with a name ending in "Extension" (e.g., MyPluginExtension)

Files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
extensions/{*Extension/src/**/*.kt,*Extension/**/*.kts}

📄 CodeRabbit inference engine (extensions/AGENTS.md)

Use 4 spaces for indentation and wrap lines at 120 characters in Kotlin code and Gradle Kotlin scripts

Files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
extensions/*Extension/src/**/*.kt

📄 CodeRabbit inference engine (extensions/AGENTS.md)

extensions/*Extension/src/**/*.kt: Keep functions short and focused; prefer guard clauses over nested conditionals
Document public APIs with KDoc, focusing on when to use them rather than restating method behavior
Avoid inline comments; refactor unclear code instead of adding explanatory comments

Files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
🧠 Learnings (10)
📚 Learning: 2025-08-24T11:10:07.264Z
Learnt from: CR
Repo: gabber235/Typewriter PR: 0
File: .github/instructions/entity-extension.add-entity.instructions.md:0-0
Timestamp: 2025-08-24T11:10:07.264Z
Learning: Applies to extensions/EntityExtension/src/main/kotlin/com/typewritermc/entity/entries/entity/minecraft/*Entity.kt : Ensure imports/types compile: Ref, Var, ConstVar, OnlyTags, and correct colors/icons/description in Entry

Applied to files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
📚 Learning: 2025-08-24T11:11:05.217Z
Learnt from: CR
Repo: gabber235/Typewriter PR: 0
File: .github/instructions/entity-extension.instructions.md:0-0
Timestamp: 2025-08-24T11:11:05.217Z
Learning: Applies to extensions/EntityExtension/src/main/kotlin/com/typewritermc/entity/entries/**/*.kt : Annotate all entry classes with Entry(id, description, Colors, icon); use Tags on definition entries; use OnlyTags on data lists to constrain allowed data types

Applied to files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
📚 Learning: 2025-08-24T11:11:05.217Z
Learnt from: CR
Repo: gabber235/Typewriter PR: 0
File: .github/instructions/entity-extension.instructions.md:0-0
Timestamp: 2025-08-24T11:11:05.217Z
Learning: Applies to extensions/EntityExtension/src/main/kotlin/com/typewritermc/entity/entries/**/*.kt : Use snake_case, descriptive IDs (e.g., armor_stand_definition, allay_instance) and practical descriptions

Applied to files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
📚 Learning: 2025-08-24T11:10:07.264Z
Learnt from: CR
Repo: gabber235/Typewriter PR: 0
File: .github/instructions/entity-extension.add-entity.instructions.md:0-0
Timestamp: 2025-08-24T11:10:07.264Z
Learning: Applies to extensions/EntityExtension/src/main/kotlin/com/typewritermc/entity/entries/entity/minecraft/*Entity.kt : Keep visual and structural parity with similar entities (icons, colors, descriptions, class structure) like AllayEntity.kt or ArmorStandEntity.kt

Applied to files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
📚 Learning: 2025-08-24T11:11:05.217Z
Learnt from: CR
Repo: gabber235/Typewriter PR: 0
File: .github/instructions/entity-extension.instructions.md:0-0
Timestamp: 2025-08-24T11:11:05.217Z
Learning: Applies to extensions/EntityExtension/src/main/kotlin/com/typewritermc/entity/entries/**/*.kt : InteractEntityObjective text may include <entity> placeholder; obtain positions by querying active AudienceEntityDisplay instances

Applied to files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
📚 Learning: 2025-08-24T11:11:05.217Z
Learnt from: CR
Repo: gabber235/Typewriter PR: 0
File: .github/instructions/entity-extension.instructions.md:0-0
Timestamp: 2025-08-24T11:11:05.217Z
Learning: Applies to extensions/EntityExtension/src/main/kotlin/com/typewritermc/entity/entries/**/*.kt : Instance entries must implement SimpleEntityInstance (or an Advanced variant) with fields: id, name, definition: Ref<...Definition>, spawnLocation, data, and activity Ref of the correct scope

Applied to files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
📚 Learning: 2025-08-24T11:11:05.217Z
Learnt from: CR
Repo: gabber235/Typewriter PR: 0
File: .github/instructions/entity-extension.instructions.md:0-0
Timestamp: 2025-08-24T11:11:05.217Z
Learning: Applies to extensions/EntityExtension/src/main/kotlin/com/typewritermc/entity/entries/**/*.kt : Choose correct activity scope: SharedEntityActivityEntry for shared behavior/position, IndividualEntityActivityEntry for per-viewer behavior/position

Applied to files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
📚 Learning: 2025-08-24T11:10:07.264Z
Learnt from: CR
Repo: gabber235/Typewriter PR: 0
File: .github/instructions/entity-extension.add-entity.instructions.md:0-0
Timestamp: 2025-08-24T11:10:07.264Z
Learning: Applies to extensions/EntityExtension/src/main/kotlin/com/typewritermc/entity/entries/entity/minecraft/*Entity.kt : Instance class <EntityName>Instance must be annotated with Entry and declare fields: id, name, definition: Ref<<EntityName>Definition>, spawnLocation, data (+OnlyTags as above), activity: Ref<out SharedEntityActivityEntry>

Applied to files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
📚 Learning: 2025-08-24T11:10:07.264Z
Learnt from: CR
Repo: gabber235/Typewriter PR: 0
File: .github/instructions/entity-extension.add-entity.instructions.md:0-0
Timestamp: 2025-08-24T11:10:07.264Z
Learning: Applies to extensions/EntityExtension/src/main/kotlin/com/typewritermc/entity/entries/entity/minecraft/*Entity.kt : Definition class <EntityName>Definition must be annotated with Entry (and optional Tags) and declare fields: id, name, displayName: Var<String>, sound: Var<Sound>, data: List<Ref<EntityData<*>>>

Applied to files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
📚 Learning: 2025-08-24T11:10:07.264Z
Learnt from: CR
Repo: gabber235/Typewriter PR: 0
File: .github/instructions/entity-extension.add-entity.instructions.md:0-0
Timestamp: 2025-08-24T11:10:07.264Z
Learning: Applies to extensions/EntityExtension/src/main/kotlin/com/typewritermc/entity/entries/entity/minecraft/*Entity.kt : If interaction support is needed, ensure EntityInteractEventEntry filters can target the new definition and InteractEntityObjective can resolve positions

Applied to files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
🔇 Additional comments (5)
extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt (5)

32-57: LGTM!

The entry annotation follows conventions with a descriptive snake_case ID, practical description, and appropriate icon. The KDoc effectively explains when to use this audience entry and its behavioral constraints.


59-67: LGTM!

The use of ConcurrentHashMap for thread-safe player item tracking and proper event listener registration in initialize() is correct.


90-100: LGTM!

The cleanup logic correctly removes all instances of the quest item using setItem() and properly identifies matching items with isSameAs().


102-110: LGTM!

The drop prevention logic is correct and uses appropriate event priority.


184-189: LGTM!

The disposal sequence correctly unregisters the listener and clears state, preventing memory leaks.

@RenaudRl RenaudRl marked this pull request as draft November 29, 2025 11:54
@RenaudRl RenaudRl marked this pull request as ready for review November 29, 2025 14:40
Copy link
Copy Markdown
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: 0

♻️ Duplicate comments (1)
extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt (1)

132-173: Inventory protection gaps: cursor→container still bypasses, hotbar swaps allow container moves, and crafting table slots aren’t covered.

There are a few issues in onInventoryClick that weaken the “cannot move to containers / cannot be crafted” guarantees:

  1. Cursor→container on empty slot still bypasses protection (duplicate of earlier concern).
    On Line 153, val currentItem = event.currentItem ?: return causes an early return when clicking an empty container slot. In that case currentItem is null, but cursor can hold the quest item. The new cursor-check block at Lines 156–162 never runs, so clicking an empty chest slot with the quest item on the cursor still moves it into the chest.

    • Scenario: Open chest, pick up quest item onto cursor, click empty chest slot → quest item ends up in container.
  2. Number-key / hotbar swaps into containers are not blocked.
    If the quest item is in a hotbar slot and the player clicks a container slot while pressing a number key, Bukkit will swap the hotbar item with the container slot. In this case:

    • clickedInventory is the container,
    • currentItem is the container’s slot item,
    • the quest item is only visible via event.hotbarButton/player.inventory.getItem(...).
      None of the current checks examine the hotbar slot, so the quest item can be moved into a container.
  3. Crafting table (WORKBENCH) grid isn’t covered.
    The crafting guard currently only checks InventoryType.CRAFTING (the 2×2 grid in the player inventory). Using a crafting table (InventoryType.WORKBENCH) still allows the quest item to be placed in crafting input slots, which contradicts the “cannot be lost via crafting” requirement.

A possible fix that addresses all three points:

         val clickedInventory = event.clickedInventory ?: return
-        
-        // Block ANY interaction with crafting slots (slots 1-4 in CRAFTING inventory type)
-        if (clickedInventory.type == InventoryType.CRAFTING && event.slot in 1..4) {
-            val currentItem = event.currentItem
-            val cursorItem = event.cursor
-            
-            // Check if the item being placed or already in the slot is the quest item
-            if ((currentItem != null && entry.item.get(player).isSameAs(player, currentItem)) ||
-                (cursorItem != null && entry.item.get(player).isSameAs(player, cursorItem))) {
-                event.isCancelled = true
-                return
-            }
-        }
-        
-        val currentItem = event.currentItem ?: return
-        val cursorItem = event.cursor
-        
-        // Prevent placing quest item from cursor into container
-        if (clickedInventory.type != InventoryType.PLAYER && clickedInventory.type != InventoryType.CRAFTING) {
-            if (cursorItem != null && !cursorItem.isEmpty && entry.item.get(player).isSameAs(player, cursorItem)) {
-                event.isCancelled = true
-                return
-            }
-        }
-
-        // Prevent moving quest item to non-player inventories (containers)
-        if (clickedInventory.type == InventoryType.PLAYER && event.view.topInventory.type != InventoryType.PLAYER) {
-            if (entry.item.get(player).isSameAs(player, currentItem)) {
-                // Check if player is trying to move item to container
-                if (event.click.isShiftClick) {
-                    event.isCancelled = true
-                }
-            }
-        }
+        
+        // Block ANY interaction with crafting slots (player 2x2 and workbench) when the quest item is involved
+        if ((clickedInventory.type == InventoryType.CRAFTING && event.slot in 1..4) ||
+            (clickedInventory.type == InventoryType.WORKBENCH && event.slot in 1..9)
+        ) {
+            val currentItem = event.currentItem
+            val cursorItem = event.cursor
+
+            if ((currentItem != null && entry.item.get(player).isSameAs(player, currentItem)) ||
+                (cursorItem != null && entry.item.get(player).isSameAs(player, cursorItem))
+            ) {
+                event.isCancelled = true
+                return
+            }
+        }
+
+        val cursorItem = event.cursor
+
+        // Prevent placing quest item from cursor into any non-player inventory (even on empty slots)
+        if (clickedInventory.type != InventoryType.PLAYER && clickedInventory.type != InventoryType.CRAFTING) {
+            if (cursorItem != null && !cursorItem.isEmpty && entry.item.get(player).isSameAs(player, cursorItem)) {
+                event.isCancelled = true
+                return
+            }
+        }
+
+        val currentItem = event.currentItem ?: return
+
+        // Prevent moving quest item to non-player inventories (containers) via shift-click from player inventory
+        if (clickedInventory.type == InventoryType.PLAYER && event.view.topInventory.type != InventoryType.PLAYER) {
+            if (entry.item.get(player).isSameAs(player, currentItem) && event.click.isShiftClick) {
+                event.isCancelled = true
+                return
+            }
+        }
+
+        // Prevent moving quest item into containers via hotbar number-key swaps
+        if (event.hotbarButton in 0..8 &&
+            clickedInventory.type != InventoryType.PLAYER &&
+            event.view.topInventory.type != InventoryType.PLAYER
+        ) {
+            val hotbarItem = player.inventory.getItem(event.hotbarButton)
+            if (hotbarItem != null && entry.item.get(player).isSameAs(player, hotbarItem)) {
+                event.isCancelled = true
+                return
+            }
+        }

This keeps normal player-inventory rearrangements intact while closing the remaining escape hatches to containers and crafting.

🧹 Nitpick comments (1)
extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt (1)

69-107: Simplify playerItems tracking and remove unused value.

playerItems is only used as a presence flag in onPlayerRemove (the ItemStack value is never read), and all event checks use contains(player) from AudienceDisplay. You can simplify by dropping the map entirely and always cleaning up via an inventory scan on removal; that scan is cheap and safe even if no quest item was ever added.

Suggested refactor:

-    private val playerItems = ConcurrentHashMap<UUID, ItemStack>()
-
@@
     override fun onPlayerAdd(player: Player) {
@@
-            playerItems[player.uniqueId] = questItem
         })
     }
@@
     override fun onPlayerRemove(player: Player) {
-        val questItem = playerItems.remove(player.uniqueId) ?: return
         val entry = ref.get() ?: return
 
         // Remove all instances of the quest item from the player's inventory
@@
     override fun dispose() {
         super.dispose()
         unregister()
-        playerItems.clear()
     }

This keeps behavior the same while removing state that doesn’t add value.

Also applies to: 110-120

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ef5094f and 948d830.

📒 Files selected for processing (1)
  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
extensions/*Extension/**

📄 CodeRabbit inference engine (extensions/AGENTS.md)

When adding a new extension, create its project directory with a name ending in "Extension" (e.g., MyPluginExtension)

Files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
extensions/{*Extension/src/**/*.kt,*Extension/**/*.kts}

📄 CodeRabbit inference engine (extensions/AGENTS.md)

Use 4 spaces for indentation and wrap lines at 120 characters in Kotlin code and Gradle Kotlin scripts

Files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
extensions/*Extension/src/**/*.kt

📄 CodeRabbit inference engine (extensions/AGENTS.md)

extensions/*Extension/src/**/*.kt: Keep functions short and focused; prefer guard clauses over nested conditionals
Document public APIs with KDoc, focusing on when to use them rather than restating method behavior
Avoid inline comments; refactor unclear code instead of adding explanatory comments

Files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
🧠 Learnings (11)
📚 Learning: 2025-08-24T11:10:07.264Z
Learnt from: CR
Repo: gabber235/Typewriter PR: 0
File: .github/instructions/entity-extension.add-entity.instructions.md:0-0
Timestamp: 2025-08-24T11:10:07.264Z
Learning: Applies to extensions/EntityExtension/src/main/kotlin/com/typewritermc/entity/entries/entity/minecraft/*Entity.kt : Ensure imports/types compile: Ref, Var, ConstVar, OnlyTags, and correct colors/icons/description in Entry

Applied to files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
📚 Learning: 2025-08-24T11:11:05.217Z
Learnt from: CR
Repo: gabber235/Typewriter PR: 0
File: .github/instructions/entity-extension.instructions.md:0-0
Timestamp: 2025-08-24T11:11:05.217Z
Learning: Applies to extensions/EntityExtension/src/main/kotlin/com/typewritermc/entity/entries/**/*.kt : Annotate all entry classes with Entry(id, description, Colors, icon); use Tags on definition entries; use OnlyTags on data lists to constrain allowed data types

Applied to files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
📚 Learning: 2025-08-24T11:11:05.217Z
Learnt from: CR
Repo: gabber235/Typewriter PR: 0
File: .github/instructions/entity-extension.instructions.md:0-0
Timestamp: 2025-08-24T11:11:05.217Z
Learning: Applies to extensions/EntityExtension/src/main/kotlin/com/typewritermc/entity/entries/**/*.kt : InteractEntityObjective text may include <entity> placeholder; obtain positions by querying active AudienceEntityDisplay instances

Applied to files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
📚 Learning: 2025-08-24T11:11:05.217Z
Learnt from: CR
Repo: gabber235/Typewriter PR: 0
File: .github/instructions/entity-extension.instructions.md:0-0
Timestamp: 2025-08-24T11:11:05.217Z
Learning: Applies to extensions/EntityExtension/src/main/kotlin/com/typewritermc/entity/entries/**/*.kt : Use snake_case, descriptive IDs (e.g., armor_stand_definition, allay_instance) and practical descriptions

Applied to files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
📚 Learning: 2025-08-24T11:11:05.217Z
Learnt from: CR
Repo: gabber235/Typewriter PR: 0
File: .github/instructions/entity-extension.instructions.md:0-0
Timestamp: 2025-08-24T11:11:05.217Z
Learning: Applies to extensions/EntityExtension/src/main/kotlin/com/typewritermc/entity/entries/**/*.kt : Instance entries must implement SimpleEntityInstance (or an Advanced variant) with fields: id, name, definition: Ref<...Definition>, spawnLocation, data, and activity Ref of the correct scope

Applied to files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
📚 Learning: 2025-08-24T11:10:07.264Z
Learnt from: CR
Repo: gabber235/Typewriter PR: 0
File: .github/instructions/entity-extension.add-entity.instructions.md:0-0
Timestamp: 2025-08-24T11:10:07.264Z
Learning: Applies to extensions/EntityExtension/src/main/kotlin/com/typewritermc/entity/entries/entity/minecraft/*Entity.kt : Keep visual and structural parity with similar entities (icons, colors, descriptions, class structure) like AllayEntity.kt or ArmorStandEntity.kt

Applied to files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
📚 Learning: 2025-08-24T11:10:07.264Z
Learnt from: CR
Repo: gabber235/Typewriter PR: 0
File: .github/instructions/entity-extension.add-entity.instructions.md:0-0
Timestamp: 2025-08-24T11:10:07.264Z
Learning: Applies to extensions/EntityExtension/src/main/kotlin/com/typewritermc/entity/entries/entity/minecraft/*Entity.kt : Instance class <EntityName>Instance must be annotated with Entry and declare fields: id, name, definition: Ref<<EntityName>Definition>, spawnLocation, data (+OnlyTags as above), activity: Ref<out SharedEntityActivityEntry>

Applied to files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
📚 Learning: 2025-08-24T11:11:05.217Z
Learnt from: CR
Repo: gabber235/Typewriter PR: 0
File: .github/instructions/entity-extension.instructions.md:0-0
Timestamp: 2025-08-24T11:11:05.217Z
Learning: Applies to extensions/EntityExtension/src/main/kotlin/com/typewritermc/entity/entries/**/*.kt : Choose correct activity scope: SharedEntityActivityEntry for shared behavior/position, IndividualEntityActivityEntry for per-viewer behavior/position

Applied to files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
📚 Learning: 2025-08-24T11:10:07.264Z
Learnt from: CR
Repo: gabber235/Typewriter PR: 0
File: .github/instructions/entity-extension.add-entity.instructions.md:0-0
Timestamp: 2025-08-24T11:10:07.264Z
Learning: Applies to extensions/EntityExtension/src/main/kotlin/com/typewritermc/entity/entries/entity/minecraft/*Entity.kt : Definition class <EntityName>Definition must be annotated with Entry (and optional Tags) and declare fields: id, name, displayName: Var<String>, sound: Var<Sound>, data: List<Ref<EntityData<*>>>

Applied to files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
📚 Learning: 2025-08-24T11:11:05.217Z
Learnt from: CR
Repo: gabber235/Typewriter PR: 0
File: .github/instructions/entity-extension.instructions.md:0-0
Timestamp: 2025-08-24T11:11:05.217Z
Learning: Applies to extensions/EntityExtension/src/main/kotlin/com/typewritermc/entity/entries/**/*.kt : Access Var values via .get(player) where applicable in activities and entries

Applied to files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
📚 Learning: 2025-08-24T11:11:05.217Z
Learnt from: CR
Repo: gabber235/Typewriter PR: 0
File: .github/instructions/entity-extension.instructions.md:0-0
Timestamp: 2025-08-24T11:11:05.217Z
Learning: Applies to extensions/EntityExtension/src/main/kotlin/com/typewritermc/entity/entries/**/*.kt : EntityInteractEventEntry should handle left/right click with optional shift and interaction type filters to trigger dialogues/flows

Applied to files:

  • extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt
🔇 Additional comments (2)
extensions/BasicExtension/src/main/kotlin/com/typewritermc/basic/entries/audience/QuestItemAudienceEntry.kt (2)

32-37: Entry and display wiring look solid and match extension guidelines.

The @Entry metadata, KDoc, and the display() implementation are consistent with other audience entries, and the display’s lifecycle correctly registers/unregisters the listener in initialize()/dispose(). No issues here.

Also applies to: 49-57, 59-68, 202-206


122-130: Drop and death handling correctly preserve the quest item across death (including edge cases).

The drop handler and death handler together:

  • Block manual drops of the quest item.
  • Strip quest items from PlayerDeathEvent.drops and re-add them after respawn using a delayed task.
  • Safely handle disconnects (getPlayer null) and audience removal between death and respawn via contains(player).
  • Handle full inventories on respawn by dropping leftovers naturally so the item is never silently lost.

Behavior here looks robust and aligned with the retention requirements.

Also applies to: 175-200

@RenaudRl RenaudRl marked this pull request as draft November 29, 2025 14:46
Refactor quest item handling to use persistent data for identification. Implement methods to tag items and check if they are quest items, enhancing inventory interaction events.
@RenaudRl RenaudRl marked this pull request as ready for review November 29, 2025 16:54
@RenaudRl RenaudRl marked this pull request as draft November 30, 2025 01:54
Refactor QuestItemAudienceEntry to improve item handling and interactions. Added cooldowns for interactions, improved inventory management, and ensured quest items are not lost on player death.
Removed unused properties and methods related to item consumption and interaction. Simplified the QuestItemAudienceEntry class by eliminating unnecessary code.
@RenaudRl RenaudRl marked this pull request as ready for review December 14, 2025 23:25
@LegendsOfXania
Copy link
Copy Markdown
Contributor

LegendsOfXania commented Dec 19, 2025

Hey, I noticed that you were using ItemMeta. For your information, this API has been deprecated in favour of DataComponents

@gabber235
Copy link
Copy Markdown
Owner

Hey @RenaudRl, I have some bad news. While refactoring the code to make it more readable I thought of several problems.

Since the item is a Var it should be able to be changed (and we definitely want this as a feature). However this poses some issues. Mostly what happens if the player spreads out the item (i.e., having the item in multiple slots). What if the amount of the quest item changes? What if the player is holding the item when it needs to change, etc.

To circumvent all of this I think it's better not to allow splitting up the item. However this requires some additional code in the event handlers (so right‑click and other actions don't place one item but always the entire stack). The item also shouldn't be draggable. This also requires extra testing to make sure things work correctly.

Finally I think there is still a bug where you can use hotbar keys to move the item into another container. If you have the quest item in your hotbar, hover over a chest slot, then press 0‑9, it will move the item. We need to block this as well.

Could you make these changes ❤️? Because they take up too much time to do myself, which I'd rather spend on v1.0

@RenaudRl
Copy link
Copy Markdown
Contributor Author

RenaudRl commented Dec 24, 2025

Hey, I noticed that you were using ItemMeta. For your information, this API has been deprecated in favour of DataComponents

I agree. In fact, I was referring to the current support, which is part of version 1.21.3. It would be best to upgrade to at least version 1.21.4.

@RenaudRl RenaudRl marked this pull request as draft December 25, 2025 03:36
Enhance quest item interaction handling in inventory events.
@gabber235 gabber235 force-pushed the develop branch 2 times, most recently from 59e27cf to 4d9cde8 Compare April 5, 2026 14:11
@RenaudRl RenaudRl closed this by deleting the head repository Apr 17, 2026
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.

3 participants