Skip to content

Add configurable wartime block lists for placeable, breakable, and interactable blocks - #1955

Closed
dmccoystephenson with Copilot wants to merge 4 commits into
mainfrom
copilot/add-configurable-war-interactions
Closed

dmccoystephenson with Copilot wants to merge 4 commits into
mainfrom
copilot/add-configurable-war-interactions

Conversation

Copilot AI commented Mar 22, 2026

Copy link
Copy Markdown
Contributor

Currently wartime block control is all-or-nothing: enableWartimeBlockDestruction allows destroying ALL blocks, and laddersPlaceableInEnemyFactionTerritory only covers ladders. There's no way to allow e.g. scaffolding placement + door interaction without opening up full block destruction.

Adds three config lists under war.items that grant granular per-block-type permissions in enemy territory during war:

war:
  items:
    placeable: [LADDER, SCAFFOLDING]
    breakable: [LADDER, SCAFFOLDING]
    interactable: [OAK_DOOR, STONE_BUTTON, OAK_FENCE_GATE]

Changes

  • MfClaimService — New isWartimeBlockActionAllowed(playerId, claim, blockTypeName, configPath) method. Extracted shared war-status logic into private isAtWarWith() helper (deduplicates from isWartimeLadderPlacementAllowed).
  • BlockBreakListener — Falls through to war.items.breakable check before cancelling
  • BlockPlaceListener — Checks war.items.placeable alongside existing ladder-specific logic
  • PlayerInteractListener — Checks war.items.interactable after ladder placement check
  • config.yml — New war.items section with three empty lists (backwards compatible)
  • CONFIG.md — Documents all three options with examples
  • Tests — Added cases for wartime breakable/interactable allow and deny paths

These lists work alongside the existing boolean toggles — enableWartimeBlockDestruction still overrides everything, and laddersPlaceableInEnemyFactionTerritory still works independently.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • hub.spigotmc.org
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.lang.invoke=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.prefs/java.util.prefs=ALL-UNNAMED --add-opens java.prefs/java.util.prefs=ALL-UNNAMED --add-opens java.base/java.nio.charset=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED --add-opens java.base/java.util.concurrent.atomic=ALL-UNNAMED -XX:MaxMetaspaceSize=256m (dns block)
  • jitpack.io
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.lang.invoke=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.prefs/java.util.prefs=ALL-UNNAMED --add-opens java.prefs/java.util.prefs=ALL-UNNAMED --add-opens java.base/java.nio.charset=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED --add-opens java.base/java.util.concurrent.atomic=ALL-UNNAMED -XX:MaxMetaspaceSize=256m (dns block)
  • repo.dansplugins.com
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.lang.invoke=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.prefs/java.util.prefs=ALL-UNNAMED --add-opens java.prefs/java.util.prefs=ALL-UNNAMED --add-opens java.base/java.nio.charset=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED --add-opens java.base/java.util.concurrent.atomic=ALL-UNNAMED -XX:MaxMetaspaceSize=256m (dns block)
  • repo.extendedclip.com
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.lang.invoke=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.prefs/java.util.prefs=ALL-UNNAMED --add-opens java.prefs/java.util.prefs=ALL-UNNAMED --add-opens java.base/java.nio.charset=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED --add-opens java.base/java.util.concurrent.atomic=ALL-UNNAMED -XX:MaxMetaspaceSize=256m (dns block)
  • repo.mikeprimm.com
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.lang.invoke=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.prefs/java.util.prefs=ALL-UNNAMED --add-opens java.prefs/java.util.prefs=ALL-UNNAMED --add-opens java.base/java.nio.charset=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED --add-opens java.base/java.util.concurrent.atomic=ALL-UNNAMED -XX:MaxMetaspaceSize=256m (dns block)
  • repo.rpkit.com
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.lang.invoke=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.prefs/java.util.prefs=ALL-UNNAMED --add-opens java.prefs/java.util.prefs=ALL-UNNAMED --add-opens java.base/java.nio.charset=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED --add-opens java.base/java.util.concurrent.atomic=ALL-UNNAMED -XX:MaxMetaspaceSize=256m (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

This section details on the original issue you should resolve

<issue_title>[Feature] A configurable list of blocks that become breakable/placeable/interactable during war</issue_title>
<issue_description>## Is your feature request related to a problem?
Currently, the only thing that changes when you declare war on another faction, is that you can place ladders in enemy territory (if that option is enabled). Or, if you have wartime block destruction enabled, ANY block can be destroyed in enemy territory, but you don't get to choose which blocks. It's just all of them.

Being able to use ladders in war makes wars more immersive, as ladders have historically been used to climb walls in castle sieges. But what about scaffolding? What about being able to open wooden doors or press buttons to open iron doors?

I personally would like for it to be possible to configure the plugin so that attackers in a war can use your buttons, doors, fence gates, etc. AND be able to place/break scaffolding. But not break other blocks.

Describe the solution you'd like

A possible solution is to add 3 lists of blocks to the config under war settings.
Placeable: A list of blocks that become placeable in enemy territory during a war
Breakable: A list of blocks that become breakable in enemy territory during a war
Interactable: A list of blocks that become interactable in enemy territory during a war</issue_description>

Comments on the Issue (you are @copilot in this section)


💬 Send tasks to Copilot coding agent from Slack and Teams to turn conversations into code. Copilot posts an update in your thread when it's finished.

Copilot AI and others added 2 commits March 22, 2026 20:25
…teractable blocks

Add war.items.placeable, war.items.breakable, and war.items.interactable
config lists that allow server admins to specify which blocks can be
placed, broken, or interacted with in enemy territory during war.

- Add isWartimeBlockActionAllowed() to MfClaimService
- Update BlockBreakListener to check war.items.breakable list
- Update BlockPlaceListener to check war.items.placeable list
- Update PlayerInteractListener to check war.items.interactable list
- Add config entries to config.yml and document in CONFIG.md
- Add tests for new wartime block action functionality

Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
Agent-Logs-Url: https://github.com/Dans-Plugins/Medieval-Factions/sessions/9a502860-9de7-449d-b5ae-49699b06b841
…ockPlaceListener

Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
Agent-Logs-Url: https://github.com/Dans-Plugins/Medieval-Factions/sessions/9a502860-9de7-449d-b5ae-49699b06b841
Copilot AI changed the title [WIP] Add configurable list of interactive blocks during war Add configurable wartime block lists for placeable, breakable, and interactable blocks Mar 22, 2026
Copilot AI requested a review from dmccoystephenson March 22, 2026 20:27

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.

Comment on lines 156 to 158
`when`(claimService.isInteractionAllowed(mfPlayer.id, claim)).thenReturn(false)
`when`(claimService.isWartimeBlockActionAllowed(mfPlayer.id, claim, block.type.name, "war.items.breakable")).thenReturn(false)

Copilot AI Apr 25, 2026

Copy link

Choose a reason for hiding this comment

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

block.type is never stubbed in this test fixture (see TestUtils.createMockBlock), so the new use of block.type.name will evaluate to null and can throw at stubbing time and/or when the listener executes. Stub block.type to a real Material (e.g., STONE) before referencing .name, or use a constant material name string in the mock setup.

Copilot uses AI. Check for mistakes.
Comment on lines +184 to +186
`when`(claimService.isInteractionAllowed(mfPlayer.id, claim)).thenReturn(false)
`when`(claimService.isWartimeBlockActionAllowed(mfPlayer.id, claim, block.type.name, "war.items.breakable")).thenReturn(true)

Copilot AI Apr 25, 2026

Copy link

Choose a reason for hiding this comment

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

This test still depends on block.type.name, but block.type is not stubbed in the fixture. Because the listener now calls event.block.type.name to consult the wartime breakable list, the test will NPE unless you stub block.type to a concrete Material before invoking uut.onBlockBreak(event).

Copilot uses AI. Check for mistakes.
Comment on lines +102 to +105
fun isWartimeBlockActionAllowed(playerId: MfPlayerId, claim: MfClaimedChunk, blockTypeName: String, configPath: String): Boolean {
val allowedBlocks = plugin.config.getStringList(configPath)
if (!allowedBlocks.contains(blockTypeName)) return false
return isAtWarWith(playerId, claim.factionId)

Copilot AI Apr 25, 2026

Copy link

Choose a reason for hiding this comment

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

isWartimeBlockActionAllowed pulls getStringList(configPath) and does a linear contains on every event call. Since this runs on hot interaction/break/place paths, consider normalizing and caching the configured materials (e.g., a Set<Material> per list) and/or at least trimming/uppercasing entries once; this also lets you warn on invalid material names (similar to MfGateService.loadRestrictedBlocksFromConfig).

Copilot uses AI. Check for mistakes.
Comment on lines +62 to 67
val isLadderAllowed = event.block.type == Material.LADDER && plugin.config.getBoolean("factions.laddersPlaceableInEnemyFactionTerritory")
val isInPlaceableList = claimService.isWartimeBlockActionAllowed(mfPlayer.id, claim, event.block.type.name, "war.items.placeable")
if (!isLadderAllowed && !isInPlaceableList) {
event.isCancelled = true
event.player.sendMessage("$RED${plugin.language["CannotPlaceBlockInFactionTerritory", claimFaction.name]}")
}

Copilot AI Apr 25, 2026

Copy link

Choose a reason for hiding this comment

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

The new wartime placeable-list behavior (war.items.placeable) is introduced here, but there are no unit tests covering claimed-territory placement allow/deny based on this list (BlockPlaceListenerTest currently only covers gate/wilderness paths). Add tests for (1) a block in the placeable list being allowed during war and (2) a block not in the list being denied (including interaction with the ladder-specific toggle).

Copilot uses AI. Check for mistakes.
}

@Test
fun onPlayerInteract_WartimeNonInteractableBlock_ShouldBlockInteraction() {

Copilot AI Apr 25, 2026

Copy link

Choose a reason for hiding this comment

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

Test name is misleading: the clicked block in this fixture defaults to Material.OAK_DOOR (interactable). Consider renaming to something like "WartimeNonAllowedBlock" / "WartimeBlockNotInInteractableList" so the name reflects what the test actually verifies (deny when isWartimeBlockActionAllowed returns false).

Suggested change
fun onPlayerInteract_WartimeNonInteractableBlock_ShouldBlockInteraction() {
fun onPlayerInteract_WartimeBlockActionNotAllowed_ShouldBlockInteraction() {

Copilot uses AI. Check for mistakes.
@dmccoystephenson
dmccoystephenson changed the base branch from develop to main April 25, 2026 21:49
@dmccoystephenson

Copy link
Copy Markdown
Member

@copilot You are a coding agent tasked with resolving all open comments and review threads
on this pull request. Work through every comment systematically and apply the
appropriate code changes.


Instructions

1. Discover all comments

  • Read every review thread, inline comment, and general PR comment
  • Include comments from: Copilot, human reviewers, and any other bots
  • Do not skip comments marked as suggestions — apply them if they make sense

2. For each comment, do the following:

  • Understand what is being requested or flagged
  • Locate the relevant file(s) and line(s)
  • Apply the fix directly in code
  • If a Copilot suggestion diff is provided, apply it (or an equivalent fix)
  • If the comment is ambiguous, use your best judgment and reply in the PR thread or review comment explaining what you did; only add a code comment when explicitly requested or when future maintainers would benefit from it
  • After addressing a comment, reply to the thread summarising what you did and mark it as resolved if your environment supports it

3. Types of issues to handle:

  • Code quality / style issues (naming, formatting, readability)
  • Logic bugs or edge cases flagged by reviewers
  • Security concerns (e.g. input validation, secrets, injection risks)
  • Performance suggestions
  • Missing or incomplete error handling
  • Test coverage gaps
  • Documentation / comment improvements
  • Dependency or import issues

4. Do NOT:

  • Dismiss or ignore any comment without acting on it — note that "acting" can also mean replying to the thread with a rationale (e.g., won't fix, already addressed elsewhere) and leaving the code unchanged when appropriate
  • Leave TODO comments as a substitute for fixing the issue
  • Break existing tests while addressing comments
  • Change functionality beyond the scope of what the comment asks for

After making changes

  • Ensure all existing tests still pass
  • Add or update tests if a comment explicitly asks for them, or if a bug fix warrants it
  • Double-check that every open thread has been addressed, replied to, and resolved

Context

  • This PR may contain Copilot-generated review comments — treat these with the same priority as human comments
  • If two comments conflict with each other, prefer the human reviewer's intent and document the conflict and resolution in the PR conversation or commit message, not in a code comment
  • Commit changes in logical groups if there are many unrelated fixes

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.

[Feature] A configurable list of blocks that become breakable/placeable/interactable during war

3 participants