├── imgs ├── icon.png ├── icon64.png ├── icon256.png ├── logo_carrier.png └── github-download-source.png ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── dependabot.yml └── workflows │ ├── gradle.yml │ └── codeql.yml ├── changelogs ├── 0.0.25-FIX1.md ├── 0.0.25-FIX3.md ├── 0.0.23-RC3.md ├── 0.0.25-FIX2.md ├── 0.0.23-RC2.md ├── 0.0.23-RC6.md ├── 0.0.23-RC7.md ├── 0.0.25.md ├── 0.0.23-RC4.md ├── 0.0.23-RC5.md ├── 0.0.23-RC1.md └── 0.0.24-RC1.md ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── req └── google-java-format-1.22.0-all-deps.jar ├── lombok.config ├── src ├── main │ ├── java │ │ ├── com │ │ │ └── cjburkey │ │ │ │ └── claimchunk │ │ │ │ ├── gui │ │ │ │ ├── CCOpenGui.java │ │ │ │ ├── ICCGui.java │ │ │ │ ├── screens │ │ │ │ │ ├── PermSelectMenu.java │ │ │ │ │ ├── PermModifyMenu.java │ │ │ │ │ └── MainMenu.java │ │ │ │ └── CCGuiHandler.java │ │ │ │ ├── service │ │ │ │ └── prereq │ │ │ │ │ ├── claim │ │ │ │ │ ├── IClaimPrereq.java │ │ │ │ │ ├── UnclaimedPrereq.java │ │ │ │ │ ├── MaxChunksPrereq.java │ │ │ │ │ ├── WorldPrereq.java │ │ │ │ │ ├── PermissionPrereq.java │ │ │ │ │ ├── WorldGuardPrereq.java │ │ │ │ │ ├── PrereqClaimData.java │ │ │ │ │ └── NearChunkPrereq.java │ │ │ │ │ ├── PrereqChecker.java │ │ │ │ │ └── IPrereq.java │ │ │ │ ├── data │ │ │ │ ├── sqlite │ │ │ │ │ ├── SqlDataChunk.java │ │ │ │ │ ├── SqlClaimedChunk.java │ │ │ │ │ ├── SqlFlagEntry.java │ │ │ │ │ ├── SqlDataPlayer.java │ │ │ │ │ └── Translators.java │ │ │ │ ├── newdata │ │ │ │ │ └── DataConvert.java │ │ │ │ └── JsonConfig.java │ │ │ │ ├── player │ │ │ │ ├── SimplePlayerData.java │ │ │ │ ├── AdminOverride.java │ │ │ │ ├── FullPlayerData.java │ │ │ │ └── PlayerHandler.java │ │ │ │ ├── transition │ │ │ │ └── Pre0024FullPlayerData.java │ │ │ │ ├── config │ │ │ │ ├── ccconfig │ │ │ │ │ ├── ICCConfigSerializable.java │ │ │ │ │ ├── CCConfigParseError.java │ │ │ │ │ ├── CCConfigHandler.java │ │ │ │ │ └── NSKey.java │ │ │ │ ├── spread │ │ │ │ │ ├── FullSpreadProfile.java │ │ │ │ │ └── SpreadProfile.java │ │ │ │ └── access │ │ │ │ │ └── Accesses.java │ │ │ │ ├── worldguard │ │ │ │ ├── WorldGuardHandler.java │ │ │ │ └── WorldGuardApi.java │ │ │ │ ├── api │ │ │ │ ├── layer │ │ │ │ │ ├── IClaimChunkLayer.java │ │ │ │ │ └── ClaimChunkLayerHandler.java │ │ │ │ └── IClaimChunkPlugin.java │ │ │ │ ├── chunk │ │ │ │ ├── DataChunk.java │ │ │ │ ├── AutoClaimHandler.java │ │ │ │ └── ChunkPos.java │ │ │ │ ├── smartcommand │ │ │ │ └── sub │ │ │ │ │ ├── ply │ │ │ │ │ ├── UnclaimCmd.java │ │ │ │ │ ├── flags │ │ │ │ │ │ ├── CmdSetPermFlag.java │ │ │ │ │ │ ├── CmdViewPermFlag.java │ │ │ │ │ │ ├── CmdClearPermFlag.java │ │ │ │ │ │ └── CCPlyAccessCmd.java │ │ │ │ │ ├── GuiCmd.java │ │ │ │ │ ├── GiveCmd.java │ │ │ │ │ ├── AutoCmd.java │ │ │ │ │ ├── AlertCmd.java │ │ │ │ │ ├── ClaimCmd.java │ │ │ │ │ ├── UnclaimAllCmd.java │ │ │ │ │ ├── NameCmd.java │ │ │ │ │ ├── ShowCmd.java │ │ │ │ │ ├── InfoCmd.java │ │ │ │ │ ├── ShowClaimedCmd.java │ │ │ │ │ ├── ScanCmd.java │ │ │ │ │ ├── ListCmd.java │ │ │ │ │ └── HelpCmd.java │ │ │ │ │ └── admin │ │ │ │ │ ├── AdminUnclaimCmd.java │ │ │ │ │ ├── AdminReloadCmd.java │ │ │ │ │ ├── AdminUnclaimWorldCmd.java │ │ │ │ │ ├── AdminOverrideCmd.java │ │ │ │ │ └── AdminUnclaimAllCmd.java │ │ │ │ ├── rank │ │ │ │ ├── Rank.java │ │ │ │ └── RankHandler.java │ │ │ │ ├── flag │ │ │ │ └── CCFlags.java │ │ │ │ ├── layer │ │ │ │ ├── PlaceholderInitLayer.java │ │ │ │ └── PrereqsInitLayer.java │ │ │ │ ├── event │ │ │ │ ├── PlayerConnectionHandler.java │ │ │ │ └── PlayerEnterChunkEvent.java │ │ │ │ └── Econ.java │ │ └── claimchunk │ │ │ └── dependency │ │ │ └── de │ │ │ └── goldmensch │ │ │ └── commanddispatcher │ │ │ ├── annotations │ │ │ └── Description.java │ │ │ ├── ArraySets.java │ │ │ ├── exceptions │ │ │ └── CommandNotValidException.java │ │ │ ├── Executor.java │ │ │ ├── ArrayUtil.java │ │ │ └── subcommand │ │ │ └── SmartSubCommand.java │ └── resources │ │ ├── validParticleEffects.txt │ │ ├── config.yml │ │ └── plugin.yml └── test │ └── java │ └── com │ └── cjburkey │ └── claimchunk │ ├── EventTests.java │ ├── TestSemVerMee.java │ ├── InteractClassTests.java │ ├── PermFlagTests.java │ └── CCConfigTests.java ├── .gitignore ├── PRE_PUSH_CHECKLIST.md ├── LICENSE ├── GourceVisualization.md ├── CONTRIBUTING.md ├── gradlew.bat └── CODE_OF_CONDUCT.md /imgs/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjburkey01/ClaimChunk/HEAD/imgs/icon.png -------------------------------------------------------------------------------- /imgs/icon64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjburkey01/ClaimChunk/HEAD/imgs/icon64.png -------------------------------------------------------------------------------- /imgs/icon256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjburkey01/ClaimChunk/HEAD/imgs/icon256.png -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | ko_fi: cjburkey 4 | -------------------------------------------------------------------------------- /imgs/logo_carrier.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjburkey01/ClaimChunk/HEAD/imgs/logo_carrier.png -------------------------------------------------------------------------------- /changelogs/0.0.25-FIX1.md: -------------------------------------------------------------------------------- 1 | # ClaimChunk 0.0.25-FIX1 2 | 3 | Fixes: 4 | * Crash on first launch! Kinda a problem :) 5 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjburkey01/ClaimChunk/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /imgs/github-download-source.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjburkey01/ClaimChunk/HEAD/imgs/github-download-source.png -------------------------------------------------------------------------------- /req/google-java-format-1.22.0-all-deps.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjburkey01/ClaimChunk/HEAD/req/google-java-format-1.22.0-all-deps.jar -------------------------------------------------------------------------------- /lombok.config: -------------------------------------------------------------------------------- 1 | # This file is generated by the 'io.freefair.lombok' Gradle plugin 2 | config.stopBubbling = true 3 | lombok.getter.noIsPrefix = true 4 | -------------------------------------------------------------------------------- /changelogs/0.0.25-FIX3.md: -------------------------------------------------------------------------------- 1 | # ClaimChunk 0.0.25-FIX3 2 | 3 | Fixes: 4 | * My stupid update checker 5 | * Allow header customization for chunk list command in messages.json 6 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/gui/CCOpenGui.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.gui; 2 | 3 | import org.bukkit.inventory.Inventory; 4 | 5 | record CCOpenGui(ICCGui gui, Inventory inventory) {} 6 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/service/prereq/claim/IClaimPrereq.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.service.prereq.claim; 2 | 3 | import com.cjburkey.claimchunk.service.prereq.IPrereq; 4 | 5 | public interface IClaimPrereq extends IPrereq {} 6 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /changelogs/0.0.23-RC3.md: -------------------------------------------------------------------------------- 1 | # ClaimChunk 0.0.23-RC3 2 | 3 | Changes: 4 | * Update to the latest version of the bStats Metrics class 5 | * Fix null pointer when loading plugin and not converting old config values. 6 | 7 | As always please report any bugs you find to the [GitHub issue tracker](https://github.com/cjburkey01/ClaimChunk/issues) or our [Discord server](https://discord.gg/swW8xX665Z). 8 | -------------------------------------------------------------------------------- /changelogs/0.0.25-FIX2.md: -------------------------------------------------------------------------------- 1 | # ClaimChunk 0.0.25-FIX2 2 | 3 | Fixes: 4 | * Re-add the `claimchunk_am_trusted` placeholder 5 | * The placeholder works the same way, returning that the player is trusted if 6 | said player has *any* permissions granted by the owner for the chunk they're 7 | standing in (the chunk must be owned; the placeholder shows not trusted in 8 | unclaimed chunks to match pre-0.0.24 behavior). 9 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/data/sqlite/SqlDataChunk.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.data.sqlite; 2 | 3 | import javax.persistence.*; 4 | 5 | @Table(name = "chunk_data") 6 | public class SqlDataChunk { 7 | 8 | @Column(name = "chunk_world") 9 | String world; 10 | 11 | @Column(name = "chunk_x") 12 | int x; 13 | 14 | @Column(name = "chunk_z") 15 | int z; 16 | 17 | @Column(name = "owner_uuid") 18 | String uuid; 19 | } 20 | -------------------------------------------------------------------------------- /changelogs/0.0.23-RC2.md: -------------------------------------------------------------------------------- 1 | # ClaimChunk 0.0.23-RC2 2 | 3 | Changes: 4 | * Update to Spigot 1.18.1 5 | * Add config conversion! 6 | * Config values under the `protection` category of the config *should* be automatically converted and removed from your config.yml files. 7 | * Reduce debug spam 8 | 9 | As always please report any bugs you find to the [GitHub issue tracker](https://github.com/cjburkey01/ClaimChunk/issues) or our [Discord server](https://discord.gg/swW8xX665Z). 10 | -------------------------------------------------------------------------------- /src/main/java/claimchunk/dependency/de/goldmensch/commanddispatcher/annotations/Description.java: -------------------------------------------------------------------------------- 1 | package claimchunk.dependency.de.goldmensch.commanddispatcher.annotations; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.TYPE) 10 | public @interface Description { 11 | String value(); 12 | } 13 | -------------------------------------------------------------------------------- /changelogs/0.0.23-RC6.md: -------------------------------------------------------------------------------- 1 | # ClaimChunk 0.0.23-RC6 2 | 3 | Changes: 4 | * Add `DOOR` block class 5 | 6 | Main remaining changes that need to be made before 0.0.23 is fully released: 7 | * Proper PvP support (the current support doesn't leave much to customizability). 8 | 9 | As always please report any bugs (or minor changes that may still need to be made) you find to the [GitHub issue tracker](https://github.com/cjburkey01/ClaimChunk/issues) or our [Discord server](https://discord.gg/swW8xX665Z). 10 | -------------------------------------------------------------------------------- /changelogs/0.0.23-RC7.md: -------------------------------------------------------------------------------- 1 | # ClaimChunk 0.0.23-RC7 2 | 3 | Changes: 4 | * Add translation keys for command arguments. 5 | 6 | Main remaining changes that need to be made before 0.0.23 is fully released: 7 | * Proper PvP support (the current support doesn't leave much to customizability). 8 | 9 | As always please report any bugs (or minor changes that may still need to be made) you find to the [GitHub issue tracker](https://github.com/cjburkey01/ClaimChunk/issues) or our [Discord server](https://discord.gg/swW8xX665Z). 10 | -------------------------------------------------------------------------------- /changelogs/0.0.25.md: -------------------------------------------------------------------------------- 1 | # ClaimChunk 0.0.25 2 | 3 | This update fixes, once and for all, the data loss issues we've been seeing 4 | since I first created this plugin! 5 | 6 | Migration: 7 | * Should not require any extra work, keep your config the same for at least the first run. 8 | * 0.0.26 will remove the database section of the config, but leave it be for your first launch to make sure old data loads. 9 | * Prior data from JSON and from MySQL will be converted automatically to the SQLite data file, and JSON backups will be made for you in the `/plugins/ClaimChunk/data` directory. 10 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Versions** 11 | Spigot Version: 12 | ClaimChunk Version: 13 | 14 | **Describe the bug** 15 | A clear and concise description of what the bug is. 16 | 17 | **To Reproduce** 18 | Steps to reproduce the behavior: 19 | 1. Blah 20 | 2. Blah 21 | 22 | **Expected behavior** 23 | A clear and concise description of what you expected to happen. 24 | 25 | **Additional context** 26 | Add any other context about the problem here. 27 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "gradle" 9 | directory: "/" 10 | schedule: 11 | interval: "daily" 12 | 13 | - package-ecosystem: "github-actions" 14 | directory: "/" 15 | schedule: 16 | interval: "daily" 17 | -------------------------------------------------------------------------------- /src/main/java/claimchunk/dependency/de/goldmensch/commanddispatcher/ArraySets.java: -------------------------------------------------------------------------------- 1 | package claimchunk.dependency.de.goldmensch.commanddispatcher; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | import java.util.Set; 6 | 7 | public class ArraySets { 8 | public static @NotNull T[] getBiggest(@NotNull Set set) { 9 | @SuppressWarnings("unchecked") 10 | T[] biggest = (T[]) new Object[0]; 11 | 12 | for (T[] c : set) { 13 | if (c.length > biggest.length) { 14 | biggest = c; 15 | } 16 | } 17 | 18 | return biggest; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/player/SimplePlayerData.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.player; 2 | 3 | import java.util.UUID; 4 | 5 | public record SimplePlayerData(UUID player, String lastIgn, long lastOnlineTime) { 6 | 7 | @Override 8 | public boolean equals(Object o) { 9 | if (this == o) return true; 10 | if (o == null || getClass() != o.getClass()) return false; 11 | SimplePlayerData that = (SimplePlayerData) o; 12 | return lastOnlineTime == that.lastOnlineTime 13 | && player.equals(that.player) 14 | && lastIgn.equals(that.lastIgn); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/claimchunk/dependency/de/goldmensch/commanddispatcher/exceptions/CommandNotValidException.java: -------------------------------------------------------------------------------- 1 | package claimchunk.dependency.de.goldmensch.commanddispatcher.exceptions; 2 | 3 | import claimchunk.dependency.de.goldmensch.commanddispatcher.subcommand.SmartSubCommand; 4 | 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | public class CommandNotValidException extends RuntimeException { 8 | 9 | @java.io.Serial private static final long serialVersionUID = 17441953375440988L; 10 | 11 | public CommandNotValidException(@NotNull Class sub) { 12 | super("Command not valid, class: " + sub.getName()); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /changelogs/0.0.23-RC4.md: -------------------------------------------------------------------------------- 1 | # ClaimChunk 0.0.23-RC4 2 | 3 | Changes: 4 | * Remove unused `debug` from the `log` section of the config file as it was unused. 5 | * Update to Java 17. 6 | * Fix plugin reloading (fingers crossed! `/chunk admin reload` *should* work!). 7 | * Try to fix dragon eggs teleporting from claimed chunks. 8 | * Add `preventAdjacent` list to world profiles to prevent chest connections if owned by different players (or unclaimed into claimed). 9 | * If a message from the `messages.json` file is empty, it won't be sent. 10 | 11 | As always please report any bugs you find to the [GitHub issue tracker](https://github.com/cjburkey01/ClaimChunk/issues) or our [Discord server](https://discord.gg/swW8xX665Z). 12 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/data/sqlite/SqlClaimedChunk.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.data.sqlite; 2 | 3 | import com.cjburkey.claimchunk.chunk.ChunkPos; 4 | 5 | import org.sormula.annotation.Column; 6 | import org.sormula.annotation.ImplicitType; 7 | import org.sormula.annotation.Row; 8 | 9 | import java.util.UUID; 10 | 11 | @Row(tableName = "claimed_chunks") 12 | public class SqlClaimedChunk { 13 | 14 | @Column(primaryKey = true, name = "chunk_pos") 15 | @ImplicitType(translator = Translators.ChunkPosTranslator.class) 16 | public ChunkPos chunkPos; 17 | 18 | @Column(name = "owner_uuid") 19 | @ImplicitType(translator = Translators.UUIDTranslator.class) 20 | public UUID chunkOwner; 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/service/prereq/claim/UnclaimedPrereq.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.service.prereq.claim; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | import java.util.Optional; 6 | 7 | public class UnclaimedPrereq implements IClaimPrereq { 8 | 9 | @Override 10 | public int getWeight() { 11 | return 0; 12 | } 13 | 14 | @Override 15 | public boolean getPassed(@NotNull PrereqClaimData data) { 16 | return !data.claimChunk().getChunkHandler().isClaimed(data.chunk()); 17 | } 18 | 19 | @Override 20 | public Optional getErrorMessage(@NotNull PrereqClaimData data) { 21 | return Optional.of(data.claimChunk().getMessages().claimAlreadyOwned); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/service/prereq/claim/MaxChunksPrereq.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.service.prereq.claim; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | import java.util.Optional; 6 | 7 | public class MaxChunksPrereq implements IClaimPrereq { 8 | 9 | @Override 10 | public int getWeight() { 11 | return 200; 12 | } 13 | 14 | @Override 15 | public boolean getPassed(@NotNull PrereqClaimData data) { 16 | return !(data.maxClaimed() > 0 && data.claimedBefore() >= data.maxClaimed()); 17 | } 18 | 19 | @Override 20 | public Optional getErrorMessage(@NotNull PrereqClaimData data) { 21 | return Optional.of(data.claimChunk().getMessages().claimTooMany); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/service/prereq/claim/WorldPrereq.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.service.prereq.claim; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | import java.util.Optional; 6 | 7 | public class WorldPrereq implements IClaimPrereq { 8 | 9 | @Override 10 | public int getWeight() { 11 | return -50; 12 | } 13 | 14 | @Override 15 | public boolean getPassed(@NotNull PrereqClaimData data) { 16 | return data.claimChunk().getProfileHandler().getProfile(data.chunk().world()).enabled; 17 | } 18 | 19 | @Override 20 | public Optional getErrorMessage(@NotNull PrereqClaimData data) { 21 | return Optional.of(data.claimChunk().getMessages().claimWorldDisabled); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp 12 | 13 | # Package Files 14 | *.war 15 | *.ear 16 | *.zip 17 | *.tar.gz 18 | *.rar 19 | 20 | # Mac 21 | *.DS_Store 22 | *__MACOSX 23 | 24 | # Virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 25 | hs_err_pid* 26 | target 27 | .classpath 28 | .project 29 | .settings/ 30 | .settings/* 31 | 32 | # Intellij 33 | .idea 34 | run 35 | out 36 | 37 | # The debug script in IDE 38 | !/run/start.sh 39 | 40 | # Gradle 41 | .gradle 42 | build 43 | OUT 44 | 45 | # Gource 46 | my_captions.txt 47 | source_visual.ppm 48 | source_visual.mp4 49 | 50 | # Leftover from tests 51 | tmp/ 52 | *.tmp.sqlite3 53 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/service/prereq/claim/PermissionPrereq.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.service.prereq.claim; 2 | 3 | import com.cjburkey.claimchunk.Utils; 4 | 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | import java.util.Optional; 8 | 9 | public class PermissionPrereq implements IClaimPrereq { 10 | 11 | @Override 12 | public int getWeight() { 13 | // Try to check permissions as early as possible 14 | return -100; 15 | } 16 | 17 | @Override 18 | public boolean getPassed(@NotNull PrereqClaimData data) { 19 | return Utils.hasPerm(data.player(), true, "claim"); 20 | } 21 | 22 | @Override 23 | public Optional getErrorMessage(@NotNull PrereqClaimData data) { 24 | return Optional.of(data.claimChunk().getMessages().claimNoPerm); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/transition/Pre0024FullPlayerData.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.transition; 2 | 3 | import java.util.Set; 4 | import java.util.UUID; 5 | 6 | public class Pre0024FullPlayerData { 7 | 8 | public final UUID player; 9 | public final String lastIgn; 10 | public final Set permitted; 11 | public String chunkName; 12 | public long lastOnlineTime; 13 | public boolean alert; 14 | 15 | public Pre0024FullPlayerData( 16 | UUID player, 17 | String lastIgn, 18 | Set permitted, 19 | String chunkName, 20 | long lastOnlineTime, 21 | boolean alert) { 22 | this.player = player; 23 | this.lastIgn = lastIgn; 24 | this.permitted = permitted; 25 | this.chunkName = chunkName; 26 | this.lastOnlineTime = lastOnlineTime; 27 | this.alert = alert; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/test/java/com/cjburkey/claimchunk/EventTests.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk; 2 | 3 | import org.junit.jupiter.api.AfterEach; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.Test; 6 | import org.mockbukkit.mockbukkit.MockBukkit; 7 | import org.mockbukkit.mockbukkit.ServerMock; 8 | 9 | // TODO: MOCKING YAY! 10 | public class EventTests { 11 | 12 | private ServerMock server; 13 | private ClaimChunk plugin; 14 | 15 | @BeforeEach 16 | public void setUp() { 17 | // Start the mock server 18 | server = MockBukkit.mock(); 19 | // Load your plugin 20 | plugin = MockBukkit.load(ClaimChunk.class); 21 | } 22 | 23 | @AfterEach 24 | public void tearDown() { 25 | // Stop the mock server 26 | MockBukkit.unmock(); 27 | } 28 | 29 | @Test 30 | public void thisTestWillFail() { 31 | // Perform your test 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/com/cjburkey/claimchunk/TestSemVerMee.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk; 2 | 3 | import com.cjburkey.claimchunk.update.SemVer; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | class TestSemVerMee { 8 | 9 | @Test 10 | void testVersions() { 11 | SemVer v0022 = new SemVer(0, 0, 22, null); 12 | SemVer v0024 = new SemVer(0, 0, 24, null); 13 | SemVer v0024FIX1 = new SemVer(0, 0, 24, "FIX1"); 14 | SemVer v0024FIX2 = new SemVer(0, 0, 24, "FIX2"); 15 | SemVer v0024RC1 = new SemVer(0, 0, 24, "RC1"); 16 | SemVer v0024RC2 = new SemVer(0, 0, 24, "RC2"); 17 | 18 | assert v0024.isNewerThan(v0022); 19 | assert v0024FIX1.isNewerThan(v0024); 20 | assert v0024FIX1.isNewerThan(v0024RC1); 21 | assert v0024FIX2.isNewerThan(v0024FIX1); 22 | assert v0024RC2.isNewerThan(v0024RC1); 23 | assert !v0024RC1.isNewerThan(v0024RC2); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/player/AdminOverride.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.player; 2 | 3 | import java.util.HashSet; 4 | import java.util.UUID; 5 | 6 | public class AdminOverride { 7 | 8 | // Use a HashSet so we don't have to iterate over each player in the list 9 | private final HashSet overriders = new HashSet<>(); 10 | 11 | /** 12 | * Toggles whether the given player has permission to override admin claims. 13 | * 14 | * @param admin The player's unique ID. 15 | * @return Whether the player *now has* access to admin override 16 | */ 17 | public boolean toggle(UUID admin) { 18 | if (overriders.remove(admin)) { 19 | return false; 20 | } 21 | return overriders.add(admin); 22 | } 23 | 24 | public void remove(UUID admin) { 25 | overriders.remove(admin); 26 | } 27 | 28 | public boolean hasOverride(UUID admin) { 29 | return overriders.contains(admin); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/config/ccconfig/ICCConfigSerializable.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.config.ccconfig; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | /** Represents an object that may be serialized into and from a ClaimChunk config file. */ 6 | public interface ICCConfigSerializable { 7 | 8 | /** 9 | * Load the value at the provided key into this instance of this class, assuming it is possible. 10 | * 11 | * @param config The ClaimChunk config from which to load this value. 12 | * @param key The key to search. 13 | */ 14 | void fromCCConfig(@NotNull CCConfig config, @NotNull String key); 15 | 16 | /** 17 | * Stores this object's value at the particular key inside the provided ClaimChunk config. 18 | * 19 | * @param config The ClaimChunk config to which to insert this value. 20 | * @param key The key at which to enter this value. 21 | */ 22 | void toCCConfig(@NotNull CCConfig config, @NotNull String key); 23 | } 24 | -------------------------------------------------------------------------------- /PRE_PUSH_CHECKLIST.md: -------------------------------------------------------------------------------- 1 | # Pre/Post-push Checklist 2 | 3 | Things to make sure I do before I push a new release version: 4 | - `build.gradle.kts` 5 | - [ ] Update `THIS_VERSION` 6 | - [ ] Update `LIVE_VERSION` 7 | - [ ] Update [wiki](https://claimchunk.cjburkey.com/) with new information. 8 | - [ ] Create changelog 9 | - Building 10 | - [ ] Run `./gradlew clean googleFormat` 11 | - [ ] Run `./gradlew build` 12 | - Run separate from others because of stupid error 13 | - Upload `OUT/claimchunk-VERSION.jar` as GitHub release with changelog. 14 | - Post push 15 | - Add new release on [Spigot](https://www.spigotmc.org/resources/claimchunk.44458/) with changelog. 16 | - Add new release on [Modrinth](https://modrinth.com/plugin/claimchunk) with changelog. 17 | - Push to Maven Central: 18 | - `./gradlew publishToMavenCentral --no-configuration-cache` 19 | - Publish release on [Maven Central](https://central.sonatype.com/artifact/com.cjburkey.claimchunk/claimchunk). 20 | - Done! 21 | 22 | Make sure to be able to provide support and bugfix releases shortly after :/ 23 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/data/sqlite/SqlFlagEntry.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.data.sqlite; 2 | 3 | import com.cjburkey.claimchunk.chunk.ChunkPos; 4 | 5 | import org.jetbrains.annotations.Nullable; 6 | import org.sormula.annotation.Column; 7 | import org.sormula.annotation.ImplicitType; 8 | import org.sormula.annotation.Row; 9 | 10 | import java.util.UUID; 11 | 12 | @Row(tableName = "flag_permissions") 13 | public class SqlFlagEntry { 14 | 15 | @Column(name = "player_uuid") 16 | @ImplicitType(translator = Translators.UUIDTranslator.class) 17 | public UUID playerUUID; 18 | 19 | @Column(name = "other_player_uuid") 20 | @ImplicitType(translator = Translators.UUIDTranslator.class) 21 | public @Nullable UUID otherPlayerUUID; 22 | 23 | @Column(name = "chunk_pos") 24 | @ImplicitType(translator = Translators.ChunkPosTranslator.class) 25 | public @Nullable ChunkPos chunkPos; 26 | 27 | @Column(name = "flag_name") 28 | public String flagName; 29 | 30 | @Column(name = "allow_deny") 31 | public boolean allowDeny; 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/claimchunk/dependency/de/goldmensch/commanddispatcher/Executor.java: -------------------------------------------------------------------------------- 1 | package claimchunk.dependency.de.goldmensch.commanddispatcher; 2 | 3 | import org.bukkit.command.CommandSender; 4 | import org.bukkit.entity.Player; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | public enum Executor { 8 | /*** 9 | * The command can be executed only from the console. 10 | */ 11 | CONSOLE, 12 | /*** 13 | * The command can be executed only from a player. 14 | */ 15 | PLAYER, 16 | /*** 17 | * The command can be executed from a player or the console. 18 | */ 19 | CONSOLE_PLAYER; 20 | 21 | /*** 22 | * Gives you the ExecutorLevel belonging to the CommandSender. 23 | * @param sender The CommandSender 24 | * @return The ExecutorLevel that corresponds to the CommandSender. 25 | */ 26 | public static @NotNull Executor fromSender(@NotNull CommandSender sender) { 27 | if (sender instanceof Player) { 28 | return Executor.PLAYER; 29 | } 30 | return Executor.CONSOLE; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/worldguard/WorldGuardHandler.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.worldguard; 2 | 3 | import static com.cjburkey.claimchunk.worldguard.WorldGuardApi.*; 4 | 5 | import com.cjburkey.claimchunk.ClaimChunk; 6 | import com.cjburkey.claimchunk.chunk.ChunkPos; 7 | 8 | /** Safe wrapper for {@link com.cjburkey.claimchunk.worldguard.WorldGuardApi} */ 9 | public class WorldGuardHandler { 10 | 11 | private static boolean loaded = false; 12 | 13 | public static boolean init(ClaimChunk claimChunk) { 14 | try { 15 | return (loaded = _init(claimChunk)); 16 | } catch (NoClassDefFoundError ignored) { 17 | } 18 | return false; 19 | } 20 | 21 | public static boolean isAllowedClaim(ClaimChunk claimChunk, ChunkPos chunk) { 22 | try { 23 | // If the WorldGuard api never loaded, just allow the claim 24 | return (!loaded || _isAllowedClaim(claimChunk, chunk)); 25 | } catch (NoClassDefFoundError ignored) { 26 | } 27 | // This should never happen, but better safe than sorry 28 | return true; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-2024 CJ Burkey 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/service/prereq/claim/WorldGuardPrereq.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.service.prereq.claim; 2 | 3 | import com.cjburkey.claimchunk.Utils; 4 | import com.cjburkey.claimchunk.worldguard.WorldGuardHandler; 5 | 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | import java.util.Optional; 9 | 10 | public class WorldGuardPrereq implements IClaimPrereq { 11 | 12 | @Override 13 | public int getWeight() { 14 | return 100; 15 | } 16 | 17 | @Override 18 | public boolean getPassed(@NotNull PrereqClaimData data) { 19 | boolean allowedToClaimWG = 20 | WorldGuardHandler.isAllowedClaim(data.claimChunk(), data.chunk()); 21 | boolean adminOverride = data.claimChunk().getConfigHandler().getAllowWGAdminOverride(); 22 | boolean hasAdmin = Utils.hasAdmin(data.player()); 23 | 24 | return allowedToClaimWG || (adminOverride && hasAdmin); 25 | } 26 | 27 | @Override 28 | public Optional getErrorMessage(@NotNull PrereqClaimData data) { 29 | return Optional.of(data.claimChunk().getMessages().claimLocationBlock); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/claimchunk/dependency/de/goldmensch/commanddispatcher/ArrayUtil.java: -------------------------------------------------------------------------------- 1 | package claimchunk.dependency.de.goldmensch.commanddispatcher; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | public class ArrayUtil { 6 | 7 | private ArrayUtil() {} 8 | 9 | public static boolean startWith(@NotNull T[] r, @NotNull T[] s) { 10 | if (s.length > r.length) return false; 11 | for (int i = 0; i < s.length; i++) { 12 | if (!r[i].equals(s[i])) { 13 | return false; 14 | } 15 | } 16 | return true; 17 | } 18 | 19 | public static @NotNull String[] toLowerCase(@NotNull String[] a) { 20 | var lowerArray = new String[a.length]; 21 | for (int i = 0; i < a.length; i++) { 22 | lowerArray[i] = a[i].toLowerCase(); 23 | } 24 | return lowerArray; 25 | } 26 | 27 | public static @NotNull String buildString(@NotNull String[] a) { 28 | var builder = new StringBuilder(); 29 | for (String c : a) { 30 | builder.append(c); 31 | builder.append(" "); 32 | } 33 | return builder.toString().trim(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/api/layer/IClaimChunkLayer.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.api.layer; 2 | 3 | import com.cjburkey.claimchunk.api.IClaimChunkPlugin; 4 | 5 | /** 6 | * ClaimChunk will be separated into different layers responsible for different things, this 7 | * represents a single one. 8 | */ 9 | public interface IClaimChunkLayer { 10 | 11 | /** 12 | * Called when ClaimChunk is enabled; this is a good place to register event handlers. 13 | * 14 | * @param claimChunk The instance of ClaimChunk. 15 | * @return Whether this layer was successfully enabled. 16 | */ 17 | boolean onEnable(IClaimChunkPlugin claimChunk); 18 | 19 | /** 20 | * Called when ClaimChunk is disabled. 21 | * 22 | * @param claimChunk The instance of ClaimChunk. 23 | */ 24 | void onDisable(IClaimChunkPlugin claimChunk); 25 | 26 | /** 27 | * Returns an ordering ID that indicates when this layer should execute. Lower values are 28 | * executed first. Changes to this value will be ignored after insertion. 29 | * 30 | * @return The ordering ID to compare to other layers (lower = sooner). 31 | */ 32 | int getOrderId(); 33 | } 34 | -------------------------------------------------------------------------------- /changelogs/0.0.23-RC5.md: -------------------------------------------------------------------------------- 1 | # ClaimChunk 0.0.23-RC5 2 | 3 | Changes: 4 | * Downgrade to Java 16 (Java 17 becomes too restrictive for older version support) 5 | * It should be noted that ClaimChunk will always try to be up-to-date with Spigot or Minecraft API changes. Generally, new versions should support the previous release or two, but unless major issues arise, there aren't likely to be updates for older versions of the game. 6 | * Make some internal structural changes (Most APIs should be safe, but be sure to double-check that things still function as intended. 7 | * Disable debug spam being overridden in versions with an extra marker (such as snapshot or RC). 8 | * Add `my_remaining_claims` placeholder that displays the number of claims a player can make 9 | * (Provides the value of `my_max_claims - my_claims`) 10 | 11 | Main remaining changes that need to be made before 0.0.23 is fully released: 12 | * Proper PvP support (the current support doesn't leave much to customizability). 13 | 14 | As always please report any bugs (or minor changes that may still need to be made) you find to the [GitHub issue tracker](https://github.com/cjburkey01/ClaimChunk/issues) or our [Discord server](https://discord.gg/swW8xX665Z). 15 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/service/prereq/claim/PrereqClaimData.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.service.prereq.claim; 2 | 3 | import com.cjburkey.claimchunk.ClaimChunk; 4 | import com.cjburkey.claimchunk.chunk.ChunkPos; 5 | 6 | import org.bukkit.entity.Player; 7 | import org.jetbrains.annotations.NotNull; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | import java.util.UUID; 11 | 12 | public record PrereqClaimData( 13 | ClaimChunk claimChunk, 14 | ChunkPos chunk, 15 | UUID playerId, 16 | Player player, 17 | int claimedBefore, 18 | int maxClaimed, 19 | int freeClaims) { 20 | 21 | public PrereqClaimData( 22 | @NotNull ClaimChunk claimChunk, 23 | @NotNull ChunkPos chunk, 24 | @NotNull UUID playerId, 25 | @Nullable Player player) { 26 | this( 27 | claimChunk, 28 | chunk, 29 | playerId, 30 | player, 31 | claimChunk.getChunkHandler().getClaimed(playerId), 32 | claimChunk.getRankHandler().getMaxClaimsForPlayer(player), 33 | claimChunk.getConfigHandler().getFirstFreeChunks()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/data/sqlite/SqlDataPlayer.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.data.sqlite; 2 | 3 | import org.sormula.annotation.Row; 4 | 5 | import javax.persistence.Column; 6 | import javax.persistence.Id; 7 | import javax.persistence.Table; 8 | 9 | @Table(name = "player_data") 10 | @Row(tableName = "player_data") 11 | public class SqlDataPlayer { 12 | 13 | @Id 14 | @Column(name = "player_uuid") 15 | @org.sormula.annotation.Column(primaryKey = true, name = "player_uuid") 16 | public String uuid; 17 | 18 | @Column(name = "last_ign") 19 | @org.sormula.annotation.Column(name = "lastIgn") 20 | public String lastIgn; 21 | 22 | @Column(name = "chunk_name") 23 | @org.sormula.annotation.Column(name = "chunk_name") 24 | public String chunkName; 25 | 26 | @Column(name = "last_online_time") 27 | @org.sormula.annotation.Column(name = "last_online_time") 28 | public long lastOnlineTime; 29 | 30 | @Column(name = "alerts_enabled") 31 | @org.sormula.annotation.Column(name = "alerts_enabled") 32 | public boolean alert; 33 | 34 | @Column(name = "extra_max_claims") 35 | @org.sormula.annotation.Column(name = "extra_max_claims") 36 | public int extraMaxClaims; 37 | } 38 | -------------------------------------------------------------------------------- /GourceVisualization.md: -------------------------------------------------------------------------------- 1 | ### Gource Source Visualization 2 | 3 | The following is a little guide to generating a Gource source visualization for ClaimChunk. There isn't a good reason to do this other than it looks cool (this guide is more for my own personal reference). 4 | 5 | Gource can be found [**here**](https://github.com/acaudwell/Gource). 6 | 7 | #### Output the name of the commits as captions: 8 | `git log --pretty='%at|%s' | sort -n > my_captions.txt` 9 | 10 | #### Open and start the visualizer: 11 | `gource -1920x1080 -e 0.05 --key --title "ClaimChunk" --seconds-per-day 3 --auto-skip-seconds 1 --file-idle-time 0 --max-file-lag 1 --caption-file my_captions.txt --caption-duration 2 --output-framerate 60 --output-ppm-stream source_visual.ppm` 12 | > Note: You can press escape to exit the visualization. 13 | 14 | The
15 | `--output-framerate 60 --output-ppm-stream source_visual.ppm`
16 | can be removed if you don't want to generate a video) 17 | 18 | #### Use FFMPEG to convert to MP4: 19 | `ffmpeg -y -r 60 -f image2pipe -vcodec ppm -i source_visual.ppm -vcodec libx264 -preset medium -pix_fmt yuv420p -crf 1 -threads 0 -bf 0 source_visual.mp4` 20 | 21 | #### Finally, delete temporary files (`del` instead of `rm` on Windows): 22 | `rm my_captions.txt` & `rm source_visual.ppm` 23 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/chunk/DataChunk.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.chunk; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | import java.util.*; 6 | 7 | /** 8 | * @param chunk The position of the chunk. 9 | * @param player The UUID of the owning player. 10 | * @param defaultFlags Which flags the owner has granted to other players. 11 | * @param specificFlags Flags that the owner has granted to specific other users. 12 | */ 13 | public record DataChunk( 14 | @NotNull ChunkPos chunk, 15 | @NotNull UUID player, 16 | @NotNull HashMap defaultFlags, 17 | @NotNull HashMap> specificFlags) { 18 | public DataChunk(@NotNull ChunkPos chunk, @NotNull UUID player) { 19 | this(chunk, player, new HashMap<>(), new HashMap<>()); 20 | } 21 | 22 | @Override 23 | public boolean equals(Object o) { 24 | if (this == o) return true; 25 | if (o == null || getClass() != o.getClass()) return false; 26 | DataChunk dataChunk = (DataChunk) o; 27 | return Objects.equals(chunk, dataChunk.chunk) && Objects.equals(player, dataChunk.player); 28 | } 29 | 30 | @Override 31 | public int hashCode() { 32 | return Objects.hash(chunk, player); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/smartcommand/sub/ply/UnclaimCmd.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.smartcommand.sub.ply; 2 | 3 | import claimchunk.dependency.de.goldmensch.commanddispatcher.Executor; 4 | 5 | import com.cjburkey.claimchunk.ClaimChunk; 6 | import com.cjburkey.claimchunk.smartcommand.CCSubCommand; 7 | 8 | import org.bukkit.command.CommandSender; 9 | import org.bukkit.entity.Player; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | public class UnclaimCmd extends CCSubCommand { 14 | 15 | public UnclaimCmd(ClaimChunk claimChunk) { 16 | super(claimChunk, Executor.PLAYER, true, "player", "unclaim"); 17 | } 18 | 19 | @Override 20 | public @Nullable String getDescription() { 21 | return claimChunk.getMessages().cmdUnclaim; 22 | } 23 | 24 | @Override 25 | public CCArg[] getPermittedArguments() { 26 | return new CCArg[0]; 27 | } 28 | 29 | @Override 30 | public int getRequiredArguments() { 31 | return 0; 32 | } 33 | 34 | @Override 35 | public boolean onCall(@NotNull String cmdUsed, @NotNull CommandSender executor, String[] args) { 36 | claimChunk.getMainHandler().unclaimChunk(false, false, (Player) executor); 37 | return true; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/config/ccconfig/CCConfigParseError.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.config.ccconfig; 2 | 3 | public final class CCConfigParseError { 4 | 5 | public final int startLine; 6 | public final int startIndex; 7 | public final int endLine; 8 | public final int endIndex; 9 | public final String source; 10 | public final String cause; 11 | 12 | public CCConfigParseError( 13 | int startLine, int startIndex, int endLine, int endIndex, String source, String cause) { 14 | this.startLine = startLine; 15 | this.startIndex = startIndex; 16 | this.endLine = endLine; 17 | this.endIndex = endIndex; 18 | this.source = source; 19 | this.cause = cause; 20 | } 21 | 22 | @Override 23 | public String toString() { 24 | return "CCConfigParseError { startLine=" 25 | + startLine 26 | + ", startIndex=" 27 | + startIndex 28 | + ", endLine=" 29 | + endLine 30 | + ", endIndex=" 31 | + endIndex 32 | + ", source='" 33 | + source 34 | + '\'' 35 | + ", cause='" 36 | + cause 37 | + "' }"; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/smartcommand/sub/admin/AdminUnclaimCmd.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.smartcommand.sub.admin; 2 | 3 | import claimchunk.dependency.de.goldmensch.commanddispatcher.Executor; 4 | 5 | import com.cjburkey.claimchunk.ClaimChunk; 6 | import com.cjburkey.claimchunk.smartcommand.CCSubCommand; 7 | 8 | import org.bukkit.command.CommandSender; 9 | import org.bukkit.entity.Player; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | public class AdminUnclaimCmd extends CCSubCommand { 14 | 15 | public AdminUnclaimCmd(ClaimChunk claimChunk) { 16 | super(claimChunk, Executor.PLAYER, false, "admin"); 17 | } 18 | 19 | public @Nullable String getDescription() { 20 | return claimChunk.getMessages().cmdAdminUnclaim; 21 | } 22 | 23 | @Override 24 | public CCArg[] getPermittedArguments() { 25 | return new CCArg[0]; 26 | } 27 | 28 | @Override 29 | public int getRequiredArguments() { 30 | return 0; 31 | } 32 | 33 | @Override 34 | public boolean onCall(@NotNull String cmdUsed, @NotNull CommandSender executor, String[] args) { 35 | var player = (Player) executor; 36 | claimChunk.getMainHandler().unclaimChunk(true, false, player); 37 | return true; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/smartcommand/sub/ply/flags/CmdSetPermFlag.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.smartcommand.sub.ply.flags; 2 | 3 | import com.cjburkey.claimchunk.ClaimChunk; 4 | import com.cjburkey.claimchunk.chunk.ChunkPos; 5 | import com.cjburkey.claimchunk.i18n.V2JsonMessages; 6 | 7 | import org.bukkit.entity.Player; 8 | import org.jetbrains.annotations.NotNull; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | import java.util.UUID; 12 | 13 | /** 14 | * @since 0.0.26 15 | */ 16 | public class CmdSetPermFlag extends CCPlyAccessCmd { 17 | 18 | public CmdSetPermFlag(@NotNull ClaimChunk claimChunk, boolean forPlayer, boolean forChunk) { 19 | super(claimChunk, forPlayer, forChunk); 20 | } 21 | 22 | @Override 23 | protected boolean handleAccess( 24 | @NotNull Player caller, 25 | @Nullable UUID otherPlayer, 26 | @Nullable ChunkPos chunkPos, 27 | @NotNull String[] arguments) { 28 | return true; 29 | } 30 | 31 | @Override 32 | public @NotNull String getDescription() { 33 | V2JsonMessages msg = claimChunk.getMessages(); 34 | return describe( 35 | msg.cmdPermFlagPlyChunkSet, 36 | msg.cmdPermFlagPlySet, 37 | msg.cmdPermFlagChunkSet, 38 | msg.cmdPermFlagGlobalSet); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/smartcommand/sub/ply/flags/CmdViewPermFlag.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.smartcommand.sub.ply.flags; 2 | 3 | import com.cjburkey.claimchunk.ClaimChunk; 4 | import com.cjburkey.claimchunk.chunk.ChunkPos; 5 | import com.cjburkey.claimchunk.i18n.V2JsonMessages; 6 | 7 | import org.bukkit.entity.Player; 8 | import org.jetbrains.annotations.NotNull; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | import java.util.UUID; 12 | 13 | /** 14 | * @since 0.0.26 15 | */ 16 | public class CmdViewPermFlag extends CCPlyAccessCmd { 17 | 18 | public CmdViewPermFlag(@NotNull ClaimChunk claimChunk, boolean forPlayer, boolean forChunk) { 19 | super(claimChunk, forPlayer, forChunk); 20 | } 21 | 22 | @Override 23 | protected boolean handleAccess( 24 | @NotNull Player caller, 25 | @Nullable UUID otherPlayer, 26 | @Nullable ChunkPos chunkPos, 27 | @NotNull String[] arguments) { 28 | return true; 29 | } 30 | 31 | @Override 32 | public @NotNull String getDescription() { 33 | V2JsonMessages msg = claimChunk.getMessages(); 34 | return describe( 35 | msg.cmdPermFlagPlyChunkList, 36 | msg.cmdPermFlagPlyList, 37 | msg.cmdPermFlagChunkList, 38 | msg.cmdPermFlagGlobalList); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/smartcommand/sub/ply/flags/CmdClearPermFlag.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.smartcommand.sub.ply.flags; 2 | 3 | import com.cjburkey.claimchunk.ClaimChunk; 4 | import com.cjburkey.claimchunk.chunk.ChunkPos; 5 | import com.cjburkey.claimchunk.i18n.V2JsonMessages; 6 | 7 | import org.bukkit.entity.Player; 8 | import org.jetbrains.annotations.NotNull; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | import java.util.UUID; 12 | 13 | /** 14 | * @since 0.0.26 15 | */ 16 | public class CmdClearPermFlag extends CCPlyAccessCmd { 17 | 18 | public CmdClearPermFlag(@NotNull ClaimChunk claimChunk, boolean forPlayer, boolean forChunk) { 19 | super(claimChunk, forPlayer, forChunk); 20 | } 21 | 22 | @Override 23 | protected boolean handleAccess( 24 | @NotNull Player caller, 25 | @Nullable UUID otherPlayer, 26 | @Nullable ChunkPos chunkPos, 27 | @NotNull String[] arguments) { 28 | return true; 29 | } 30 | 31 | @Override 32 | public @NotNull String getDescription() { 33 | V2JsonMessages msg = claimChunk.getMessages(); 34 | return describe( 35 | msg.cmdPermFlagPlyChunkClear, 36 | msg.cmdPermFlagPlyClear, 37 | msg.cmdPermFlagChunkClear, 38 | msg.cmdPermFlagGlobalClear); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/chunk/AutoClaimHandler.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.chunk; 2 | 3 | import org.bukkit.entity.Player; 4 | 5 | import java.util.HashSet; 6 | import java.util.UUID; 7 | 8 | // This is not saved across server launches 9 | public class AutoClaimHandler { 10 | 11 | // Keep a temporary list (set) of all players with auto claiming enabled 12 | private static final HashSet current = new HashSet<>(); 13 | 14 | public static boolean inList(Player ply) { 15 | // Check if the player is within the list 16 | return current.contains(ply.getUniqueId()); 17 | } 18 | 19 | @SuppressWarnings("unused") 20 | private static boolean enable(Player ply) { 21 | // Enable for the provided player 22 | return current.add(ply.getUniqueId()); 23 | } 24 | 25 | public static boolean disable(Player ply) { 26 | // Disable for the provided player 27 | return current.remove(ply.getUniqueId()); 28 | } 29 | 30 | /** 31 | * Toggles whether or not the player has autoclaim enabled. 32 | * 33 | * @param ply The player to toggle. 34 | * @return Whether or not the mode is NOW enabled. 35 | */ 36 | public static boolean toggle(Player ply) { 37 | // Toggle for the provided player 38 | if (disable(ply)) return false; 39 | return enable(ply); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/smartcommand/sub/ply/GuiCmd.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.smartcommand.sub.ply; 2 | 3 | import claimchunk.dependency.de.goldmensch.commanddispatcher.Executor; 4 | 5 | import com.cjburkey.claimchunk.ClaimChunk; 6 | import com.cjburkey.claimchunk.gui.screens.MainMenu; 7 | import com.cjburkey.claimchunk.smartcommand.CCSubCommand; 8 | 9 | import org.bukkit.command.CommandSender; 10 | import org.bukkit.entity.Player; 11 | import org.jetbrains.annotations.NotNull; 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | /** 15 | * @since 0.0.26 16 | */ 17 | public class GuiCmd extends CCSubCommand { 18 | 19 | public GuiCmd(ClaimChunk claimChunk) { 20 | super(claimChunk, Executor.PLAYER, true, "player"); 21 | } 22 | 23 | @Override 24 | public @Nullable String getDescription() { 25 | return claimChunk.getMessages().cmdGui; 26 | } 27 | 28 | @Override 29 | public CCArg[] getPermittedArguments() { 30 | return new CCArg[0]; 31 | } 32 | 33 | @Override 34 | public int getRequiredArguments() { 35 | return 0; 36 | } 37 | 38 | @Override 39 | public boolean onCall(@NotNull String cmdUsed, @NotNull CommandSender executor, String[] args) { 40 | claimChunk.getGuiHandler().openOrRefreshGui(new MainMenu(claimChunk, (Player) executor)); 41 | return true; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/com/cjburkey/claimchunk/InteractClassTests.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import com.cjburkey.claimchunk.flag.CCInteractClasses; 6 | 7 | import org.bukkit.configuration.file.YamlConfiguration; 8 | import org.bukkit.entity.EntityType; 9 | import org.junit.jupiter.api.Test; 10 | 11 | import java.util.List; 12 | import java.util.Objects; 13 | 14 | @SuppressWarnings("unchecked") 15 | public class InteractClassTests { 16 | 17 | @Test 18 | void testToYml() { 19 | CCInteractClasses classes = new CCInteractClasses(false); 20 | classes.addEntityClass("VEHICLES", EntityType.MINECART, EntityType.FURNACE_MINECART); 21 | 22 | YamlConfiguration yaml = classes.toYaml(); 23 | List entities = 24 | (List) Objects.requireNonNull(yaml.getList("entityClasses.VEHICLES")); 25 | assertEquals(2, entities.size()); 26 | assert entities.contains("MINECART") && entities.contains("FURNACE_MINECART"); 27 | } 28 | 29 | @Test 30 | void testDefaults() { 31 | CCInteractClasses classes = new CCInteractClasses(true); 32 | YamlConfiguration yaml = classes.toYaml(); 33 | List entities = 34 | (List) 35 | Objects.requireNonNull(yaml.getList("entityClasses.CONTAINER_ENTITIES")); 36 | assert entities.contains("CHEST_MINECART"); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/smartcommand/sub/ply/GiveCmd.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.smartcommand.sub.ply; 2 | 3 | import claimchunk.dependency.de.goldmensch.commanddispatcher.Executor; 4 | 5 | import com.cjburkey.claimchunk.ClaimChunk; 6 | import com.cjburkey.claimchunk.smartcommand.CCSubCommand; 7 | 8 | import org.bukkit.command.CommandSender; 9 | import org.bukkit.entity.Player; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | /** 14 | * @since 0.0.23 15 | */ 16 | public class GiveCmd extends CCSubCommand { 17 | 18 | public GiveCmd(ClaimChunk claimChunk) { 19 | super(claimChunk, Executor.PLAYER, true, "player", "give"); 20 | } 21 | 22 | @Override 23 | public @Nullable String getDescription() { 24 | return claimChunk.getMessages().cmdGive; 25 | } 26 | 27 | @Override 28 | public CCArg[] getPermittedArguments() { 29 | return new CCArg[] { 30 | new CCArg(claimChunk.getMessages().argPlayer, CCAutoComplete.OFFLINE_PLAYER), 31 | }; 32 | } 33 | 34 | @Override 35 | public int getRequiredArguments() { 36 | return 1; 37 | } 38 | 39 | @Override 40 | public boolean onCall(@NotNull String cmdUsed, @NotNull CommandSender executor, String[] args) { 41 | var player = (Player) executor; 42 | claimChunk.getMainHandler().giveChunk(player, player.getLocation().getChunk(), args[0]); 43 | return true; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/smartcommand/sub/admin/AdminReloadCmd.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.smartcommand.sub.admin; 2 | 3 | import claimchunk.dependency.de.goldmensch.commanddispatcher.Executor; 4 | 5 | import com.cjburkey.claimchunk.ClaimChunk; 6 | import com.cjburkey.claimchunk.Utils; 7 | import com.cjburkey.claimchunk.smartcommand.CCSubCommand; 8 | 9 | import org.bukkit.command.CommandSender; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | public class AdminReloadCmd extends CCSubCommand { 14 | 15 | public AdminReloadCmd(ClaimChunk claimChunk) { 16 | super(claimChunk, Executor.CONSOLE_PLAYER, false, "admin"); 17 | } 18 | 19 | @Override 20 | public @Nullable String getDescription() { 21 | return claimChunk.getMessages().cmdReload; 22 | } 23 | 24 | @Override 25 | public CCArg[] getPermittedArguments() { 26 | return new CCArg[0]; 27 | } 28 | 29 | @Override 30 | public int getRequiredArguments() { 31 | return 0; 32 | } 33 | 34 | @Override 35 | public boolean onCall(@NotNull String cmdUsed, @NotNull CommandSender executor, String[] args) { 36 | // Simulate a restart 37 | claimChunk.onDisable(); 38 | claimChunk.onLoad(); 39 | claimChunk.onEnable(); 40 | Utils.log("Performing reload! See you on the other side!"); 41 | messageChat(executor, claimChunk.getMessages().reloadComplete); 42 | return true; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/smartcommand/sub/admin/AdminUnclaimWorldCmd.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.smartcommand.sub.admin; 2 | 3 | import claimchunk.dependency.de.goldmensch.commanddispatcher.Executor; 4 | 5 | import com.cjburkey.claimchunk.ClaimChunk; 6 | import com.cjburkey.claimchunk.smartcommand.CCSubCommand; 7 | 8 | import org.bukkit.command.CommandSender; 9 | import org.bukkit.entity.Player; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | public class AdminUnclaimWorldCmd extends CCSubCommand { 14 | 15 | public AdminUnclaimWorldCmd(ClaimChunk claimChunk) { 16 | super(claimChunk, Executor.PLAYER, false, "admin"); 17 | } 18 | 19 | public @Nullable String getDescription() { 20 | return claimChunk.getMessages().cmdAdminUnclaimWorld; 21 | } 22 | 23 | @Override 24 | public CCArg[] getPermittedArguments() { 25 | return new CCArg[0]; 26 | } 27 | 28 | @Override 29 | public int getRequiredArguments() { 30 | return 0; 31 | } 32 | 33 | @Override 34 | public boolean onCall(@NotNull String cmdUsed, @NotNull CommandSender executor, String[] args) { 35 | Player player = (Player) executor; 36 | int unclaimed = 37 | claimChunk.getChunkHandler().deleteAllWorldClaims(player.getWorld().getName()); 38 | messagePly( 39 | player, 40 | claimChunk.getMessages().adminUnclaimAll.replace("%%CHUNKS%%", unclaimed + "")); 41 | 42 | return true; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/smartcommand/sub/ply/AutoCmd.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.smartcommand.sub.ply; 2 | 3 | import claimchunk.dependency.de.goldmensch.commanddispatcher.Executor; 4 | 5 | import com.cjburkey.claimchunk.ClaimChunk; 6 | import com.cjburkey.claimchunk.chunk.AutoClaimHandler; 7 | import com.cjburkey.claimchunk.smartcommand.CCSubCommand; 8 | 9 | import org.bukkit.command.CommandSender; 10 | import org.bukkit.entity.Player; 11 | import org.jetbrains.annotations.NotNull; 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | /** 15 | * @since 0.0.23 16 | */ 17 | public class AutoCmd extends CCSubCommand { 18 | 19 | public AutoCmd(ClaimChunk claimChunk) { 20 | super(claimChunk, Executor.PLAYER, true, "player", "auto"); 21 | } 22 | 23 | @Override 24 | public @Nullable String getDescription() { 25 | return claimChunk.getMessages().cmdAuto; 26 | } 27 | 28 | @Override 29 | public CCArg[] getPermittedArguments() { 30 | return new CCArg[0]; 31 | } 32 | 33 | @Override 34 | public int getRequiredArguments() { 35 | return 0; 36 | } 37 | 38 | @Override 39 | public boolean onCall(@NotNull String cmdUsed, @NotNull CommandSender executor, String[] args) { 40 | var player = (Player) executor; 41 | if (AutoClaimHandler.toggle(player)) { 42 | messagePly(player, claimChunk.getMessages().autoEnabled); 43 | } else { 44 | messagePly(player, claimChunk.getMessages().autoDisabled); 45 | } 46 | return true; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/smartcommand/sub/admin/AdminOverrideCmd.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.smartcommand.sub.admin; 2 | 3 | import claimchunk.dependency.de.goldmensch.commanddispatcher.Executor; 4 | 5 | import com.cjburkey.claimchunk.ClaimChunk; 6 | import com.cjburkey.claimchunk.smartcommand.CCSubCommand; 7 | 8 | import org.bukkit.command.CommandSender; 9 | import org.bukkit.entity.Player; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | /** 14 | * @since 0.0.23 15 | */ 16 | public class AdminOverrideCmd extends CCSubCommand { 17 | 18 | public AdminOverrideCmd(ClaimChunk claimChunk) { 19 | super(claimChunk, Executor.PLAYER, false, "admin"); 20 | } 21 | 22 | @Override 23 | public @Nullable String getDescription() { 24 | return claimChunk.getMessages().cmdAdminOverride; 25 | } 26 | 27 | @Override 28 | public CCArg[] getPermittedArguments() { 29 | return new CCArg[0]; 30 | } 31 | 32 | @Override 33 | public int getRequiredArguments() { 34 | return 0; 35 | } 36 | 37 | @Override 38 | public boolean onCall(@NotNull String cmdUsed, @NotNull CommandSender executor, String[] args) { 39 | var player = (Player) executor; 40 | if (claimChunk.getAdminOverrideHandler().toggle(player.getUniqueId())) { 41 | messagePly(player, claimChunk.getMessages().adminOverrideEnable); 42 | } else { 43 | messagePly(player, claimChunk.getMessages().adminOverrideDisable); 44 | } 45 | return true; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/smartcommand/sub/ply/AlertCmd.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.smartcommand.sub.ply; 2 | 3 | import claimchunk.dependency.de.goldmensch.commanddispatcher.Executor; 4 | 5 | import com.cjburkey.claimchunk.ClaimChunk; 6 | import com.cjburkey.claimchunk.smartcommand.CCSubCommand; 7 | 8 | import org.bukkit.command.CommandSender; 9 | import org.bukkit.entity.Player; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | /** 14 | * @since 0.0.23 15 | */ 16 | public class AlertCmd extends CCSubCommand { 17 | 18 | public AlertCmd(ClaimChunk claimChunk) { 19 | super(claimChunk, Executor.PLAYER, true, "player", "alert"); 20 | } 21 | 22 | @Override 23 | public @Nullable String getDescription() { 24 | return claimChunk.getMessages().cmdAlert; 25 | } 26 | 27 | @Override 28 | public CCArg[] getPermittedArguments() { 29 | return new CCArg[0]; 30 | } 31 | 32 | @Override 33 | public int getRequiredArguments() { 34 | return 0; 35 | } 36 | 37 | @Override 38 | public boolean onCall(@NotNull String cmdUsed, @NotNull CommandSender executor, String[] args) { 39 | var player = (Player) executor; 40 | var newVal = claimChunk.getPlayerHandler().toggleAlerts(player.getUniqueId()); 41 | var msg = 42 | (newVal 43 | ? claimChunk.getMessages().enabledAlerts 44 | : claimChunk.getMessages().disabledAlerts); 45 | messagePly(player, msg); 46 | return true; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Gradle 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle 3 | 4 | name: Automatic Build 5 | 6 | on: 7 | push: 8 | branches: [ main ] 9 | pull_request: 10 | branches: [ main ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | strategy: 17 | matrix: 18 | java: [21] 19 | 20 | steps: 21 | - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0 22 | - name: Set up JDK ${{ matrix.java }} 23 | uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 #v5.0.0 24 | with: 25 | java-version: ${{ matrix.java }} 26 | distribution: 'adopt' 27 | 28 | - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 #v4.3.0 29 | with: 30 | path: | 31 | ~/.gradle/caches 32 | ~/.gradle/wrapper 33 | key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} 34 | restore-keys: | 35 | ${{ runner.os }}-gradle- 36 | 37 | - name: Grant execute permission for gradlew 38 | run: chmod +x gradlew 39 | 40 | - name: Build with Gradle 41 | run: ./gradlew clean build --no-daemon 42 | 43 | - name: Upload artifact 44 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 #v4.6.2 45 | with: 46 | # Artifact name 47 | name: ClaimChunk 48 | # Upload plugin jar 49 | path: OUT/*-plugin.jar 50 | # Fail if the output file isn't found 51 | if-no-files-found: error 52 | -------------------------------------------------------------------------------- /src/main/resources/validParticleEffects.txt: -------------------------------------------------------------------------------- 1 | EXPLOSION_NORMAL, 2 | EXPLOSION_LARGE, 3 | EXPLOSION_HUGE, 4 | FIREWORKS_SPARK, 5 | WATER_BUBBLE, 6 | WATER_SPLASH, 7 | WATER_WAKE, 8 | SUSPENDED, 9 | SUSPENDED_DEPTH, 10 | CRIT, 11 | CRIT_MAGIC, 12 | SMOKE_NORMAL, 13 | SMOKE_LARGE, 14 | SPELL, 15 | SPELL_INSTANT, 16 | SPELL_MOB, 17 | SPELL_MOB_AMBIENT, 18 | SPELL_WITCH, 19 | DRIP_WATER, 20 | DRIP_LAVA, 21 | VILLAGER_ANGRY, 22 | VILLAGER_HAPPY, 23 | TOWN_AURA, 24 | NOTE, 25 | PORTAL, 26 | ENCHANTMENT_TABLE, 27 | FLAME, 28 | LAVA, 29 | CLOUD, 30 | REDSTONE, 31 | SNOWBALL, 32 | SNOW_SHOVEL, 33 | SLIME, 34 | HEART, 35 | ITEM_CRACK, 36 | BLOCK_CRACK, 37 | BLOCK_DUST, 38 | WATER_DROP, 39 | MOB_APPEARANCE, 40 | DRAGON_BREATH, 41 | END_ROD, 42 | DAMAGE_INDICATOR, 43 | SWEEP_ATTACK, 44 | FALLING_DUST, 45 | TOTEM, 46 | SPIT, 47 | SQUID_INK, 48 | BUBBLE_POP, 49 | CURRENT_DOWN, 50 | BUBBLE_COLUMN_UP, 51 | NAUTILUS, 52 | DOLPHIN, 53 | SNEEZE, 54 | CAMPFIRE_COSY_SMOKE, 55 | CAMPFIRE_SIGNAL_SMOKE, 56 | COMPOSTER, 57 | FLASH, 58 | FALLING_LAVA, 59 | LANDING_LAVA, 60 | FALLING_WATER, 61 | DRIPPING_HONEY, 62 | FALLING_HONEY, 63 | LANDING_HONEY, 64 | FALLING_NECTAR, 65 | SOUL_FIRE_FLAME, 66 | ASH, 67 | CRIMSON_SPORE, 68 | WARPED_SPORE, 69 | SOUL, 70 | DRIPPING_OBSIDIAN_TEAR, 71 | FALLING_OBSIDIAN_TEAR, 72 | LANDING_OBSIDIAN_TEAR, 73 | REVERSE_PORTAL, 74 | WHITE_ASH, 75 | DUST_COLOR_TRANSITION, 76 | VIBRATION, 77 | FALLING_SPORE_BLOSSOM, 78 | SPORE_BLOSSOM_AIR, 79 | SMALL_FLAME, 80 | SNOWFLAKE, 81 | DRIPPING_DRIPSTONE_LAVA, 82 | FALLING_DRIPSTONE_LAVA, 83 | DRIPPING_DRIPSTONE_WATER, 84 | FALLING_DRIPSTONE_WATER, 85 | GLOW_SQUID_INK, 86 | GLOW, 87 | WAX_ON, 88 | WAX_OFF, 89 | ELECTRIC_SPARK, 90 | SCRAPE, 91 | BLOCK_MARKER, 92 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/rank/Rank.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.rank; 2 | 3 | import org.bukkit.Bukkit; 4 | import org.bukkit.permissions.Permission; 5 | import org.bukkit.permissions.PermissionDefault; 6 | import org.bukkit.plugin.PluginManager; 7 | 8 | import java.util.HashMap; 9 | import java.util.Objects; 10 | 11 | public class Rank { 12 | 13 | int claims; 14 | private final String name; 15 | private transient Permission perm; 16 | 17 | Rank(String name, int claims) { 18 | this.name = name; 19 | this.claims = claims; 20 | } 21 | 22 | Permission getPerm() { 23 | if (perm == null) { 24 | perm = 25 | new Permission( 26 | "claimchunk.claim." + name, 27 | "CLAIMCHUNK", 28 | PermissionDefault.FALSE, 29 | new HashMap<>()); 30 | PluginManager pm = Bukkit.getServer().getPluginManager(); 31 | if (pm.getPermission(getPerm().getName()) == null) pm.addPermission(getPerm()); 32 | } 33 | return perm; 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return String.format("%s: %s claims", name, claims); 39 | } 40 | 41 | @Override 42 | public boolean equals(Object o) { 43 | if (this == o) return true; 44 | if (o == null || getClass() != o.getClass()) return false; 45 | Rank rank = (Rank) o; 46 | return claims == rank.claims && Objects.equals(name, rank.name); 47 | } 48 | 49 | @Override 50 | public int hashCode() { 51 | return Objects.hash(name, claims); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/data/newdata/DataConvert.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.data.newdata; 2 | 3 | /** 4 | * Represents a class that may act as a converter between two different data systems. 5 | * 6 | * @since 0.0.13 7 | */ 8 | public class DataConvert { 9 | 10 | private DataConvert() {} 11 | 12 | /** 13 | * Copies the data from the provided old data handler into the provided new data handler. This 14 | * does not update the old data handler. 15 | * 16 | * @param oldDataHandler The old handler, may or may not be initialized 17 | * @param newDataHandler The new handler, may or may not be initialized 18 | * @since 0.0.13 19 | */ 20 | public static void copyConvert( 21 | IClaimChunkDataHandler oldDataHandler, IClaimChunkDataHandler newDataHandler) 22 | throws Exception { 23 | // Initialize the old data handler if it hasn't been initialized yet 24 | if (!oldDataHandler.getHasInit()) oldDataHandler.init(); 25 | 26 | // Load the old data 27 | oldDataHandler.load(); 28 | 29 | // Initialize the new data handler if it hasn't been initialized yet 30 | if (!newDataHandler.getHasInit()) newDataHandler.init(); 31 | 32 | // Copy the player data from the old data handler to the new data handler. 33 | // Make sure we do this before players! The SQLite data handler will make dummy players if 34 | // there aren't proper players in the player data table already. 35 | newDataHandler.addPlayers(oldDataHandler.getFullPlayerData()); 36 | 37 | // Copy the chunks from the old data handler to the new data handler 38 | newDataHandler.addClaimedChunks(oldDataHandler.getClaimedChunks()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/service/prereq/PrereqChecker.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.service.prereq; 2 | 3 | import org.jetbrains.annotations.Nullable; 4 | 5 | import java.util.*; 6 | 7 | public final class PrereqChecker, E> { 8 | 9 | // Sort the prerequisites according to their weights 10 | public final PriorityQueue prereqs = 11 | new PriorityQueue<>(Comparator.comparingInt(T::getWeight)); 12 | 13 | public void check( 14 | E prereqData, 15 | @Nullable String defaultSuccessMessage, 16 | IPrereqAction onError, 17 | IPrereqAction onSuccess) { 18 | String successOutput = defaultSuccessMessage; 19 | 20 | // Check all the prerequisites 21 | for (T prereq : prereqs) { 22 | if (!prereq.getPassed(prereqData)) { 23 | // Call the error handler 24 | onError.call(prereq.getErrorMessage(prereqData)); 25 | return; 26 | } 27 | 28 | // Get and update (if present) the message about the success 29 | Optional successMessage = prereq.getSuccessMessage(prereqData); 30 | if (successMessage.isPresent()) { 31 | successOutput = successMessage.get(); 32 | } 33 | } 34 | 35 | // Call the success handler 36 | onSuccess.call(Optional.ofNullable(successOutput)); 37 | 38 | // Run success methods 39 | for (T prereq : prereqs) { 40 | prereq.onSuccess(prereqData); 41 | } 42 | } 43 | 44 | @FunctionalInterface 45 | public interface IPrereqAction { 46 | 47 | void call(@SuppressWarnings("OptionalUsedAsFieldOrParameterType") Optional message); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/smartcommand/sub/ply/ClaimCmd.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.smartcommand.sub.ply; 2 | 3 | import claimchunk.dependency.de.goldmensch.commanddispatcher.Executor; 4 | 5 | import com.cjburkey.claimchunk.ClaimChunk; 6 | import com.cjburkey.claimchunk.chunk.ChunkPos; 7 | import com.cjburkey.claimchunk.smartcommand.CCSubCommand; 8 | 9 | import org.bukkit.command.CommandSender; 10 | import org.bukkit.entity.Player; 11 | import org.jetbrains.annotations.NotNull; 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | /** 15 | * @since 0.0.23 16 | */ 17 | public class ClaimCmd extends CCSubCommand { 18 | 19 | public ClaimCmd(ClaimChunk claimChunk) { 20 | // TODO: ADD `/chunk admin claim ` to allow claiming a chunk for 21 | // a player. 22 | // ADD `/chunk admin claim ` to allow claiming the 23 | // chunk containing the world coordinates X and Y for the given 24 | // player. 25 | super(claimChunk, Executor.PLAYER, true, "player", "claim"); 26 | } 27 | 28 | @Override 29 | public @Nullable String getDescription() { 30 | return claimChunk.getMessages().cmdClaim; 31 | } 32 | 33 | @Override 34 | public CCArg[] getPermittedArguments() { 35 | return new CCArg[0]; 36 | } 37 | 38 | @Override 39 | public int getRequiredArguments() { 40 | return 0; 41 | } 42 | 43 | @Override 44 | public boolean onCall(@NotNull String cmdUsed, @NotNull CommandSender executor, String[] args) { 45 | var player = (Player) executor; 46 | claimChunk 47 | .getMainHandler() 48 | .claimChunk(player, new ChunkPos(player.getLocation().getChunk())); 49 | return true; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | There are two main ways to support ClaimChunk; the first is [donating to the author, CJ Burkey](https://bit.ly/2LH23dn). The second option is creating pull requests & issues. 4 | 5 | I appreciate all the forms of support players and server maintainers have given me over the past couple of years with this plugin; I would like to keep this small, respectful community of everyone a welcoming place for everyone and I appreciate everyone else willing to help :) 6 | 7 | ### [Issues](https://github.com/cjburkey01/claimchunk/issues) 8 | 9 | Creating an issue for an unexpected behavior you notice while using the plugin or missing expected behavior is probably the simplest way to make a good impact on the plugin. Make sure to include as much information as possible, and I'll do my best to locate and fix the bug responsible. There are also feature request issues which, much like they sound, describe some sort of feature that ought to exist but doesn't at the present. There are, of course, plenty of different types of issues and suggestions a plugin could receive and you're not limited to those two specifically. 10 | 11 | ### [Pull Requests](https://github.com/cjburkey01/claimchunk/pulls) 12 | 13 | Pull requests are how Github handles other people adding code into the repo. I'm going to be entirely honest here: I've never made a pull request, which is definitely weird now that I think about it. That being said, the process to develop one is [documented here on Github's documentation](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests). You're more than welcome (and even encouraged) to submit your own pull requests to address certain features/bugs, especially if I'm difficult to reach for a couple days, or even if you just want to help out. 14 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/flag/CCFlags.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.flag; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | import java.util.Set; 6 | 7 | /** 8 | * @since 0.0.26 9 | */ 10 | public final class CCFlags { 11 | 12 | // Methods named such that they may align with record getters :} 13 | public interface IFlagData> { 14 | @NotNull 15 | TypeEnum flagType(); 16 | 17 | @NotNull 18 | FlagData flagData(); 19 | } 20 | 21 | public enum BlockFlagType { 22 | BREAK, 23 | PLACE, 24 | INTERACT, 25 | EXPLODE, 26 | } 27 | 28 | public enum EntityFlagType { 29 | DAMAGE, 30 | INTERACT, 31 | EXPLODE, 32 | } 33 | 34 | public enum ProtectWhen { 35 | ENABLED, 36 | DISABLED; 37 | 38 | public boolean ifEnabled() { 39 | return this == ENABLED; 40 | } 41 | 42 | public boolean doesProtect(boolean isFlagEnabled) { 43 | if (ifEnabled()) { 44 | return isFlagEnabled; 45 | } 46 | return !isFlagEnabled; 47 | } 48 | } 49 | 50 | public record SimpleFlag(@NotNull String name, @NotNull ProtectWhen protectWhen) {} 51 | 52 | public record ProtectingFlag(@NotNull String name, @NotNull CCFlags.FlagData flagData) {} 53 | 54 | public record FlagData( 55 | @NotNull ProtectWhen protectWhen, 56 | @NotNull Set include, 57 | @NotNull Set exclude) {} 58 | 59 | public record BlockFlagData(@NotNull BlockFlagType flagType, @NotNull FlagData flagData) 60 | implements IFlagData {} 61 | 62 | public record EntityFlagData(@NotNull EntityFlagType flagType, @NotNull FlagData flagData) 63 | implements IFlagData {} 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/gui/ICCGui.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.gui; 2 | 3 | import org.bukkit.entity.Player; 4 | import org.bukkit.event.inventory.ClickType; 5 | import org.bukkit.inventory.Inventory; 6 | import org.bukkit.inventory.ItemStack; 7 | import org.jetbrains.annotations.NotNull; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | /** 11 | * Interface representing a ClaimChunk GUI screen. 12 | * 13 | * @since 0.0.26 14 | */ 15 | public interface ICCGui { 16 | 17 | /** 18 | * Called when the GUI is being constructed to be shown to the player. 19 | * 20 | *

Modify the inventory inside this method 21 | * 22 | * @param inventory The inventory being shown. 23 | */ 24 | void onOpen(@NotNull Inventory inventory); 25 | 26 | /** 27 | * Called when the GUI is closed; nothing usually needs to happen, but just in case, you know? 28 | * 29 | * @param inventory The inventory being shown. 30 | */ 31 | void onClose(@NotNull Inventory inventory); 32 | 33 | /** 34 | * Called when the player clicks on a given slot within the inventory. 35 | * 36 | * @param inventory The inventory being shown. 37 | * @param slot The index of the slot being clicked. 38 | * @param clickType Which type of click the player performed. 39 | * @param stack The stack on which the player clicked. 40 | */ 41 | void onClick( 42 | @NotNull Inventory inventory, 43 | int slot, 44 | @NotNull ClickType clickType, 45 | @Nullable ItemStack stack); 46 | 47 | /** 48 | * @return The name to be shown at the top of the GUI 49 | */ 50 | @NotNull 51 | String getName(); 52 | 53 | /** 54 | * @return The number of rows this GUI should have 55 | */ 56 | int getRows(); 57 | 58 | /** 59 | * @return The player this GUI is going to be shown to. 60 | */ 61 | Player getPlayer(); 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/chunk/ChunkPos.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.chunk; 2 | 3 | import org.bukkit.Chunk; 4 | 5 | import java.util.Objects; 6 | 7 | public record ChunkPos(String world, int x, int z) { 8 | 9 | /** 10 | * Create an instance of a chunk position from Spigot's chunk position representation. 11 | * 12 | * @param chunk The Spigot chunk representation. 13 | */ 14 | public ChunkPos(Chunk chunk) { 15 | this(chunk.getWorld().getName(), chunk.getX(), chunk.getZ()); 16 | } 17 | 18 | /** 19 | * Helper method to get a chunk north, relative to this one. 20 | * 21 | * @since 0.0.23 22 | */ 23 | public ChunkPos north() { 24 | return new ChunkPos(world, x, z - 1); 25 | } 26 | 27 | /** 28 | * Helper method to get a chunk south, relative to this one. 29 | * 30 | * @since 0.0.23 31 | */ 32 | public ChunkPos south() { 33 | return new ChunkPos(world, x, z + 1); 34 | } 35 | 36 | /** 37 | * Helper method to get a chunk east, relative to this one. 38 | * 39 | * @since 0.0.23 40 | */ 41 | public ChunkPos east() { 42 | return new ChunkPos(world, x - 1, z); 43 | } 44 | 45 | /** 46 | * Helper method to get a chunk west, relative to this one. 47 | * 48 | * @since 0.0.23 49 | */ 50 | public ChunkPos west() { 51 | return new ChunkPos(world, x + 1, z); 52 | } 53 | 54 | @Override 55 | public String toString() { 56 | return String.format("%s, %s in %s", x, z, world); 57 | } 58 | 59 | @Override 60 | public boolean equals(Object o) { 61 | if (this == o) return true; 62 | if (o == null || getClass() != o.getClass()) return false; 63 | ChunkPos chunkPos = (ChunkPos) o; 64 | return x == chunkPos.x && z == chunkPos.z && Objects.equals(world, chunkPos.world); 65 | } 66 | 67 | @Override 68 | public int hashCode() { 69 | return Objects.hash(world, x, z); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/layer/PlaceholderInitLayer.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.layer; 2 | 3 | import com.cjburkey.claimchunk.Utils; 4 | import com.cjburkey.claimchunk.api.IClaimChunkPlugin; 5 | import com.cjburkey.claimchunk.api.layer.IClaimChunkLayer; 6 | import com.cjburkey.claimchunk.placeholder.ClaimChunkPlaceholders; 7 | 8 | import lombok.Getter; 9 | 10 | @SuppressWarnings("LombokGetterMayBeUsed") 11 | public class PlaceholderInitLayer implements IClaimChunkLayer { 12 | 13 | @Getter private ClaimChunkPlaceholders placeholders; 14 | 15 | @Override 16 | public boolean onEnable(IClaimChunkPlugin claimChunk) { 17 | try { 18 | // Check if PlaceholderAPI is present 19 | if (claimChunk.getServer().getPluginManager().getPlugin("PlaceholderAPI") != null) { 20 | // Try to initialize 21 | placeholders = new ClaimChunkPlaceholders(claimChunk); 22 | // Finally, register them 23 | if (placeholders.register()) { 24 | Utils.log("Successfully enabled the ClaimChunk PlaceholderAPI expansion!"); 25 | return true; 26 | } else { 27 | Utils.err("PlaceholderAPI is present but setting up the API failed!"); 28 | } 29 | } else { 30 | Utils.log("PlaceholderAPI not found, not loading API."); 31 | } 32 | } catch (Exception e) { 33 | Utils.err( 34 | "An error occurred while trying to enable the PlaceholderAPI expansion for" 35 | + " claimchunk placeholders!"); 36 | Utils.err("Here is the error for reference:"); 37 | //noinspection CallToPrintStackTrace 38 | e.printStackTrace(); 39 | } 40 | 41 | return false; 42 | } 43 | 44 | @Override 45 | public void onDisable(IClaimChunkPlugin claimChunk) { 46 | placeholders = null; 47 | } 48 | 49 | @Override 50 | public int getOrderId() { 51 | return 600; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/config/spread/FullSpreadProfile.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.config.spread; 2 | 3 | import com.cjburkey.claimchunk.config.ccconfig.CCConfig; 4 | 5 | import org.jetbrains.annotations.NotNull; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | import java.util.Objects; 9 | import java.util.UUID; 10 | 11 | public class FullSpreadProfile extends SpreadProfile { 12 | 13 | public boolean inClaimed = true; 14 | public boolean inUnclaimed = true; 15 | 16 | public FullSpreadProfile() {} 17 | 18 | // Clone 19 | @SuppressWarnings("unused") 20 | public FullSpreadProfile(FullSpreadProfile other) { 21 | super(other); 22 | 23 | this.inClaimed = other.inClaimed; 24 | this.inUnclaimed = other.inUnclaimed; 25 | } 26 | 27 | @Override 28 | public boolean getShouldCancel(@Nullable UUID sourceOwner, @Nullable UUID newOwner) { 29 | if (Objects.equals(sourceOwner, newOwner)) { 30 | // Disable block spread from unclaimed chunks to unclaimed chunks 31 | if (!inUnclaimed && sourceOwner == null) return true; 32 | 33 | // Disable block spread from claimed chunks into the same owner's chunks 34 | if (!inClaimed && sourceOwner != null) return true; 35 | } 36 | 37 | // Defer to general spread protection 38 | return super.getShouldCancel(sourceOwner, newOwner); 39 | } 40 | 41 | @Override 42 | public void toCCConfig(@NotNull CCConfig config, @NotNull String key) { 43 | super.toCCConfig(config, key); 44 | 45 | config.set(key + ".from_claimed.into_same_claimed", inClaimed); 46 | config.set(key + ".from_unclaimed.into_unclaimed", inUnclaimed); 47 | } 48 | 49 | @Override 50 | public void fromCCConfig(@NotNull CCConfig config, @NotNull String key) { 51 | super.fromCCConfig(config, key); 52 | 53 | inClaimed = config.getBool(key + ".from_claimed.into_same_claimed", inClaimed); 54 | inUnclaimed = config.getBool(key + ".from_unclaimed.into_unclaimed", inUnclaimed); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/layer/PrereqsInitLayer.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.layer; 2 | 3 | import com.cjburkey.claimchunk.Utils; 4 | import com.cjburkey.claimchunk.api.IClaimChunkPlugin; 5 | import com.cjburkey.claimchunk.api.layer.IClaimChunkLayer; 6 | import com.cjburkey.claimchunk.service.prereq.PrereqChecker; 7 | import com.cjburkey.claimchunk.service.prereq.claim.*; 8 | 9 | import lombok.Getter; 10 | 11 | @Getter 12 | public class PrereqsInitLayer implements IClaimChunkLayer { 13 | 14 | // The pre-req checker responsible for chunk claiming. 15 | // We reuse this instance (clearing and re-adding on disable and re-enable, respectively). 16 | private final PrereqChecker claimPrereqChecker = 17 | new PrereqChecker<>(); 18 | 19 | // Initialize each of the prereq checkers (based on config values when necessary) 20 | @Override 21 | public boolean onEnable(IClaimChunkPlugin claimChunk) { 22 | // Add default chunk claiming prerequisites 23 | 24 | // Check permissions 25 | claimPrereqChecker.prereqs.add(new PermissionPrereq()); 26 | // Check that the world is enabled 27 | claimPrereqChecker.prereqs.add(new WorldPrereq()); 28 | // Check if the chunk is already claimed 29 | claimPrereqChecker.prereqs.add(new UnclaimedPrereq()); 30 | // Check if players can claim chunks here/in this world 31 | claimPrereqChecker.prereqs.add(new WorldGuardPrereq()); 32 | // Check if the player has room for more chunk claims 33 | claimPrereqChecker.prereqs.add(new MaxChunksPrereq()); 34 | // Check if the player is near someone else's claim 35 | claimPrereqChecker.prereqs.add(new NearChunkPrereq()); 36 | 37 | // Enable this layer 38 | return true; 39 | } 40 | 41 | // Clear the prereqs when the plugin is disabled 42 | @Override 43 | public void onDisable(IClaimChunkPlugin claimChunk) { 44 | Utils.debug("Clearing prerequisites"); 45 | claimPrereqChecker.prereqs.clear(); 46 | } 47 | 48 | @Override 49 | public int getOrderId() { 50 | return 500; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/smartcommand/sub/ply/UnclaimAllCmd.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.smartcommand.sub.ply; 2 | 3 | import claimchunk.dependency.de.goldmensch.commanddispatcher.Executor; 4 | 5 | import com.cjburkey.claimchunk.ClaimChunk; 6 | import com.cjburkey.claimchunk.smartcommand.CCSubCommand; 7 | 8 | import org.bukkit.command.CommandSender; 9 | import org.bukkit.entity.Player; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | public class UnclaimAllCmd extends CCSubCommand { 14 | 15 | public UnclaimAllCmd(ClaimChunk claimChunk) { 16 | super(claimChunk, Executor.PLAYER, true, "player", "unclaim"); 17 | } 18 | 19 | @Override 20 | public @Nullable String getDescription() { 21 | return claimChunk.getMessages().cmdUnclaimAll; 22 | } 23 | 24 | @Override 25 | public CCArg[] getPermittedArguments() { 26 | return new CCArg[] { 27 | new CCArg(claimChunk.getMessages().argAcrossAllWorlds, CCAutoComplete.BOOLEAN) 28 | }; 29 | } 30 | 31 | @Override 32 | public int getRequiredArguments() { 33 | return 0; 34 | } 35 | 36 | @Override 37 | public boolean onCall(@NotNull String cmdUsed, @NotNull CommandSender executor, String[] args) { 38 | var player = (Player) executor; 39 | var allWorlds = (args.length == 1 && Boolean.parseBoolean(args[0])); 40 | var chunkHandler = claimChunk.getChunkHandler(); 41 | 42 | var claimedChunks = chunkHandler.getClaimedChunks(player.getUniqueId()); 43 | int unclaimed = 0; 44 | for (var chunk : claimedChunks) { 45 | if ((allWorlds || player.getWorld().getName().equals(chunk.world())) 46 | && claimChunk 47 | .getMainHandler() 48 | .unclaimChunk( 49 | false, true, player, chunk.world(), chunk.x(), chunk.z())) { 50 | unclaimed++; 51 | } 52 | } 53 | 54 | messagePly( 55 | player, claimChunk.getMessages().unclaimAll.replace("%%CHUNKS%%", unclaimed + "")); 56 | return true; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/data/sqlite/Translators.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.data.sqlite; 2 | 3 | import com.cjburkey.claimchunk.chunk.ChunkPos; 4 | 5 | import org.sormula.translator.TypeTranslator; 6 | 7 | import java.sql.PreparedStatement; 8 | import java.sql.ResultSet; 9 | import java.util.UUID; 10 | 11 | public final class Translators { 12 | 13 | public record UUIDTranslator() implements TypeTranslator { 14 | @Override 15 | public UUID read(ResultSet resultSet, int columnIndex) throws Exception { 16 | String str = resultSet.getString(columnIndex); 17 | if (str.isEmpty()) { 18 | return null; 19 | } 20 | 21 | return UUID.fromString(str); 22 | } 23 | 24 | @Override 25 | public void write(PreparedStatement preparedStatement, int parameterIndex, UUID parameter) 26 | throws Exception { 27 | preparedStatement.setString( 28 | parameterIndex, parameter != null ? parameter.toString() : ""); 29 | } 30 | } 31 | 32 | public record ChunkPosTranslator() implements TypeTranslator { 33 | @Override 34 | public ChunkPos read(ResultSet resultSet, int columnIndex) throws Exception { 35 | String val = resultSet.getString(columnIndex); 36 | if (val.isEmpty()) { 37 | return null; 38 | } 39 | 40 | String[] str = val.split(","); 41 | String world = str[0]; 42 | int x = Integer.parseInt(str[1]); 43 | int z = Integer.parseInt(str[2]); 44 | return new ChunkPos(world, x, z); 45 | } 46 | 47 | @Override 48 | public void write( 49 | PreparedStatement preparedStatement, int parameterIndex, ChunkPos parameter) 50 | throws Exception { 51 | preparedStatement.setString( 52 | parameterIndex, 53 | parameter != null 54 | ? String.format( 55 | "%s,%s,%s", parameter.world(), parameter.x(), parameter.z()) 56 | : ""); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/service/prereq/claim/NearChunkPrereq.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.service.prereq.claim; 2 | 3 | import org.bukkit.Chunk; 4 | import org.bukkit.World; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | import java.util.Optional; 8 | import java.util.UUID; 9 | 10 | public class NearChunkPrereq implements IClaimPrereq { 11 | 12 | @Override 13 | public int getWeight() { 14 | return 300; 15 | } 16 | 17 | @Override 18 | public boolean getPassed(@NotNull PrereqClaimData data) { 19 | boolean nearClaimed = false; 20 | 21 | // Get the diameter around the player to check 22 | int near = data.claimChunk().getConfigHandler().getNearChunkSearch(); 23 | if (near < 1) return true; 24 | 25 | // Get starting and ending bounds 26 | int min = (near - 1) / 2; 27 | int max = (near - 1) / 2 + 1; 28 | 29 | World world = data.claimChunk().getServer().getWorld(data.chunk().world()); 30 | if (world == null) { 31 | return false; 32 | } 33 | 34 | // Check through chunks within the given area 35 | for (int x1 = -min; x1 < max; x1++) { 36 | for (int z1 = -min; z1 < max; z1++) { 37 | if (nearClaimed || data.player().hasPermission("claimchunk.bypassnearbychunk")) 38 | break; 39 | 40 | Chunk chunk = world.getChunkAt(x1 + data.chunk().x(), z1 + data.chunk().z()); 41 | 42 | if (data.claimChunk().getChunkHandler().isOwner(chunk, data.player())) continue; 43 | nearClaimed = data.claimChunk().getChunkHandler().isClaimed(chunk); 44 | UUID owner = data.claimChunk().getChunkHandler().getOwner(data.chunk()); 45 | // If the given chunk is owned but not by this player, fail this prereq 46 | if (owner != null && owner.equals(data.playerId())) return false; 47 | } 48 | } 49 | 50 | // Otherwise, pass 51 | return true; 52 | } 53 | 54 | @Override 55 | public Optional getErrorMessage(@NotNull PrereqClaimData data) { 56 | return Optional.of(data.claimChunk().getMessages().nearChunkSearch); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/config/access/Accesses.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.config.access; 2 | 3 | import com.cjburkey.claimchunk.Utils; 4 | 5 | import org.bukkit.Material; 6 | import org.bukkit.entity.EntityType; 7 | 8 | import java.util.HashMap; 9 | 10 | public class Accesses { 11 | 12 | // Entity access tracking 13 | public final HashMap entityAccesses; 14 | public final HashMap liveEntityAccesses; 15 | public final HashMap entityAccessClassMapping; 16 | 17 | // Block access tracking 18 | public final HashMap blockAccesses; 19 | public final HashMap liveBlockAccesses; 20 | public final HashMap blockAccessClassMapping; 21 | 22 | public Accesses( 23 | HashMap entityAccesses, 24 | HashMap entityAccessClassMapping, 25 | HashMap blockAccesses, 26 | HashMap blockAccessClassMapping) { 27 | this.entityAccesses = entityAccesses; 28 | this.entityAccessClassMapping = entityAccessClassMapping; 29 | this.liveEntityAccesses = new HashMap<>(); 30 | 31 | this.blockAccesses = blockAccesses; 32 | this.blockAccessClassMapping = blockAccessClassMapping; 33 | this.liveBlockAccesses = new HashMap<>(); 34 | } 35 | 36 | // Clone 37 | public Accesses(Accesses original) { 38 | this.entityAccesses = Utils.deepCloneMap(original.entityAccesses, EntityAccess::new); 39 | this.liveEntityAccesses = 40 | Utils.deepCloneMap(original.liveEntityAccesses, EntityAccess::new); 41 | this.entityAccessClassMapping = 42 | Utils.deepCloneMap(original.entityAccessClassMapping, EntityAccess::new); 43 | 44 | this.blockAccesses = Utils.deepCloneMap(original.blockAccesses, BlockAccess::new); 45 | this.liveBlockAccesses = Utils.deepCloneMap(original.liveBlockAccesses, BlockAccess::new); 46 | this.blockAccessClassMapping = 47 | Utils.deepCloneMap(original.blockAccessClassMapping, BlockAccess::new); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/smartcommand/sub/ply/NameCmd.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.smartcommand.sub.ply; 2 | 3 | import claimchunk.dependency.de.goldmensch.commanddispatcher.Executor; 4 | 5 | import com.cjburkey.claimchunk.ClaimChunk; 6 | import com.cjburkey.claimchunk.smartcommand.CCSubCommand; 7 | 8 | import org.bukkit.command.CommandSender; 9 | import org.bukkit.entity.Player; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | /** 14 | * @since 0.0.23 15 | */ 16 | public class NameCmd extends CCSubCommand { 17 | 18 | public NameCmd(ClaimChunk claimChunk) { 19 | super(claimChunk, Executor.PLAYER, true, "player", "name"); 20 | } 21 | 22 | @Override 23 | public @Nullable String getDescription() { 24 | return claimChunk.getMessages().cmdName; 25 | } 26 | 27 | @Override 28 | public CCArg[] getPermittedArguments() { 29 | return new CCArg[] { 30 | new CCArg(claimChunk.getMessages().argNewName, CCAutoComplete.NONE), 31 | }; 32 | } 33 | 34 | @Override 35 | public int getRequiredArguments() { 36 | return 0; 37 | } 38 | 39 | @Override 40 | public boolean onCall(@NotNull String cmdUsed, @NotNull CommandSender executor, String[] args) { 41 | var player = (Player) executor; 42 | var nh = claimChunk.getPlayerHandler(); 43 | try { 44 | if (args.length == 0) { 45 | if (nh.hasChunkName(player.getUniqueId())) { 46 | nh.clearChunkName(player.getUniqueId()); 47 | messagePly(player, claimChunk.getMessages().nameClear); 48 | } else { 49 | messagePly(player, claimChunk.getMessages().nameNotSet); 50 | } 51 | } else { 52 | nh.setChunkName(player.getUniqueId(), args[0].trim()); 53 | messagePly( 54 | player, 55 | claimChunk.getMessages().nameSet.replace("%%NAME%%", args[0].trim())); 56 | } 57 | } catch (Exception e) { 58 | e.printStackTrace(); 59 | messageChat(player, "&4&lAn error occurred, please contact an admin."); 60 | } 61 | return true; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/smartcommand/sub/ply/ShowCmd.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.smartcommand.sub.ply; 2 | 3 | import claimchunk.dependency.de.goldmensch.commanddispatcher.Executor; 4 | 5 | import com.cjburkey.claimchunk.ClaimChunk; 6 | import com.cjburkey.claimchunk.chunk.ChunkOutlineHandler; 7 | import com.cjburkey.claimchunk.chunk.ChunkPos; 8 | import com.cjburkey.claimchunk.smartcommand.CCSubCommand; 9 | 10 | import org.bukkit.command.CommandSender; 11 | import org.bukkit.entity.Player; 12 | import org.jetbrains.annotations.NotNull; 13 | import org.jetbrains.annotations.Nullable; 14 | 15 | /** 16 | * @since 0.0.23 17 | */ 18 | public class ShowCmd extends CCSubCommand { 19 | 20 | public int maxSeconds = 60; 21 | 22 | public ShowCmd(ClaimChunk claimChunk) { 23 | super(claimChunk, Executor.PLAYER, true, "player", "show"); 24 | } 25 | 26 | @Override 27 | public @Nullable String getDescription() { 28 | return claimChunk.getMessages().cmdShow; 29 | } 30 | 31 | @Override 32 | public CCArg[] getPermittedArguments() { 33 | return new CCArg[] {new CCArg(claimChunk.getMessages().argSeconds, CCAutoComplete.NONE)}; 34 | } 35 | 36 | @Override 37 | public int getRequiredArguments() { 38 | return 0; 39 | } 40 | 41 | @Override 42 | public boolean onCall(@NotNull String cmdUsed, @NotNull CommandSender executor, String[] args) { 43 | var player = (Player) executor; 44 | var chunkPos = new ChunkPos(player.getLocation().getChunk()); 45 | var showForSeconds = 5; 46 | 47 | // Optional argument to show for a given time (up to the max defined) 48 | if (args.length == 1) { 49 | try { 50 | showForSeconds = Integer.min(Integer.parseInt(args[0]), maxSeconds); 51 | } catch (Exception e) { 52 | return false; 53 | } 54 | } 55 | 56 | // Use the new particle system! 57 | claimChunk 58 | .getChunkOutlineHandler() 59 | .showChunkFor( 60 | chunkPos, 61 | player, 62 | showForSeconds, 63 | ChunkOutlineHandler.OutlineSides.makeAll(true)); 64 | return true; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/player/FullPlayerData.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.player; 2 | 3 | import com.cjburkey.claimchunk.data.sqlite.SqlDataPlayer; 4 | 5 | import java.util.HashMap; 6 | import java.util.UUID; 7 | 8 | public class FullPlayerData { 9 | 10 | public final UUID player; 11 | public final String lastIgn; 12 | public String chunkName; 13 | public long lastOnlineTime; 14 | public boolean alert; 15 | public int extraMaxClaims; 16 | public final HashMap globalFlags; 17 | public final HashMap> playerFlags; 18 | 19 | public FullPlayerData( 20 | UUID player, 21 | String lastIgn, 22 | String chunkName, 23 | long lastOnlineTime, 24 | boolean alert, 25 | int extraMaxClaims, 26 | HashMap globalFlags, 27 | HashMap> playerFlags) { 28 | this.player = player; 29 | this.lastIgn = lastIgn; 30 | this.chunkName = chunkName; 31 | this.lastOnlineTime = lastOnlineTime; 32 | this.alert = alert; 33 | this.extraMaxClaims = extraMaxClaims; 34 | this.globalFlags = globalFlags; 35 | this.playerFlags = playerFlags; 36 | } 37 | 38 | public FullPlayerData( 39 | UUID player, 40 | String lastIgn, 41 | String chunkName, 42 | long lastOnlineTime, 43 | boolean alert, 44 | int extraMaxClaims) { 45 | this( 46 | player, 47 | lastIgn, 48 | chunkName, 49 | lastOnlineTime, 50 | alert, 51 | extraMaxClaims, 52 | new HashMap<>(), 53 | new HashMap<>()); 54 | } 55 | 56 | public FullPlayerData(SqlDataPlayer player) { 57 | this( 58 | UUID.fromString(player.uuid), 59 | player.lastIgn, 60 | player.chunkName, 61 | player.lastOnlineTime, 62 | player.alert, 63 | player.extraMaxClaims); 64 | } 65 | 66 | public SimplePlayerData toSimplePlayer() { 67 | return new SimplePlayerData(player, lastIgn, lastOnlineTime); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/service/prereq/IPrereq.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.service.prereq; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | import java.util.Optional; 6 | 7 | /** 8 | * An interface representing some class that performs a check. 9 | * 10 | * @since 0.0.20 11 | */ 12 | public interface IPrereq { 13 | 14 | /** 15 | * Gets the priority of this prerequisite. A smaller number would be checked first. 16 | * 17 | * @return The signed integer weight of this prerequisite. 18 | * @since 0.0.20 19 | */ 20 | int getWeight(); 21 | 22 | /** 23 | * Determines whether the provided player should be able to perform the action. Note: this 24 | * method should not change the state of anything as it's currently unknown whether the action 25 | * could fail after this check. If a subsequent check fails, then the action does not occur, so 26 | * it's important to make sure nothing is changed in this method. 27 | * 28 | * @param data The prerequisite data. 29 | * @return Whether this prerequisite was met. 30 | * @since 0.0.20 31 | */ 32 | boolean getPassed(@NotNull T data); 33 | 34 | /** 35 | * Gets the error message that should be displayed if this prerequisite is not met. If a 36 | * prerequisite is not met, the process stops there and displays the error message returned by 37 | * the failing check. 38 | * 39 | * @param data The prerequisite data. 40 | * @return An optional error message describing why the action has failed. 41 | * @since 0.0.20 42 | */ 43 | default Optional getErrorMessage(@NotNull T data) { 44 | return Optional.empty(); 45 | } 46 | 47 | /** 48 | * If this prerequisite succeeds, it will override the current success message with this one if 49 | * it is supplied. 50 | * 51 | * @param data The prerequisite data. 52 | * @return An optional success message. 53 | * @since 0.0.20 54 | */ 55 | default Optional getSuccessMessage(@NotNull T data) { 56 | return Optional.empty(); 57 | } 58 | 59 | /** 60 | * Called after the action has been successfully completed. 61 | * 62 | * @param data The prerequisite data. 63 | * @since 0.0.20 64 | */ 65 | default void onSuccess(@NotNull T data) {} 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/gui/screens/PermSelectMenu.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.gui.screens; 2 | 3 | import com.cjburkey.claimchunk.ClaimChunk; 4 | import com.cjburkey.claimchunk.ClaimChunkConfig; 5 | import com.cjburkey.claimchunk.gui.GuiMenuScreen; 6 | import com.cjburkey.claimchunk.i18n.V2JsonMessages; 7 | 8 | import org.bukkit.Material; 9 | import org.bukkit.entity.Player; 10 | import org.bukkit.inventory.Inventory; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | public class PermSelectMenu extends GuiMenuScreen { 14 | 15 | public PermSelectMenu(@NotNull ClaimChunk claimChunk, @NotNull Player player) { 16 | // Offset to make it 6 tall so that we can have the top row be a menu bar. 17 | super(claimChunk, player, 1, claimChunk.getMessages().guiPermSelectMenuTitle); 18 | } 19 | 20 | @Override 21 | public void onOpen(@NotNull Inventory inventory) { 22 | V2JsonMessages messages = claimChunk.getMessages(); 23 | ClaimChunkConfig config = claimChunk.getConfigHandler(); 24 | 25 | // Back button 26 | addInteractiveButton( 27 | inventory, 28 | 0, 29 | materialFromStr(config.getGuiMenuBackButtonItem()), 30 | messages.guiMenuBackButtonName, 31 | splitLineLore(messages.guiPermModifyBackButtonDesc), 32 | (clickType, stack) -> openGui(new MainMenu(claimChunk, getPlayer()))); 33 | 34 | Material item = materialFromStr(config.getGuiPermSelectMenuItem()); 35 | 36 | // Permission type selection button 37 | addInteractiveButton( 38 | inventory, 39 | 4, 40 | item, 41 | messages.guiPermSelectMenuThisChunkName, 42 | splitLineLore(messages.guiPermSelectMenuThisChunkDesc), 43 | (clickType, stack) -> 44 | openGui(new PermModifyMenu(claimChunk, getPlayer(), false, true))); 45 | addInteractiveButton( 46 | inventory, 47 | 5, 48 | item, 49 | messages.guiPermSelectMenuAllChunksName, 50 | splitLineLore(messages.guiPermSelectMenuAllChunksDesc), 51 | (clickType, stack) -> 52 | openGui(new PermModifyMenu(claimChunk, getPlayer(), true, true))); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/smartcommand/sub/admin/AdminUnclaimAllCmd.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.smartcommand.sub.admin; 2 | 3 | import claimchunk.dependency.de.goldmensch.commanddispatcher.Executor; 4 | 5 | import com.cjburkey.claimchunk.ClaimChunk; 6 | import com.cjburkey.claimchunk.smartcommand.CCSubCommand; 7 | 8 | import org.bukkit.command.CommandSender; 9 | import org.bukkit.entity.Player; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | public class AdminUnclaimAllCmd extends CCSubCommand { 14 | 15 | public AdminUnclaimAllCmd(ClaimChunk claimChunk) { 16 | super(claimChunk, Executor.PLAYER, false, "admin"); 17 | } 18 | 19 | public @Nullable String getDescription() { 20 | return claimChunk.getMessages().cmdAdminUnclaimAll; 21 | } 22 | 23 | @Override 24 | public CCArg[] getPermittedArguments() { 25 | return new CCArg[] { 26 | new CCArg(claimChunk.getMessages().argPlayer, CCAutoComplete.OFFLINE_PLAYER), 27 | new CCArg(claimChunk.getMessages().argAcrossAllWorlds, CCAutoComplete.NONE), 28 | }; 29 | } 30 | 31 | @Override 32 | public int getRequiredArguments() { 33 | return 1; 34 | } 35 | 36 | @Override 37 | public boolean onCall(@NotNull String cmdUsed, @NotNull CommandSender executor, String[] args) { 38 | var player = (Player) executor; 39 | var allWorlds = false; 40 | if (args.length == 2) { 41 | var allWorldsOpt = claimChunk.getMessages().parseBool(args[1]); 42 | // Failed to parse the provided boolean value 43 | if (allWorldsOpt.isEmpty()) { 44 | return false; 45 | } 46 | allWorlds = allWorldsOpt.get(); 47 | } 48 | var chunkHandler = claimChunk.getChunkHandler(); 49 | 50 | var ply = claimChunk.getPlayerHandler().getUUID(args[0]); 51 | if (ply != null) { 52 | var claimedChunks = chunkHandler.getClaimedChunks(ply); 53 | int unclaimed = 0; 54 | for (var chunk : claimedChunks) { 55 | if (allWorlds || player.getWorld().getName().equals(chunk.world())) { 56 | chunkHandler.unclaimChunk(chunk.world(), chunk.x(), chunk.z()); 57 | unclaimed++; 58 | } 59 | } 60 | messagePly( 61 | player, 62 | claimChunk.getMessages().adminUnclaimAll.replace("%%CHUNKS%%", unclaimed + "")); 63 | } else { 64 | messagePly(player, claimChunk.getMessages().noPlayer); 65 | } 66 | 67 | return true; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/resources/config.yml: -------------------------------------------------------------------------------- 1 | # Config generated for ClaimChunk version @PLUGIN_VERSION@ 2 | # For more information about all these options, please visit the wiki page: 3 | # https://github.com/cjburkey01/ClaimChunk/wiki/Configuration-File 4 | 5 | basic: 6 | disablePermissions: false 7 | checkForUpdates: true 8 | 9 | log: 10 | debugSpam: false 11 | anonymousMetrics: true 12 | showExtraInfoOnAnonymousMetrics: true 13 | 14 | titles: 15 | useTitlesInsteadOfChat: true 16 | useActionBar: true 17 | titleFadeInTime: 20 18 | titleStayTime: 140 19 | titleFadeOutTime: 20 20 | # Delay in ticks between "you have entered" or "you have exited" messages 21 | # emitted by the server to prevent chat spam. 22 | chunkEnterExitSpamDelay: 20 23 | 24 | colors: 25 | infoColor: GOLD 26 | errorColor: RED 27 | 28 | chunks: 29 | maxChunksClaimed: 50 30 | particlesWhenClaiming: true 31 | hideAlertsForVanishedPlayers: true 32 | automaticUnclaimSeconds: -1 33 | unclaimCheckIntervalTicks: 1200 34 | displayNameOfOwner: true 35 | defaultSendAlertsToOwner: true 36 | maxPerListPage: 5 37 | disabledWorlds: [] 38 | nearChunkSearch: 5 39 | # Anything higher than 25 runs the risk of crashing your server 40 | maxScanRange: 20 41 | 42 | chunkOutline: 43 | name: 'SMOKE_LARGE' 44 | durationSeconds: 5 45 | spawnsPerSecond: 2 46 | particlesPerSpawn: 2 47 | heightRadius: 3 48 | # TODO: NOT IMPLEMENTED 49 | useNewEffect: false 50 | 51 | worldguard: 52 | allowClaimsInRegionsByDefault: true 53 | allowClaimingInNonGuardedWorlds: true 54 | allowAdminOverride: true 55 | 56 | economy: 57 | useEconomy: true 58 | claimPrice: 100.0 59 | unclaimReward: 10.0 60 | firstFreeChunks: 0 61 | 62 | floodclaim: 63 | enabled: false 64 | maximumIterations: 6 65 | maximumArea: 41 66 | 67 | data: 68 | saveDataIntervalInMinutes: 10 69 | keepJsonBackups: true 70 | minBackupIntervalInMinutes: 120 71 | deleteOldBackupsAfterMinutes: 10080 72 | 73 | gui: 74 | menuBackButtonItem: 'minecraft:barrier' 75 | mainMenuCurrentChunkItem: 'minecraft:grass_block' 76 | mainMenuChunkMapItem: 'minecraft:map' 77 | mainMenuPermFlagsItem: 'minecraft:writable_book' 78 | mapMenuUnclaimedItem: 'minecraft:white_wool' 79 | mapMenuSelfClaimedItem: 'minecraft:green_wool' 80 | mapMenuOtherClaimedItem: 'minecraft:blue_wool' 81 | mapMenuCenterUnclaimedItem: 'minecraft:white_glazed_terracotta' 82 | mapMenuCenterSelfClaimedItem: 'minecraft:green_glazed_terracotta' 83 | mapMenuCenterOtherClaimedItem: 'minecraft:blue_glazed_terracotta' 84 | mapMenuAllowClaimOtherChunks: true 85 | permSelectMenuItem: 'minecraft:bow' 86 | permModifyAllowItem: 'minecraft:green_wool' 87 | permModifyDenyItem: 'minecraft:red_wool' 88 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/config/spread/SpreadProfile.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.config.spread; 2 | 3 | import com.cjburkey.claimchunk.config.ccconfig.CCConfig; 4 | import com.cjburkey.claimchunk.config.ccconfig.ICCConfigSerializable; 5 | 6 | import org.jetbrains.annotations.NotNull; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | import java.util.Objects; 10 | import java.util.UUID; 11 | 12 | public class SpreadProfile implements ICCConfigSerializable { 13 | 14 | public boolean fromClaimedIntoDiffClaimed = true; 15 | public boolean fromClaimedIntoUnclaimed = true; 16 | public boolean fromUnclaimedIntoClaimed = true; 17 | 18 | public SpreadProfile() {} 19 | 20 | // Clone 21 | public SpreadProfile(SpreadProfile other) { 22 | this.fromClaimedIntoDiffClaimed = other.fromClaimedIntoDiffClaimed; 23 | this.fromClaimedIntoUnclaimed = other.fromClaimedIntoUnclaimed; 24 | this.fromUnclaimedIntoClaimed = other.fromUnclaimedIntoClaimed; 25 | } 26 | 27 | public boolean getShouldCancel(@Nullable UUID sourceOwner, @Nullable UUID newOwner) { 28 | // Full spread profile handles same-owner spreads 29 | if (!Objects.equals(sourceOwner, newOwner)) { 30 | // Disable block spread from unclaimed chunks into claimed chunks 31 | if (!fromUnclaimedIntoClaimed && sourceOwner == null) return true; 32 | 33 | // Disable block spread from claimed chunks into different claimed chunks 34 | if (!fromClaimedIntoDiffClaimed && newOwner != null && sourceOwner != null) return true; 35 | 36 | // Disable block spread from claimed chunks into unclaimed chunks 37 | //noinspection RedundantIfStatement 38 | if (!fromClaimedIntoUnclaimed && sourceOwner != null) return true; 39 | } 40 | 41 | // No need to cancel 42 | return false; 43 | } 44 | 45 | public void toCCConfig(@NotNull CCConfig config, @NotNull String key) { 46 | config.set(key + ".from_claimed.into_diff_claimed", fromClaimedIntoDiffClaimed); 47 | config.set(key + ".from_claimed.into_unclaimed", fromClaimedIntoUnclaimed); 48 | config.set(key + ".from_unclaimed.into_claimed", fromUnclaimedIntoClaimed); 49 | } 50 | 51 | public void fromCCConfig(@NotNull CCConfig config, @NotNull String key) { 52 | fromClaimedIntoDiffClaimed = 53 | config.getBool(key + ".from_claimed.into_diff_claimed", fromClaimedIntoDiffClaimed); 54 | fromClaimedIntoUnclaimed = 55 | config.getBool(key + ".from_claimed.into_unclaimed", fromClaimedIntoUnclaimed); 56 | fromUnclaimedIntoClaimed = 57 | config.getBool(key + ".from_unclaimed.into_claimed", fromUnclaimedIntoClaimed); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/event/PlayerConnectionHandler.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.event; 2 | 3 | import com.cjburkey.claimchunk.ClaimChunk; 4 | import com.cjburkey.claimchunk.Utils; 5 | import com.cjburkey.claimchunk.chunk.AutoClaimHandler; 6 | 7 | import net.md_5.bungee.api.ChatColor; 8 | import net.md_5.bungee.api.chat.*; 9 | import net.md_5.bungee.api.chat.hover.content.Text; 10 | 11 | import org.bukkit.event.EventHandler; 12 | import org.bukkit.event.Listener; 13 | import org.bukkit.event.player.PlayerJoinEvent; 14 | import org.bukkit.event.player.PlayerQuitEvent; 15 | 16 | import java.util.Objects; 17 | 18 | public class PlayerConnectionHandler implements Listener { 19 | 20 | private final ClaimChunk claimChunk; 21 | 22 | public PlayerConnectionHandler(ClaimChunk claimChunk) { 23 | this.claimChunk = claimChunk; 24 | } 25 | 26 | @EventHandler 27 | public void onPlayerJoin(PlayerJoinEvent e) { 28 | if (claimChunk.getConfigHandler().getCheckForUpdates() 29 | && claimChunk.isUpdateAvailable() 30 | && e.getPlayer().hasPermission("claimchunk.update")) { 31 | BaseComponent bc = 32 | new TextComponent( 33 | Utils.toComponent( 34 | e.getPlayer(), 35 | "&l&aAn update is available for ClaimChunk! Current version: &e" 36 | + claimChunk.getVersion() 37 | + "&a | Latest version: ")); 38 | TextComponent link = 39 | new TextComponent( 40 | Objects.requireNonNull(claimChunk.getAvailableVersion()).toString()); 41 | link.setColor(ChatColor.YELLOW); 42 | link.setUnderlined(true); 43 | link.setHoverEvent( 44 | new HoverEvent( 45 | HoverEvent.Action.SHOW_TEXT, 46 | new Text("Go to the ClaimChunk downloads page"))); 47 | link.setClickEvent( 48 | new ClickEvent( 49 | ClickEvent.Action.OPEN_URL, 50 | "https://github.com/cjburkey01/ClaimChunk/releases")); 51 | bc.addExtra(link); 52 | 53 | Utils.msg(e.getPlayer(), new TextComponent(bc)); 54 | } 55 | claimChunk.getPlayerHandler().onJoin(e.getPlayer()); 56 | } 57 | 58 | @EventHandler 59 | public void onPlayerLeave(PlayerQuitEvent e) { 60 | claimChunk.getAdminOverrideHandler().remove(e.getPlayer().getUniqueId()); 61 | AutoClaimHandler.disable(e.getPlayer()); 62 | claimChunk 63 | .getPlayerHandler() 64 | .setLastJoinedTime(e.getPlayer().getUniqueId(), System.currentTimeMillis()); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /changelogs/0.0.23-RC1.md: -------------------------------------------------------------------------------- 1 | # ClaimChunk 0.0.23-RC1 2 | 3 | This update is 1½ years in the making! There are a ton of changes that have a potential to 4 | collide with previous settings for your servers! 5 | 6 | Please make sure to back up your old configs and *don't delete your old ClaimChunk jar file; renaming it will be good 7 | after obtaining this **release candidate**(RC). In fact, I recommend renaming your config as well, allowing the new 8 | version to generate a new one with the correct options (otherwise, you will receive console spam when starting your server). 9 | 10 | A release candidate is basically like a snapshot for Minecraft, meaning things are likely to change and bugs are 11 | expected! If you run into any unexpected behavior, feel free to 12 | [create an issue](https://github.com/cjburkey01/ClaimChunk/issues) to let me know. 13 | 14 | Please note that this version **does not convert your old config values to the new world profile system** (more on that 15 | system in this changelog). 16 | 17 | Changes: 18 | * Update java version updated from 8 to 16 to match 1.16/1.17! 19 | * Fix version checker message (finally!). 20 | * Fixed host of annoying console errors. 21 | * World Profiles 22 | * Most protection settings are per-world now! 23 | * **Each world has a file located in `SERVER/plugins/ClaimChunk/worlds/` describing who has access in which particular chunks.** 24 | * This file contains instructions as well as default settings that disable interactions on entities and blocks within claimed chunks (not owned by the interacting player). 25 | * ClaimChunk can be disabled for each world in its file. 26 | * Commands 27 | * Many of the multi-word subcommands for `/claimchunk` and `/chunk` have been separated into different words! 28 | * Thanks to [a new command system by Goldmensch](https://github.com/Goldmensch/SmartCommandDispatcher). 29 | * The changed commands are: 30 | * Admin: 31 | * `/chunk admin override` 32 | * `/chunk admin reload` 33 | * `/chunk admin unclaim` 34 | * `/chunk admin unclaim all [ALL WORLDS? true/false]` 35 | * Player: 36 | * `/chunk unclaim all [ALL WORLDS? true/false]` 37 | * Added new commands: 38 | * `/chunk show claimed` 39 | * Shows nearby claimed chunks 40 | * Permission: `claimchunk.show-claimed` (default with `claimchunk.player`) 41 | * `/chunk scan [RADIUS IN CHUNKS]` 42 | * Counts number of nearby claims owned by other players. 43 | * Permission: `claimchunk.scan` (default with claimchunk.scan). 44 | * Permissions 45 | * `claimchunk.base` has been removed (finally, ugh, what was the point?). 46 | * Config 47 | * Removed much of the `protection` category and inserted into the world profiles. 48 | 49 | Changes to happen: 50 | * Switch to [a new message system handler also by Goldmensch](https://github.com/Goldmensch/JALL). 51 | * Per-world disallowed commands. 52 | -------------------------------------------------------------------------------- /src/test/java/com/cjburkey/claimchunk/PermFlagTests.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import com.cjburkey.claimchunk.flag.CCFlags; 6 | import com.cjburkey.claimchunk.flag.CCInteractClasses; 7 | import com.cjburkey.claimchunk.flag.CCPermFlags; 8 | 9 | import org.bukkit.configuration.file.YamlConfiguration; 10 | import org.junit.jupiter.api.Test; 11 | 12 | import java.io.StringReader; 13 | import java.util.Objects; 14 | 15 | public class PermFlagTests { 16 | 17 | @Test 18 | void testLoadFlags() { 19 | YamlConfiguration config = 20 | YamlConfiguration.loadConfiguration( 21 | new StringReader( 22 | """ 23 | permissionFlags: 24 | breakBlocks: 25 | - for: BLOCKS 26 | type: BREAK 27 | damageEntities: 28 | - for: ENTITIES 29 | type: DAMAGE 30 | redstone: 31 | - for: BLOCKS 32 | type: INTERACT 33 | include: ['@REDSTONE'] 34 | """)); 35 | CCPermFlags permFlags = new CCPermFlags(new CCInteractClasses(true)); 36 | permFlags.loadFromConfig(config); 37 | 38 | CCFlags.BlockFlagData breakBlocks = permFlags.blockControls.get("breakBlocks"); 39 | assertEquals(breakBlocks.flagType(), CCFlags.BlockFlagType.BREAK); 40 | 41 | CCFlags.EntityFlagData damageEntities = permFlags.entityControls.get("damageEntities"); 42 | assertEquals(damageEntities.flagType(), CCFlags.EntityFlagType.DAMAGE); 43 | 44 | assert Objects.requireNonNull( 45 | permFlags.blockControls.get("redstone").flagData().include(), 46 | "Missing include list") 47 | .contains("@REDSTONE"); 48 | } 49 | 50 | @Test 51 | void testTwo() { 52 | YamlConfiguration config = 53 | YamlConfiguration.loadConfiguration( 54 | new StringReader( 55 | """ 56 | permissionFlags: 57 | ruinStuff: 58 | - for: ENTITIES 59 | type: DAMAGE 60 | - for: BLOCKS 61 | type: BREAK 62 | """)); 63 | CCPermFlags permFlags = new CCPermFlags(new CCInteractClasses(true)); 64 | permFlags.loadFromConfig(config); 65 | 66 | assert permFlags.blockControls.containsKey("ruinStuff"); 67 | assert permFlags.entityControls.containsKey("ruinStuff"); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/Econ.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk; 2 | 3 | import net.milkbowl.vault.economy.Economy; 4 | import net.milkbowl.vault.economy.EconomyResponse; 5 | 6 | import org.bukkit.entity.Player; 7 | import org.bukkit.plugin.RegisteredServiceProvider; 8 | 9 | import java.util.UUID; 10 | 11 | public final class Econ { 12 | 13 | private Economy econ; 14 | private ClaimChunk instance; 15 | 16 | boolean setupEconomy(ClaimChunk instance) { 17 | this.instance = instance; 18 | 19 | // Check if Vault is present 20 | if (instance.getServer().getPluginManager().getPlugin("Vault") == null) return false; 21 | 22 | // Get the Vault service if it is present 23 | RegisteredServiceProvider rsp = 24 | instance.getServer().getServicesManager().getRegistration(Economy.class); 25 | 26 | // Check if the service is valid 27 | if (rsp == null) return false; 28 | 29 | // Update current economy handler 30 | econ = rsp.getProvider(); 31 | 32 | // Success 33 | return true; 34 | } 35 | 36 | public double getMoney(UUID player) { 37 | Player ply = getPlayer(player); 38 | // If the player has joined the server before, return their balance 39 | if (ply != null) { 40 | return econ.getBalance(ply); 41 | } 42 | return -1.0d; 43 | } 44 | 45 | @SuppressWarnings("UnusedReturnValue") 46 | public EconomyResponse addMoney(UUID player, double amt) { 47 | Player ply = getPlayer(player); 48 | if (ply != null) { 49 | // Add the (safe) balance to the player 50 | return econ.depositPlayer(ply, Math.abs(amt)); 51 | } 52 | return null; 53 | } 54 | 55 | @SuppressWarnings("UnusedReturnValue") 56 | private EconomyResponse takeMoney(UUID player, double amt) { 57 | Player ply = getPlayer(player); 58 | if (ply != null) { 59 | // Remove the money from the player's balance 60 | return econ.withdrawPlayer(ply, Math.abs(amt)); 61 | } 62 | return null; 63 | } 64 | 65 | /** 66 | * Take money from the player. 67 | * 68 | * @param ply Player purchasing. 69 | * @param cost The cost of the purchase. 70 | * @return Whether or not the transaction was successful. 71 | */ 72 | public boolean buy(UUID ply, double cost) { 73 | if (getMoney(ply) >= cost) { 74 | EconomyResponse response = takeMoney(ply, cost); 75 | // Return whether the transaction was completed successfully 76 | return response != null && response.type == EconomyResponse.ResponseType.SUCCESS; 77 | } 78 | return false; 79 | } 80 | 81 | public String format(double amt) { 82 | return econ.format(amt); 83 | } 84 | 85 | private Player getPlayer(UUID id) { 86 | if (instance == null) { 87 | return null; 88 | } 89 | return instance.getServer().getPlayer(id); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/event/PlayerEnterChunkEvent.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.event; 2 | 3 | import org.bukkit.Chunk; 4 | import org.bukkit.entity.Player; 5 | import org.bukkit.event.Cancellable; 6 | import org.bukkit.event.Event; 7 | import org.bukkit.event.HandlerList; 8 | import org.jetbrains.annotations.NotNull; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | import java.util.Objects; 12 | import java.util.UUID; 13 | 14 | /** An event broadcast to the server when a player enters a new chunk. */ 15 | public class PlayerEnterChunkEvent extends Event implements Cancellable { 16 | 17 | private static final HandlerList HANDLERS_LIST = new HandlerList(); 18 | 19 | private boolean cancelled; 20 | 21 | public final Player player; 22 | public final Chunk previousChunk; 23 | public final Chunk nextChunk; 24 | public final UUID previousOwner; 25 | public final UUID nextOwner; 26 | 27 | // Handy 28 | public final boolean chunksHaveSameOwner; 29 | public final boolean isPlayerPreviousOwner; 30 | public final boolean isPlayerNextOwner; 31 | 32 | /** 33 | * {@code previousChunk} and {@code nextChunk} should NEVER be the same, I WILL THROW A RUNTIME 34 | * EXCEPTION. 35 | * 36 | * @param player The player. 37 | * @param previousChunk The player's previous chunk. 38 | * @param nextChunk The player's next chunk. 39 | */ 40 | public PlayerEnterChunkEvent( 41 | @NotNull Player player, 42 | @NotNull Chunk previousChunk, 43 | @NotNull Chunk nextChunk, 44 | @Nullable UUID previousOwner, 45 | @Nullable UUID nextOwner) { 46 | // I'm sorry, but I have to be sure :| 47 | if (previousChunk.getX() == nextChunk.getX() && previousChunk.getZ() == nextChunk.getZ()) { 48 | throw new RuntimeException("previousChunk and nextChunk must be different!!!!"); 49 | } 50 | 51 | // The needies 52 | this.player = Objects.requireNonNull(player); 53 | this.previousChunk = Objects.requireNonNull(previousChunk); 54 | this.nextChunk = Objects.requireNonNull(nextChunk); 55 | this.previousOwner = previousOwner; 56 | this.nextOwner = nextOwner; 57 | 58 | // Calculate the handies 59 | this.chunksHaveSameOwner = Objects.equals(previousOwner, nextOwner); 60 | this.isPlayerPreviousOwner = player.getUniqueId().equals(previousOwner); 61 | this.isPlayerNextOwner = player.getUniqueId().equals(nextOwner); 62 | } 63 | 64 | @NotNull 65 | @Override 66 | public HandlerList getHandlers() { 67 | return HANDLERS_LIST; 68 | } 69 | 70 | @SuppressWarnings("unused") 71 | @NotNull 72 | public static HandlerList getHandlerList() { 73 | return HANDLERS_LIST; 74 | } 75 | 76 | @Override 77 | public boolean isCancelled() { 78 | return this.cancelled; 79 | } 80 | 81 | @Override 82 | public void setCancelled(boolean cancel) { 83 | this.cancelled = cancel; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/gui/screens/PermModifyMenu.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.gui.screens; 2 | 3 | import com.cjburkey.claimchunk.ClaimChunk; 4 | import com.cjburkey.claimchunk.ClaimChunkConfig; 5 | import com.cjburkey.claimchunk.gui.GuiMenuScreen; 6 | import com.cjburkey.claimchunk.i18n.V2JsonMessages; 7 | 8 | import org.bukkit.Material; 9 | import org.bukkit.entity.Player; 10 | import org.bukkit.inventory.Inventory; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | public class PermModifyMenu extends GuiMenuScreen { 17 | 18 | public final boolean isAllChunks; 19 | public final boolean isAllPlayers; 20 | 21 | public PermModifyMenu( 22 | @NotNull ClaimChunk claimChunk, 23 | @NotNull Player player, 24 | boolean isAllChunks, 25 | boolean isAllPlayers) { 26 | // Offset to make it 6 tall so that we can have the top row be a menu bar. 27 | super(claimChunk, player, 4, claimChunk.getMessages().guiPermSelectMenuTitle); 28 | 29 | this.isAllChunks = isAllChunks; 30 | this.isAllPlayers = isAllPlayers; 31 | } 32 | 33 | @Override 34 | public void onOpen(@NotNull Inventory inventory) { 35 | V2JsonMessages messages = claimChunk.getMessages(); 36 | ClaimChunkConfig config = claimChunk.getConfigHandler(); 37 | Material allowItem = materialFromStr(config.getGuiPermSelectMenuItem()); 38 | Material denyItem = materialFromStr(config.getGuiPermSelectMenuItem()); 39 | 40 | // Back button 41 | addInteractiveButton( 42 | inventory, 43 | 0, 44 | materialFromStr(config.getGuiMenuBackButtonItem()), 45 | messages.guiMenuBackButtonName, 46 | splitLineLore(messages.guiMenuBackButtonDesc), 47 | (clickType, stack) -> openGui(new PermSelectMenu(claimChunk, getPlayer()))); 48 | 49 | // Add permission buttons 50 | // TODO: THIS! 51 | Map permissions = 52 | claimChunk.getPermFlags().getAllFlags().stream() 53 | .collect( 54 | HashMap::new, (acc, flag) -> acc.put(flag, false), HashMap::putAll); 55 | 56 | int slot = 0; 57 | for (Map.Entry permission : permissions.entrySet()) { 58 | addInteractiveButton( 59 | inventory, 60 | 9 + slot, 61 | permission.getValue() ? allowItem : denyItem, 62 | "&r&f" + permission.getKey(), 63 | splitLineLore(messages.guiPermModifyPermDesc), 64 | (clickType, stack) -> {}); 65 | slot++; 66 | } 67 | } 68 | 69 | @Override 70 | public @NotNull String getName() { 71 | V2JsonMessages messages = claimChunk.getMessages(); 72 | return isAllChunks 73 | ? messages.guiPermModifyAllMenuTitle 74 | : messages.guiPermModifyThisMenuTitle; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/smartcommand/sub/ply/InfoCmd.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.smartcommand.sub.ply; 2 | 3 | import claimchunk.dependency.de.goldmensch.commanddispatcher.Executor; 4 | 5 | import com.cjburkey.claimchunk.ClaimChunk; 6 | import com.cjburkey.claimchunk.smartcommand.CCSubCommand; 7 | 8 | import org.bukkit.command.CommandSender; 9 | import org.bukkit.entity.Player; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | /** 14 | * @since 0.0.23 15 | */ 16 | public class InfoCmd extends CCSubCommand { 17 | 18 | public InfoCmd(ClaimChunk claimChunk) { 19 | super(claimChunk, Executor.PLAYER, true, "player", "info"); 20 | } 21 | 22 | @Override 23 | public @Nullable String getDescription() { 24 | return claimChunk.getMessages().cmdInfo; 25 | } 26 | 27 | @Override 28 | public CCArg[] getPermittedArguments() { 29 | return new CCArg[0]; 30 | } 31 | 32 | @Override 33 | public int getRequiredArguments() { 34 | return 0; 35 | } 36 | 37 | @Override 38 | public boolean onCall(@NotNull String cmdUsed, @NotNull CommandSender executor, String[] args) { 39 | var player = (Player) executor; 40 | var playerHandler = claimChunk.getPlayerHandler(); 41 | var chunk = player.getLocation().getChunk(); 42 | var owner = claimChunk.getChunkHandler().getOwner(chunk); 43 | 44 | var ownerName = ((owner == null) ? null : playerHandler.getUsername(owner)); 45 | if (ownerName == null) ownerName = claimChunk.getMessages().infoOwnerUnknown; 46 | 47 | var ownerDisplay = 48 | ((owner == null || !playerHandler.hasChunkName(owner)) 49 | ? null 50 | : playerHandler.getChunkName(owner)); 51 | if (ownerDisplay == null) ownerDisplay = claimChunk.getMessages().infoNameNone; 52 | 53 | messageChat( 54 | player, 55 | String.format( 56 | claimChunk.getMessages().infoHeader, // "%s&l--- [ %s ] ---", 57 | claimChunk.getConfigHandler().getInfoColor(), 58 | claimChunk.getMessages().infoTitle)); 59 | messageChat( 60 | player, 61 | claimChunk.getConfigHandler().getInfoColor() 62 | + (claimChunk 63 | .getMessages() 64 | .infoPosition 65 | .replace("%%X%%", "" + chunk.getX()) 66 | .replace("%%Z%%", "" + chunk.getZ()) 67 | .replace("%%WORLD%%", chunk.getWorld().getName()))); 68 | messageChat( 69 | player, 70 | claimChunk.getConfigHandler().getInfoColor() 71 | + claimChunk.getMessages().infoOwner.replace("%%PLAYER%%", ownerName)); 72 | messageChat( 73 | player, 74 | claimChunk.getConfigHandler().getInfoColor() 75 | + claimChunk.getMessages().infoName.replace("%%NAME%%", ownerDisplay)); 76 | return true; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/smartcommand/sub/ply/flags/CCPlyAccessCmd.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.smartcommand.sub.ply.flags; 2 | 3 | import claimchunk.dependency.de.goldmensch.commanddispatcher.Executor; 4 | 5 | import com.cjburkey.claimchunk.ClaimChunk; 6 | import com.cjburkey.claimchunk.chunk.ChunkPos; 7 | import com.cjburkey.claimchunk.smartcommand.CCSubCommand; 8 | 9 | import org.bukkit.command.CommandSender; 10 | import org.bukkit.entity.Player; 11 | import org.jetbrains.annotations.NotNull; 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | import java.util.Arrays; 15 | import java.util.UUID; 16 | 17 | public abstract class CCPlyAccessCmd extends CCSubCommand { 18 | 19 | public final boolean forPlayer; 20 | public final boolean forChunk; 21 | 22 | public CCPlyAccessCmd(@NotNull ClaimChunk claimChunk, boolean forPlayer, boolean forChunk) { 23 | super(claimChunk, Executor.PLAYER, true, "player", "access"); 24 | this.forPlayer = forPlayer; 25 | this.forChunk = forChunk; 26 | } 27 | 28 | protected abstract boolean handleAccess( 29 | @NotNull Player caller, 30 | @Nullable UUID otherPlayer, 31 | @Nullable ChunkPos chunkPos, 32 | @NotNull String[] arguments); 33 | 34 | @Override 35 | public abstract @NotNull String getDescription(); 36 | 37 | @Override 38 | public CCArg[] getPermittedArguments() { 39 | var perm = new CCArg("permissions", CCAutoComplete.PERMISSION); 40 | if (forPlayer) { 41 | return new CCArg[] {new CCArg("otherPlayer", CCAutoComplete.OFFLINE_PLAYER), perm}; 42 | } 43 | return new CCArg[] {perm}; 44 | } 45 | 46 | @Override 47 | public int getRequiredArguments() { 48 | return forPlayer ? 2 : 1; 49 | } 50 | 51 | @Override 52 | public boolean onCall( 53 | @NotNull String cmdUsed, @NotNull CommandSender executor, @NotNull String[] args) { 54 | Player caller = (Player) executor; 55 | 56 | UUID otherPlayer = null; 57 | if (forPlayer) { 58 | String plyName = args[0]; 59 | args = Arrays.copyOfRange(args, 1, args.length); 60 | otherPlayer = claimChunk.getPlayerHandler().getUUID(plyName); 61 | if (otherPlayer == null) { 62 | messagePly(caller, claimChunk.getMessages().noPlayer); 63 | return true; 64 | } 65 | } 66 | 67 | ChunkPos chunkPos = forChunk ? new ChunkPos(caller.getLocation().getChunk()) : null; 68 | return handleAccess(caller, otherPlayer, chunkPos, args); 69 | } 70 | 71 | protected final @NotNull String describe( 72 | @NotNull String plyChunk, 73 | @NotNull String ply, 74 | @NotNull String chunk, 75 | @NotNull String global) { 76 | if (forPlayer) { 77 | if (forChunk) { 78 | return plyChunk; 79 | } else { 80 | return ply; 81 | } 82 | } else { 83 | if (forChunk) { 84 | return chunk; 85 | } else { 86 | return global; 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/smartcommand/sub/ply/ShowClaimedCmd.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.smartcommand.sub.ply; 2 | 3 | import claimchunk.dependency.de.goldmensch.commanddispatcher.Executor; 4 | 5 | import com.cjburkey.claimchunk.ClaimChunk; 6 | import com.cjburkey.claimchunk.chunk.ChunkPos; 7 | import com.cjburkey.claimchunk.smartcommand.CCSubCommand; 8 | 9 | import org.bukkit.command.CommandSender; 10 | import org.bukkit.entity.Player; 11 | import org.jetbrains.annotations.NotNull; 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | import java.util.HashSet; 15 | 16 | /** 17 | * @since 0.0.23 18 | */ 19 | public class ShowClaimedCmd extends CCSubCommand { 20 | 21 | public int maxSeconds = 60; 22 | public int maxRadius = 6; 23 | 24 | public ShowClaimedCmd(ClaimChunk claimChunk) { 25 | super(claimChunk, Executor.PLAYER, true, "player", "show-claimed"); 26 | } 27 | 28 | @Override 29 | public @Nullable String getDescription() { 30 | return claimChunk.getMessages().cmdShow; 31 | } 32 | 33 | @Override 34 | public CCArg[] getPermittedArguments() { 35 | return new CCArg[] { 36 | new CCArg(claimChunk.getMessages().argRadius, CCAutoComplete.NONE), 37 | new CCArg(claimChunk.getMessages().argSeconds, CCAutoComplete.NONE) 38 | }; 39 | } 40 | 41 | @Override 42 | public int getRequiredArguments() { 43 | return 0; 44 | } 45 | 46 | @Override 47 | public boolean onCall(@NotNull String cmdUsed, @NotNull CommandSender executor, String[] args) { 48 | var player = (Player) executor; 49 | var chunkPos = new ChunkPos(player.getLocation().getChunk()); 50 | var showForSeconds = 5; 51 | var radius = 3; 52 | 53 | // Optional argument to show chunks within a given radius (up to the max defined) 54 | if (args.length >= 1) { 55 | try { 56 | radius = Integer.min(Integer.parseInt(args[0]), maxRadius); 57 | } catch (Exception e) { 58 | return false; 59 | } 60 | } 61 | // Optional argument to show for a given time (up to the max defined) 62 | if (args.length >= 2) { 63 | try { 64 | showForSeconds = Integer.min(Integer.parseInt(args[1]), maxSeconds); 65 | } catch (Exception e) { 66 | return false; 67 | } 68 | } 69 | 70 | // Create a set of this player's claimed chunks within the given radius 71 | HashSet claimedChunks = new HashSet<>(); 72 | for (var x = chunkPos.x() - radius; x <= chunkPos.x() + radius; x++) { 73 | for (var z = chunkPos.z() - radius; z <= chunkPos.z() + radius; z++) { 74 | if (claimChunk 75 | .getChunkHandler() 76 | .isOwner(player.getWorld(), x, z, player.getUniqueId())) { 77 | claimedChunks.add(new ChunkPos(player.getWorld().getName(), x, z)); 78 | } 79 | } 80 | } 81 | 82 | // Use the new particle system! 83 | claimChunk.getChunkOutlineHandler().showChunksFor(claimedChunks, player, showForSeconds); 84 | return true; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/config/ccconfig/CCConfigHandler.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.config.ccconfig; 2 | 3 | import com.cjburkey.claimchunk.Utils; 4 | 5 | import java.io.BufferedReader; 6 | import java.io.File; 7 | import java.io.FileReader; 8 | import java.io.FileWriter; 9 | import java.io.IOException; 10 | import java.util.function.BiConsumer; 11 | import java.util.function.Function; 12 | import java.util.stream.Collectors; 13 | 14 | public class CCConfigHandler { 15 | 16 | private final File configFile; 17 | private final ConfigType config; 18 | 19 | public CCConfigHandler(File configFile, ConfigType config) { 20 | this.configFile = configFile; 21 | this.config = config; 22 | } 23 | 24 | public ConfigType config() { 25 | return config; 26 | } 27 | 28 | public File file() { 29 | return configFile; 30 | } 31 | 32 | public boolean save(Function serializeConfig) { 33 | // Try to create the parent file if it doesn't exist 34 | if (configFile.getParentFile() != null 35 | && !configFile.getParentFile().exists() 36 | && !configFile.getParentFile().mkdirs()) { 37 | Utils.err( 38 | "Failed to create parent directory \"%s\" for file \"%s\"", 39 | configFile.getParent(), configFile.getAbsolutePath()); 40 | } 41 | 42 | // Check if the config can exist 43 | if (!configFile.exists() 44 | && (configFile.getParentFile() == null || !configFile.getParentFile().exists())) { 45 | Utils.err("Unable to save config to file \"%s\"", configFile.getAbsolutePath()); 46 | return false; 47 | } 48 | 49 | // Try to write the config to the file 50 | try (FileWriter writer = new FileWriter(configFile, false)) { 51 | writer.write(serializeConfig.apply(config)); 52 | return true; 53 | } catch (IOException e) { 54 | Utils.err("Error saving config to file \"%s\":", configFile.getAbsolutePath()); 55 | e.printStackTrace(); 56 | } 57 | 58 | // The file must not have been saved successfully 59 | return false; 60 | } 61 | 62 | public boolean load(BiConsumer deserializeConfig) { 63 | // Check if the config can exist 64 | if (!configFile.exists()) { 65 | Utils.err( 66 | "Unable to load config from file \"%s\" because it didn't exist", 67 | configFile.getAbsolutePath()); 68 | return false; 69 | } 70 | 71 | // Read the file 72 | try (BufferedReader reader = new BufferedReader(new FileReader(configFile))) { 73 | // Read the string, deserialize it into a CCConfig, then merge this 74 | // config with that config 75 | deserializeConfig.accept(reader.lines().collect(Collectors.joining("\n")), config); 76 | return true; 77 | } catch (IOException e) { 78 | Utils.err("Error loading config from file \"%s\":", configFile.getAbsolutePath()); 79 | e.printStackTrace(); 80 | } 81 | 82 | // The file must not have been loaded successfully 83 | return false; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/rank/RankHandler.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.rank; 2 | 3 | import com.cjburkey.claimchunk.ClaimChunk; 4 | import com.cjburkey.claimchunk.Utils; 5 | import com.cjburkey.claimchunk.data.JsonConfig; 6 | 7 | import org.bukkit.entity.Player; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | import java.io.File; 11 | 12 | // TODO: REPLACE WITH PER-PLAYER AMOUNT OF CHUNKS! 13 | // IT'S JUST BETTER ALL AROUND! 14 | public class RankHandler { 15 | 16 | // Info! 17 | private static final String RANK_FILE_HELP = 18 | """ 19 | This file lists all of the ranks for claimchunk. 20 | For more information, see this wiki page: 21 | https://github.com/cjburkey01/ClaimChunk/wiki/Ranks-System"""; 22 | 23 | private final JsonConfig ranks; 24 | private final ClaimChunk claimChunk; 25 | 26 | public RankHandler(File file, ClaimChunk claimChunk) { 27 | ranks = new JsonConfig<>(Rank[].class, file, true); 28 | this.claimChunk = claimChunk; 29 | } 30 | 31 | public void readFromDisk() { 32 | try { 33 | ranks.reloadData(); 34 | } catch (Exception e) { 35 | Utils.err("There was an error reading rank data!"); 36 | Utils.err("This means ranks WILL NOT WORK!"); 37 | Utils.err("Error: \"%s\"", e.getMessage()); 38 | } 39 | for (Rank rank : ranks) { 40 | if (rank.claims < 1) rank.claims = 1; 41 | rank.getPerm(); 42 | } 43 | if (!ranks.file.exists()) { 44 | // Create the example ranks file 45 | ranks.addData(new Rank("some_random_example_rank", 100)); 46 | ranks.addData(new Rank("another_random_example_rank", 200)); 47 | } 48 | try { 49 | // Save with the info header comment 50 | ranks.saveData(RANK_FILE_HELP); 51 | } catch (Exception e) { 52 | Utils.err("Failed to save rank data!"); 53 | Utils.err("This means ranks WILL BE DELETED!!!"); 54 | Utils.err("Error:"); 55 | //noinspection CallToPrintStackTrace 56 | e.printStackTrace(); 57 | Utils.err("Current rank print: \"\"", ranks.toString()); 58 | } 59 | } 60 | 61 | public int getMaxClaimsForPlayer(@Nullable Player player) { 62 | int defaultMax = claimChunk.getConfigHandler().getDefaultMaxChunksClaimed(); 63 | if (defaultMax <= 0) { 64 | defaultMax = Integer.MAX_VALUE; 65 | } 66 | if (player == null) { 67 | return defaultMax; 68 | } 69 | 70 | int maxClaims = -1; 71 | boolean hadRank = false; 72 | for (Rank rank : ranks) { 73 | // Don't use Utils.hasPerm because it returns true for admins! 74 | if (player.hasPermission(rank.getPerm())) { 75 | if (rank.claims <= 0) return -1; 76 | maxClaims = Integer.max(maxClaims, rank.claims); 77 | hadRank = true; 78 | } 79 | } 80 | 81 | // All that work, and this is the load-bearing line :) 82 | int preExtra = hadRank ? maxClaims : defaultMax; 83 | return preExtra + claimChunk.getPlayerHandler().getMaxClaims(player.getUniqueId()); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /src/main/java/claimchunk/dependency/de/goldmensch/commanddispatcher/subcommand/SmartSubCommand.java: -------------------------------------------------------------------------------- 1 | package claimchunk.dependency.de.goldmensch.commanddispatcher.subcommand; 2 | 3 | import claimchunk.dependency.de.goldmensch.commanddispatcher.Executor; 4 | 5 | import lombok.Getter; 6 | 7 | import org.bukkit.command.CommandExecutor; 8 | import org.bukkit.command.CommandSender; 9 | import org.jetbrains.annotations.ApiStatus; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | import java.util.Arrays; 13 | 14 | public abstract class SmartSubCommand implements CommandExecutor { 15 | 16 | private final Executor executor; 17 | @Getter private final String[] permissions; 18 | private String name; 19 | @Getter private String description; 20 | 21 | // vararg 22 | public SmartSubCommand(@NotNull Executor executor, String... permissions) { 23 | this.executor = executor; 24 | this.permissions = permissions; 25 | } 26 | 27 | @ApiStatus.Internal 28 | public void setName(@NotNull String name) { 29 | this.name = name; 30 | } 31 | 32 | @ApiStatus.Internal 33 | public void setDescription(@NotNull String description) { 34 | this.description = description; 35 | } 36 | 37 | /*** 38 | * @return The {@link Executor} of the SubCommand 39 | */ 40 | public @NotNull Executor getExecutor() { 41 | return executor; 42 | } 43 | 44 | /*** 45 | * Checks if the provided player should be able to execute a given command. 46 | * 47 | * @param sender The player whose permission we need to check. 48 | * @return Whether the player should be allowed to execute this command. 49 | */ 50 | public boolean rightPermission(@NotNull CommandSender sender) { 51 | return permissions.length == 0 52 | || Arrays.stream(permissions).anyMatch(sender::hasPermission); 53 | } 54 | 55 | /*** 56 | * Checks if the provided command executor should be able to execute a given command. 57 | * 58 | * @param sender The sender whose executor type. 59 | * @return Whether the executor should be allowed to execute this command. 60 | */ 61 | public boolean rightExecutor(@NotNull CommandSender sender) { 62 | Executor senderExecutor = Executor.fromSender(sender); 63 | return getExecutor() == Executor.CONSOLE_PLAYER || senderExecutor == getExecutor(); 64 | } 65 | 66 | /*** 67 | * Checks if the provided sender should be able to execute a given command. If the console is 68 | * the executor, {@code true} is always returned. 69 | * 70 | * @param sender The sender whose permission we need to check. 71 | * @return Whether the given sender should be allowed to execute this command based on type and permission. 72 | */ 73 | public boolean rightExecutorAndPermission(@NotNull CommandSender sender) { 74 | Executor senderExecutor = Executor.fromSender(sender); 75 | return rightExecutor(sender) 76 | && (senderExecutor == Executor.CONSOLE || rightPermission(sender)); 77 | } 78 | 79 | /*** 80 | *

The name is a string consisting of the path and the name.

81 | *

Example: The StringArray {"arg0", "arg1", "name"} becomes "arg0 arg1 name"

82 | * @return The name of the SubCommand 83 | */ 84 | public @NotNull String getName() { 85 | return name; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/gui/CCGuiHandler.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.gui; 2 | 3 | import com.cjburkey.claimchunk.Utils; 4 | 5 | import org.bukkit.Bukkit; 6 | import org.bukkit.entity.Player; 7 | import org.bukkit.event.EventHandler; 8 | import org.bukkit.event.Listener; 9 | import org.bukkit.event.inventory.InventoryClickEvent; 10 | import org.bukkit.event.inventory.InventoryCloseEvent; 11 | import org.bukkit.inventory.Inventory; 12 | import org.jetbrains.annotations.NotNull; 13 | import org.jetbrains.annotations.Nullable; 14 | 15 | import java.util.HashMap; 16 | import java.util.UUID; 17 | 18 | /** 19 | * The handler behind ClaimChunk GUI screens. This class acts as the interface for opening GUIs as 20 | * well as the event listener for players clicking in/closing the screens. 21 | * 22 | * @since 0.0.26 23 | */ 24 | public class CCGuiHandler implements Listener { 25 | 26 | private final HashMap openGuis = new HashMap<>(); 27 | 28 | @EventHandler 29 | public void onGuiClick(InventoryClickEvent e) { 30 | UUID uuid = e.getWhoClicked().getUniqueId(); 31 | CCOpenGui openGui = openGuis.get(uuid); 32 | if (openGui != null) { 33 | if (e.getInventory().equals(openGui.inventory()) 34 | && e.getSlot() >= 0 35 | && e.getSlot() < e.getInventory().getSize()) { 36 | openGui.gui() 37 | .onClick( 38 | openGui.inventory(), e.getSlot(), e.getClick(), e.getCurrentItem()); 39 | } 40 | e.setCancelled(true); 41 | } 42 | } 43 | 44 | @EventHandler 45 | public void onGuiClose(InventoryCloseEvent e) { 46 | if (openGuis.containsKey(e.getPlayer().getUniqueId())) { 47 | closeGui((Player) e.getPlayer()); 48 | } 49 | } 50 | 51 | public void openOrRefreshGui(@NotNull ICCGui gui) { 52 | Player player = gui.getPlayer(); 53 | CCOpenGui openGui = closeGui(player, false); 54 | 55 | final Inventory inventory; 56 | if (openGui != null && openGui.gui() == gui) { 57 | inventory = openGui.inventory(); 58 | inventory.clear(); 59 | } else { 60 | inventory = createAndShowGui(player, gui); 61 | } 62 | 63 | openGuis.put(player.getUniqueId(), new CCOpenGui(gui, inventory)); 64 | gui.onOpen(openGuis.get(player.getUniqueId()).inventory()); 65 | } 66 | 67 | public void closeGui(@NotNull Player player) { 68 | closeGui(player, true); 69 | } 70 | 71 | private @Nullable CCOpenGui closeGui(@NotNull Player player, boolean removeFromMap) { 72 | CCOpenGui openGui = openGuis.get(player.getUniqueId()); 73 | if (openGui != null) { 74 | openGui.gui().onClose(openGuis.get(player.getUniqueId()).inventory()); 75 | if (removeFromMap) { 76 | openGuis.remove(player.getUniqueId()); 77 | } 78 | } 79 | return openGui; 80 | } 81 | 82 | private static Inventory createAndShowGui(@NotNull Player player, @NotNull ICCGui gui) { 83 | int rowCount = Math.min(Math.max(gui.getRows(), 1), 6); 84 | Inventory inventory = 85 | Bukkit.createInventory(player, rowCount * 9, Utils.color(gui.getName())); 86 | player.openInventory(inventory); 87 | return inventory; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at bulletlanguage@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/config/ccconfig/NSKey.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.config.ccconfig; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Arrays; 8 | import java.util.List; 9 | import java.util.Objects; 10 | 11 | public class NSKey { 12 | 13 | private final ArrayList category; 14 | public final String key; 15 | 16 | public NSKey(@Nullable String namespacedKey) { 17 | category = new ArrayList<>(); 18 | if (namespacedKey == null) { 19 | key = ""; 20 | return; 21 | } 22 | 23 | String[] split = namespacedKey.split("\\."); 24 | if (split.length <= 0) { 25 | key = ""; 26 | } else { 27 | key = split[split.length - 1]; 28 | category.addAll(Arrays.asList(split).subList(0, split.length - 1)); 29 | } 30 | } 31 | 32 | @SuppressWarnings("unused") 33 | public List category() { 34 | return category; 35 | } 36 | 37 | @SuppressWarnings("unused") 38 | public String categories() { 39 | return String.join(".", category); 40 | } 41 | 42 | @SuppressWarnings("unused") 43 | public List parentCategory() { 44 | if (category.size() <= 0) return new ArrayList<>(); 45 | return category.subList(1, category.size()); 46 | } 47 | 48 | @SuppressWarnings("unused") 49 | public boolean isParent(@NotNull List otherCat) { 50 | // Check if the provided category is a parent of this one 51 | for (int i = 0; i < category.size() && i < otherCat.size(); i++) { 52 | if (!otherCat.get(i).equals(category.get(i))) { 53 | return i > 0; 54 | } 55 | } 56 | 57 | return true; 58 | } 59 | 60 | @SuppressWarnings("unused") 61 | public @NotNull List getRelativeCat(@NotNull List otherCat) { 62 | // Keep track of the same path elements. 63 | List same = new ArrayList<>(category()); 64 | int sameCount = 0; 65 | for (int i = 0; i < same.size() && i < otherCat.size(); i++) { 66 | if (!otherCat.get(i).equals(same.get(i))) break; 67 | sameCount++; 68 | } 69 | 70 | // If there are no elements that are the same, the path must be 71 | // absolute 72 | if (sameCount == 0) { 73 | same.add(0, "<<"); 74 | return same; 75 | } 76 | 77 | // Cut off the parts of the label that were the same 78 | same = same.subList(sameCount, same.size()); 79 | 80 | // Add the up level symbols 81 | for (int i = 0; i < (category().size() - sameCount); i++) { 82 | same.add(0, "<"); 83 | } 84 | 85 | return same; 86 | } 87 | 88 | @Override 89 | public String toString() { 90 | StringBuilder output = new StringBuilder(); 91 | for (String cat : category) output.append(cat).append('.'); 92 | return output.append(key).toString(); 93 | } 94 | 95 | @Override 96 | public boolean equals(Object o) { 97 | if (this == o) { 98 | return true; 99 | } 100 | if (o == null || getClass() != o.getClass()) { 101 | return false; 102 | } 103 | NSKey nsKey = (NSKey) o; 104 | return category.equals(nsKey.category) && Objects.equals(key, nsKey.key); 105 | } 106 | 107 | @Override 108 | public int hashCode() { 109 | return Objects.hash(category, key); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/player/PlayerHandler.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.player; 2 | 3 | import com.cjburkey.claimchunk.ClaimChunk; 4 | import com.cjburkey.claimchunk.data.newdata.IClaimChunkDataHandler; 5 | 6 | import org.bukkit.entity.Player; 7 | 8 | import java.util.*; 9 | 10 | public class PlayerHandler { 11 | 12 | private final IClaimChunkDataHandler dataHandler; 13 | private final ClaimChunk claimChunk; 14 | 15 | public PlayerHandler(IClaimChunkDataHandler dataHandler, ClaimChunk claimChunk) { 16 | this.dataHandler = dataHandler; 17 | this.claimChunk = claimChunk; 18 | } 19 | 20 | public Collection getJoinedPlayers() { 21 | return dataHandler.getPlayers(); 22 | } 23 | 24 | public List getJoinedPlayersFromName(String start) { 25 | List out = new ArrayList<>(); 26 | for (SimplePlayerData ply : dataHandler.getPlayers()) { 27 | if (ply.lastIgn() != null 28 | && ply.lastIgn().toLowerCase().startsWith(start.toLowerCase())) { 29 | out.add(ply.lastIgn()); 30 | } 31 | } 32 | return out; 33 | } 34 | 35 | public boolean toggleAlerts(UUID player) { 36 | boolean newVal = !hasAlerts(player); 37 | dataHandler.setPlayerReceiveAlerts(player, newVal); 38 | return newVal; 39 | } 40 | 41 | public boolean hasAlerts(UUID owner) { 42 | return dataHandler.getPlayerReceiveAlerts(owner); 43 | } 44 | 45 | public void setChunkName(UUID owner, String name) { 46 | dataHandler.setPlayerChunkName(owner, name); 47 | } 48 | 49 | public void clearChunkName(UUID owner) { 50 | setChunkName(owner, null); 51 | } 52 | 53 | public String getChunkName(UUID owner) { 54 | String chunkName = dataHandler.getPlayerChunkName(owner); 55 | if (chunkName != null) return chunkName; 56 | return dataHandler.getPlayerUsername(owner); 57 | } 58 | 59 | public boolean hasChunkName(UUID owner) { 60 | return dataHandler.getPlayerChunkName(owner) != null; 61 | } 62 | 63 | public String getUsername(UUID player) { 64 | return dataHandler.getPlayerUsername(player); 65 | } 66 | 67 | public UUID getUUID(String username) { 68 | return dataHandler.getPlayerUUID(username); 69 | } 70 | 71 | public void setLastJoinedTime(UUID player, long time) { 72 | dataHandler.setPlayerLastOnline(player, time); 73 | } 74 | 75 | public void setMaxClaims(UUID player, int maxClaims) { 76 | dataHandler.setPlayerExtraMaxClaims(player, maxClaims); 77 | } 78 | 79 | // Use negative to take 80 | public void addOrTakeMaxClaims(UUID player, int claimsToAdd) { 81 | if (claimsToAdd > 0) { 82 | dataHandler.addPlayerExtraMaxClaims(player, claimsToAdd); 83 | } else if (claimsToAdd < 0) { 84 | dataHandler.takePlayerExtraMaxClaims(player, -claimsToAdd); 85 | } 86 | } 87 | 88 | public int getMaxClaims(UUID player) { 89 | return dataHandler.getPlayerExtraMaxClaims(player); 90 | } 91 | 92 | public void onJoin(Player ply) { 93 | UUID uuid = ply.getUniqueId(); 94 | if (dataHandler.hasPlayer(uuid)) { 95 | dataHandler.setPlayerLastOnline(uuid, System.currentTimeMillis()); 96 | } else { 97 | dataHandler.addPlayer( 98 | uuid, 99 | ply.getName(), 100 | claimChunk.getConfigHandler().getDefaultSendAlertsToOwner(), 101 | 0); 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ "main" ] 17 | pull_request: 18 | branches: [ "main" ] 19 | schedule: 20 | - cron: '25 3 * * 4' 21 | 22 | jobs: 23 | analyze: 24 | name: Analyze (${{ matrix.language }}) 25 | # Runner size impacts CodeQL analysis time. To learn more, please see: 26 | # - https://gh.io/recommended-hardware-resources-for-running-codeql 27 | # - https://gh.io/supported-runners-and-hardware-resources 28 | # - https://gh.io/using-larger-runners (GitHub.com only) 29 | # Consider using larger runners or machines with greater resources for possible analysis time improvements. 30 | runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} 31 | timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} 32 | permissions: 33 | # required for all workflows 34 | security-events: write 35 | 36 | # required to fetch internal or private CodeQL packs 37 | packages: read 38 | 39 | # only required for workflows in private repositories 40 | actions: read 41 | contents: read 42 | 43 | strategy: 44 | fail-fast: false 45 | matrix: 46 | include: 47 | - language: java-kotlin 48 | build-mode: autobuild 49 | steps: 50 | - name: Checkout repository 51 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0 52 | 53 | # Initializes the CodeQL tools for scanning. 54 | - name: Initialize CodeQL 55 | uses: github/codeql-action/init@16140ae1a102900babc80a33c44059580f687047 #v4.30.9 56 | with: 57 | languages: ${{ matrix.language }} 58 | build-mode: ${{ matrix.build-mode }} 59 | # If you wish to specify custom queries, you can do so here or in a config file. 60 | # By default, queries listed here will override any specified in a config file. 61 | # Prefix the list here with "+" to use these queries and those in the config file. 62 | 63 | # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 64 | # queries: security-extended,security-and-quality 65 | 66 | # If the analyze step fails for one of the languages you are analyzing with 67 | # "We were unable to automatically build your code", modify the matrix above 68 | # to set the build mode to "manual" for that language. Then modify this step 69 | # to build your code. 70 | # ℹ️ Command-line programs to run using the OS shell. 71 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 72 | - if: matrix.build-mode == 'manual' 73 | run: | 74 | echo 'If you are using a "manual" build mode for one or more of the' \ 75 | 'languages you are analyzing, replace this with the commands to build' \ 76 | 'your code, for example:' 77 | echo ' ./gradlew build' 78 | exit 1 79 | 80 | - name: Perform CodeQL Analysis 81 | uses: github/codeql-action/analyze@16140ae1a102900babc80a33c44059580f687047 #v4.30.9 82 | with: 83 | category: "/language:${{matrix.language}}" 84 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/smartcommand/sub/ply/ScanCmd.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.smartcommand.sub.ply; 2 | 3 | import claimchunk.dependency.de.goldmensch.commanddispatcher.Executor; 4 | 5 | import com.cjburkey.claimchunk.ClaimChunk; 6 | import com.cjburkey.claimchunk.smartcommand.CCSubCommand; 7 | 8 | import org.bukkit.Chunk; 9 | import org.bukkit.command.CommandSender; 10 | import org.bukkit.entity.Player; 11 | import org.jetbrains.annotations.NotNull; 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | public class ScanCmd extends CCSubCommand { 18 | 19 | public ScanCmd(ClaimChunk claimChunk) { 20 | super(claimChunk, Executor.PLAYER, true, "player", "scan"); 21 | } 22 | 23 | @Override 24 | public @Nullable String getDescription() { 25 | return claimChunk.getMessages().cmdScan; 26 | } 27 | 28 | @Override 29 | public CCArg[] getPermittedArguments() { 30 | return new CCArg[] { 31 | new CCArg(claimChunk.getMessages().argScanDistance, CCAutoComplete.NONE), 32 | }; 33 | } 34 | 35 | @Override 36 | public int getRequiredArguments() { 37 | return 0; 38 | } 39 | 40 | @Override 41 | public boolean onCall(@NotNull String cmdUsed, @NotNull CommandSender executor, String[] args) { 42 | final Player player = (Player) executor; 43 | final Chunk playerChunk = player.getWorld().getChunkAt(player.getLocation()); 44 | 45 | List nearbyChunks = new ArrayList<>(); 46 | int near = claimChunk.getConfigHandler().getNearChunkSearch(); 47 | 48 | if (args.length > 0 && isInteger(args[0], 10)) near = Integer.parseInt(args[0]); 49 | 50 | if (near > claimChunk.getConfigHandler().getMaxScanRange()) { 51 | messagePly( 52 | player, 53 | claimChunk 54 | .getMessages() 55 | .scanInputTooBig 56 | .replace( 57 | "%%MAXAREA%%", 58 | String.valueOf( 59 | claimChunk.getConfigHandler().getMaxScanRange()))); 60 | return true; 61 | } 62 | 63 | if (near < 1) return true; 64 | 65 | int min = (near - 1) / 2; 66 | int max = (near - 1) / 2 + 1; 67 | 68 | for (int x1 = -min; x1 < max; x1++) { 69 | for (int z1 = -min; z1 < max; z1++) { 70 | 71 | final Chunk chunk = 72 | player.getWorld() 73 | .getChunkAt(x1 + playerChunk.getX(), z1 + playerChunk.getZ()); 74 | 75 | if (claimChunk.getChunkHandler().isClaimed(chunk) 76 | && !claimChunk.getChunkHandler().isOwner(chunk, player)) 77 | nearbyChunks.add(chunk); 78 | } 79 | } 80 | 81 | messagePly( 82 | player, 83 | claimChunk 84 | .getMessages() 85 | .claimsFound 86 | .replace("%%NEARBY%%", String.valueOf(nearbyChunks.size())) 87 | .replace("%%RADIUS%%", String.valueOf(near))); 88 | return true; 89 | } 90 | 91 | public boolean isInteger(String s, int radix) { 92 | if (s.isEmpty()) return false; 93 | for (int i = 0; i < s.length(); i++) { 94 | if (i == 0 && s.charAt(i) == '-') { 95 | if (s.length() == 1) return false; 96 | else continue; 97 | } 98 | if (Character.digit(s.charAt(i), radix) < 0) return false; 99 | } 100 | return true; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/gui/screens/MainMenu.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.gui.screens; 2 | 3 | import com.cjburkey.claimchunk.ClaimChunk; 4 | import com.cjburkey.claimchunk.chunk.ChunkPos; 5 | import com.cjburkey.claimchunk.gui.GuiMenuScreen; 6 | 7 | import org.bukkit.entity.Player; 8 | import org.bukkit.inventory.Inventory; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | import java.util.ArrayList; 12 | import java.util.UUID; 13 | 14 | public class MainMenu extends GuiMenuScreen { 15 | 16 | // +---------+ 17 | // |XX2X4X6XX| 18 | // +---------+ 19 | // 2: Current chunk info 20 | // 4: Chunk map 21 | // 6: Chunk permissions 22 | 23 | public MainMenu(ClaimChunk claimChunk, Player player) { 24 | super(claimChunk, player, 1, claimChunk.getMessages().guiMainMenuTitle); 25 | } 26 | 27 | @Override 28 | public void onOpen(@NotNull Inventory inventory) { 29 | addCurrentChunkItem(inventory); 30 | addMapItem(inventory); 31 | addPermsItem(inventory); 32 | } 33 | 34 | private void addCurrentChunkItem(@NotNull Inventory inventory) { 35 | ChunkPos chunkPos = new ChunkPos(getPlayer().getLocation().getChunk()); 36 | UUID chunkOwner = claimChunk.getChunkHandler().getOwner(chunkPos); 37 | 38 | ArrayList lore = new ArrayList<>(); 39 | 40 | lore.add(guiChunkPosText(chunkPos)); 41 | if (chunkOwner != null) { 42 | lore.add(guiChunkOwnerNameText(chunkNameOrUnknown(chunkOwner))); 43 | if (chunkOwner.equals(getPlayer().getUniqueId())) { 44 | lore.add(""); 45 | lore.add(claimChunk.getMessages().guiClickToUnclaim); 46 | } 47 | } else { 48 | lore.add(claimChunk.getMessages().guiNotClaimed); 49 | lore.add(""); 50 | lore.add(claimChunk.getMessages().guiClickToClaim); 51 | } 52 | 53 | addInteractiveButton( 54 | inventory, 55 | 2, 56 | materialFromStr(claimChunk.getConfigHandler().getGuiMainMenuCurrentChunkItem()), 57 | claimChunk.getMessages().guiMainMenuCurrentChunkItemName, 58 | lore, 59 | (clickType, stack) -> { 60 | if (clickType.isLeftClick()) { 61 | claimChunk.getMainHandler().claimChunk(getPlayer(), chunkPos); 62 | refresh(); 63 | } else if (clickType.isRightClick()) { 64 | claimChunk 65 | .getMainHandler() 66 | .unclaimChunk( 67 | false, 68 | false, 69 | getPlayer(), 70 | chunkPos.world(), 71 | chunkPos.x(), 72 | chunkPos.z()); 73 | refresh(); 74 | } 75 | }); 76 | } 77 | 78 | private void addMapItem(@NotNull Inventory inventory) { 79 | addInteractiveButton( 80 | inventory, 81 | 4, 82 | materialFromStr(claimChunk.getConfigHandler().getGuiMainMenuChunkMapItem()), 83 | claimChunk.getMessages().guiMainMenuMapItemName, 84 | splitLineLore(claimChunk.getMessages().guiMainMenuMapItemDesc), 85 | (clickType, stack) -> openGui(new MapMenu(claimChunk, getPlayer()))); 86 | } 87 | 88 | private void addPermsItem(@NotNull Inventory inventory) { 89 | addInteractiveButton( 90 | inventory, 91 | 6, 92 | materialFromStr(claimChunk.getConfigHandler().getGuiMainMenuPermFlagsItem()), 93 | claimChunk.getMessages().guiMainMenuPermFlagsItemName, 94 | splitLineLore(claimChunk.getMessages().guiMainMenuPermFlagsDescription), 95 | (clickType, stack) -> openGui(new PermSelectMenu(claimChunk, getPlayer()))); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/api/IClaimChunkPlugin.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.api; 2 | 3 | import com.cjburkey.claimchunk.ClaimChunkConfig; 4 | import com.cjburkey.claimchunk.chunk.ChunkHandler; 5 | import com.cjburkey.claimchunk.config.ClaimChunkWorldProfileHandler; 6 | import com.cjburkey.claimchunk.i18n.V2JsonMessages; 7 | import com.cjburkey.claimchunk.layer.PrereqsInitLayer; 8 | import com.cjburkey.claimchunk.player.AdminOverride; 9 | import com.cjburkey.claimchunk.player.PlayerHandler; 10 | import com.cjburkey.claimchunk.rank.RankHandler; 11 | import com.cjburkey.claimchunk.update.SemVer; 12 | 13 | import org.bukkit.Server; 14 | import org.bukkit.configuration.file.FileConfiguration; 15 | import org.bukkit.plugin.PluginDescriptionFile; 16 | import org.jetbrains.annotations.NotNull; 17 | 18 | import java.io.File; 19 | 20 | public interface IClaimChunkPlugin { 21 | 22 | /* Info */ 23 | 24 | /** 25 | * Get an instance of the Bukkit server class. 26 | * 27 | * @return Get the server 28 | */ 29 | Server getServer(); 30 | 31 | /** 32 | * The version for ClaimChunk. 33 | * 34 | * @return The version for this installation of ClaimChunk. 35 | */ 36 | SemVer getVersion(); 37 | 38 | /** 39 | * The latest release of ClaimChunk online. 40 | * 41 | * @return The latest GitHub release for ClaimChunk. 42 | */ 43 | SemVer getAvailableVersion(); 44 | 45 | /* Initialization layers */ 46 | 47 | /** 48 | * Get an instance of the layer responsible for handling the prereq system 49 | * 50 | * @return A non-null instance of the PrereqChecker for chunk claiming. 51 | */ 52 | PrereqsInitLayer getPrereqHandlerLayer(); 53 | 54 | /* Handlers */ 55 | 56 | /** 57 | * Get the ClaimChunk config handler. 58 | * 59 | * @return The loaded config file for ClaimChunk 60 | */ 61 | ClaimChunkConfig getConfigHandler(); 62 | 63 | /** 64 | * Get the translations to use for messages. 65 | * 66 | * @return The messages handler. 67 | */ 68 | V2JsonMessages getMessages(); 69 | 70 | /** 71 | * Get the instance of the ClaimChunk world profile handler. 72 | * 73 | * @return The instance for this given instance of ClaimChunk. 74 | */ 75 | ClaimChunkWorldProfileHandler getProfileHandler(); 76 | 77 | /** 78 | * Get the chunk handler for this instance. 79 | * 80 | * @return The ClaimChunk chunk handler. 81 | */ 82 | ChunkHandler getChunkHandler(); 83 | 84 | /** 85 | * Get the instance of the player information handler. 86 | * 87 | * @return The ClaimChunk player handler. 88 | */ 89 | PlayerHandler getPlayerHandler(); 90 | 91 | /** 92 | * Get the instance of the rank handler. 93 | * 94 | * @return The rank handler. 95 | */ 96 | RankHandler getRankHandler(); 97 | 98 | /** 99 | * Get the instance of the handler for admin override info. 100 | * 101 | * @return The list of players with admin override enabled. 102 | */ 103 | AdminOverride getAdminOverrideHandler(); 104 | 105 | /* Management */ 106 | 107 | /** Disable the ClaimChunk plugin. */ 108 | void disable(); 109 | 110 | /* Spigot API */ 111 | 112 | /** 113 | * Get the plugin descriptor file information. 114 | * 115 | * @return Information regarding the plugin. 116 | */ 117 | PluginDescriptionFile getDescription(); 118 | 119 | /** 120 | * Get the raw {@code config.yml} YML handler for ClaimChunk. 121 | * 122 | * @return The FileConfiguration for ClaimChunk's config file. 123 | */ 124 | @NotNull 125 | FileConfiguration getConfig(); 126 | 127 | /** 128 | * Get the location for this plugins data. This should be {@code ./plugins/ClaimChunk}. 129 | * 130 | * @return The direction for this plugin's files. 131 | */ 132 | @NotNull 133 | File getDataFolder(); 134 | 135 | /** Save the modified FileConfiguration for ClaimChunk. */ 136 | void saveConfig(); 137 | } 138 | -------------------------------------------------------------------------------- /src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | # noinspection YAMLSchemaValidation 2 | name: '@PLUGIN_NAME@' 3 | # noinspection YAMLSchemaValidation 4 | main: '@MAIN_CLASS@' 5 | authors: 6 | - 'CJ Burkey' 7 | - 'Goldmensch' 8 | - 'DeathsGun' 9 | - 'T0biii' 10 | - 'Geolykt' 11 | - 'JustDoom' 12 | - 'AlexFF000' 13 | database: true 14 | prefix: 'ClaimChunk' 15 | version: '@PLUGIN_VERSION@' 16 | website: 'https://www.spigotmc.org/resources/claimchunk.44458/' 17 | api-version: '1.13' 18 | softdepend: 19 | - 'WorldGuard' 20 | - 'Vault' 21 | - 'PlaceholderAPI' 22 | # Temporary fix for economy not being loaded lazily, add support for this 23 | # specific economy plugin 24 | - 'RetroConomy' 25 | 26 | permission: 27 | # Basic permissions (granted by default without permission plugin) 28 | claimchunk.help: 29 | description: 'Allow use of the help subcommand.' 30 | default: true 31 | claimchunk.claim: 32 | description: 'Allow claiming of chunks.' 33 | default: true 34 | claimchunk.unclaim: 35 | description: 'Allow unclaiming of one''s own chunks.' 36 | default: true 37 | claimchunk.access: 38 | description: 'Allow giving access to other players on one''s chunks.' 39 | default: true 40 | claimchunk.alert: 41 | description: 'Allow players to toggle receiving alerts when another player enters their chunks.' 42 | default: true 43 | claimchunk.give: 44 | description: 'Allow players to give chunks to other players' 45 | default: true 46 | claimchunk.name: 47 | description: 'Allow players to rename their chunks'' display name' 48 | default: true 49 | claimchunk.show: 50 | description: 'Outline the current chunk' 51 | default: true 52 | claimchunk.show-claimed: 53 | description: 'Outline nearby claimed chunks with particles' 54 | default: true 55 | claimchunk.scan: 56 | description: 'Allow players to scan nearby chunks for claims' 57 | default: true 58 | claimchunk.info: 59 | description: 'Get info for the current chunk' 60 | default: true 61 | claimchunk.auto: 62 | description: 'Allow player to automatically claim chunks they walk into.' 63 | default: true 64 | 65 | # This acts as a "package" permission to give all the basic required permissions to players 66 | claimchunk.player: 67 | description: 'Gives players the claim, unclaim, chunk give, access, alert, auto-claim, color, scan, and show claim permissions.' 68 | default: true 69 | children: 70 | claimchunk.help: true 71 | claimchunk.claim: true 72 | claimchunk.unclaim: true 73 | claimchunk.give: true 74 | claimchunk.access: true 75 | claimchunk.alert: true 76 | claimchunk.auto: true 77 | claimchunk.show: true 78 | claimchunk.color: true 79 | claimchunk.show-claimed: true 80 | claimchunk.scan: true 81 | claimchunk.list: true 82 | claimchunk.name: true 83 | claimchunk.info: true 84 | 85 | # The following permissions are not given by default to players 86 | claimchunk.update: 87 | description: 'Alert the player that an update for ClaimChunk is available' 88 | default: op 89 | claimchunk.bypassnearbychunk: 90 | description: 'Bypass being too close to other chunks when you claim a chunk' 91 | default: op 92 | claimchunk.invis: 93 | description: 'Allow players to enter claimed chunks without alerting their owner(s)' 94 | default: false 95 | 96 | # This acts as a "package" permission to give all the admin permissions 97 | claimchunk.admin: 98 | description: 'Allows the player to modify others'' chunks.' 99 | default: op 100 | children: 101 | # Give basic player permission by default 102 | claimchunk.player: true 103 | # Admin permissions 104 | claimchunk.update: true 105 | claimchunk.invis: true 106 | claimchunk.bypassnearbychunk: true 107 | 108 | # Registered at runtime by `CCBukkitCommand` (Thank you Goldmensch!) 109 | # chunk: 110 | # description: 'The ClaimChunk main command. Use ''/claimchunk help'' or ''/chunk help'' for more information' 111 | # usage: '/ help' 112 | # aliases: 113 | # - 'claimchunk' 114 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/smartcommand/sub/ply/ListCmd.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.smartcommand.sub.ply; 2 | 3 | import claimchunk.dependency.de.goldmensch.commanddispatcher.Executor; 4 | 5 | import com.cjburkey.claimchunk.ClaimChunk; 6 | import com.cjburkey.claimchunk.Utils; 7 | import com.cjburkey.claimchunk.smartcommand.CCSubCommand; 8 | 9 | import org.bukkit.command.CommandSender; 10 | import org.bukkit.entity.Player; 11 | import org.jetbrains.annotations.NotNull; 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | /** 15 | * @since 0.0.23 16 | */ 17 | public class ListCmd extends CCSubCommand { 18 | 19 | public ListCmd(ClaimChunk claimChunk) { 20 | super(claimChunk, Executor.PLAYER, true, "player", "list"); 21 | } 22 | 23 | @Override 24 | public @Nullable String getDescription() { 25 | return claimChunk.getMessages().cmdList; 26 | } 27 | 28 | @Override 29 | public CCArg[] getPermittedArguments() { 30 | return new CCArg[] {new CCArg(claimChunk.getMessages().argPage, CCAutoComplete.NONE)}; 31 | } 32 | 33 | @Override 34 | public int getRequiredArguments() { 35 | return 0; 36 | } 37 | 38 | @Override 39 | public boolean onCall(@NotNull String cmdUsed, @NotNull CommandSender executor, String[] args) { 40 | var player = (Player) executor; 41 | var playerHandler = claimChunk.getPlayerHandler(); 42 | var chunkHandler = claimChunk.getChunkHandler(); 43 | 44 | var ply = player.getUniqueId(); 45 | var ownerName = playerHandler.getUsername(player.getUniqueId()); 46 | if (ownerName == null) ownerName = claimChunk.getMessages().infoOwnerUnknown; 47 | 48 | var chunks = chunkHandler.getClaimedChunks(ply); 49 | int page = 0; 50 | final var maxPerPage = 51 | Utils.clamp(claimChunk.getConfigHandler().getMaxPerListPage(), 2, 10); 52 | final var maxPage = Integer.max(0, (chunks.length - 1) / maxPerPage); 53 | 54 | if (args.length == 1) { 55 | try { 56 | page = Utils.clamp(Integer.parseInt(args[0]) - 1, 0, maxPage); 57 | } catch (Exception ignored) { 58 | messageChat( 59 | player, 60 | claimChunk.getConfigHandler().getInfoColor() 61 | + claimChunk.getMessages().errEnterValidNum); 62 | return true; 63 | } 64 | } 65 | 66 | messageChat( 67 | player, 68 | String.format( 69 | claimChunk.getMessages().claimsHeader, 70 | claimChunk.getConfigHandler().getInfoColor(), 71 | claimChunk 72 | .getMessages() 73 | .claimsTitle 74 | .replace("%%NAME%%", ownerName) 75 | .replace("%%WORLD%%", player.getWorld().getName()))); 76 | messageChat( 77 | player, 78 | claimChunk.getConfigHandler().getInfoColor() 79 | + claimChunk 80 | .getMessages() 81 | .claimsPagination 82 | .replace("%%PAGE%%", (page + 1) + "") 83 | .replace("%%MAXPAGE%%", (maxPage + 1) + "")); 84 | messageChat(player, ""); 85 | for (var i = page * maxPerPage; (i < (page + 1) * maxPerPage) && (i < chunks.length); i++) { 86 | // Using `x << 4` is the same as `x * 16` I think bitwise is 87 | // more efficient than multiplication? 88 | messageChat( 89 | player, 90 | claimChunk.getConfigHandler().getInfoColor() 91 | + claimChunk 92 | .getMessages() 93 | .claimsChunk 94 | .replace("%%X%%", "" + (chunks[i].x() << 4)) 95 | .replace("%%Z%%", "" + (chunks[i].z() << 4))); 96 | } 97 | return true; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/data/JsonConfig.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.data; 2 | 3 | import com.cjburkey.claimchunk.Utils; 4 | import com.google.gson.Gson; 5 | import com.google.gson.GsonBuilder; 6 | 7 | import org.jetbrains.annotations.NotNull; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | import java.io.*; 11 | import java.util.Collection; 12 | import java.util.Collections; 13 | import java.util.HashSet; 14 | import java.util.Iterator; 15 | 16 | public class JsonConfig implements Iterable { 17 | 18 | public final File file; 19 | private final HashSet data = new HashSet<>(); 20 | private final Class referenceClass; 21 | private final boolean pretty; 22 | 23 | public JsonConfig(Class referenceClass, File file, boolean pretty) { 24 | this.file = file; 25 | this.referenceClass = referenceClass; 26 | this.pretty = pretty; 27 | } 28 | 29 | public Collection getData() { 30 | return Collections.unmodifiableCollection(data); 31 | } 32 | 33 | public void addData(T toAdd) { 34 | data.add(toAdd); 35 | } 36 | 37 | public void saveData(@Nullable String headerComment) throws IOException { 38 | if (file == null) { 39 | return; 40 | } 41 | if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) { 42 | Utils.err("Failed to create parent directory for file: %s", file.getAbsolutePath()); 43 | return; 44 | } 45 | if (file.exists() && !file.delete()) { 46 | Utils.err("Failed to clear old offline JSON data in file: %s", file.getAbsolutePath()); 47 | return; 48 | } 49 | try (FileWriter writer = new FileWriter(file)) { 50 | StringBuilder output = new StringBuilder(); 51 | if (headerComment != null) { 52 | // Split the comment into its line components to make sure they 53 | // are all commented out 54 | String[] commentLines = headerComment.split("\n"); 55 | for (String commentLine : commentLines) { 56 | commentLine = commentLine.trim(); 57 | // No empty line comments (may be changed later) 58 | if (!commentLine.isEmpty()) { 59 | output.append('#'); 60 | output.append(' '); 61 | output.append(commentLine.trim()); 62 | output.append('\n'); 63 | } 64 | } 65 | } 66 | output.append(getGson().toJson(data)); 67 | writer.write(output.toString()); 68 | } 69 | } 70 | 71 | public void reloadData() throws IOException { 72 | if (file == null || !file.exists()) { 73 | return; 74 | } 75 | StringBuilder json = new StringBuilder(); 76 | try (BufferedReader reader = new BufferedReader(new FileReader(file))) { 77 | String line; 78 | while ((line = reader.readLine()) != null) { 79 | line = line.trim(); 80 | // Add support for comments! Lines that start with # are 81 | // ignored. 82 | if (!line.startsWith("#")) { 83 | json.append(line); 84 | json.append('\n'); 85 | } 86 | } 87 | } 88 | T[] out = getGson().fromJson(json.toString(), referenceClass); 89 | if (out != null) { 90 | data.clear(); 91 | Collections.addAll(data, out); 92 | } 93 | } 94 | 95 | @SuppressWarnings("unused") 96 | public void clearData() { 97 | data.clear(); 98 | } 99 | 100 | private Gson getGson() { 101 | GsonBuilder builder = new GsonBuilder().setLenient(); 102 | if (pretty) builder.setPrettyPrinting(); 103 | return builder.serializeNulls().create(); 104 | } 105 | 106 | @Override 107 | public String toString() { 108 | return data.toString(); 109 | } 110 | 111 | @Override 112 | public @NotNull Iterator iterator() { 113 | return getData().iterator(); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/smartcommand/sub/ply/HelpCmd.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.smartcommand.sub.ply; 2 | 3 | import claimchunk.dependency.de.goldmensch.commanddispatcher.Executor; 4 | 5 | import com.cjburkey.claimchunk.ClaimChunk; 6 | import com.cjburkey.claimchunk.Utils; 7 | import com.cjburkey.claimchunk.smartcommand.CCSubCommand; 8 | import com.cjburkey.claimchunk.smartcommand.ClaimChunkBaseCommand; 9 | 10 | import org.bukkit.command.CommandSender; 11 | import org.jetbrains.annotations.NotNull; 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | /** 15 | * @since 0.0.23 16 | */ 17 | public class HelpCmd extends CCSubCommand { 18 | 19 | private final ClaimChunkBaseCommand baseCommand; 20 | 21 | public HelpCmd(ClaimChunk claimChunk, ClaimChunkBaseCommand baseCommand) { 22 | // TODO: MAKE ACCESSIBLE FROM CONSOLE 23 | super(claimChunk, Executor.CONSOLE_PLAYER, true, "player", "help"); 24 | 25 | this.baseCommand = baseCommand; 26 | } 27 | 28 | @Override 29 | public @Nullable String getDescription() { 30 | return claimChunk.getMessages().cmdHelp; 31 | } 32 | 33 | @Override 34 | public CCArg[] getPermittedArguments() { 35 | return new CCArg[] {new CCArg(claimChunk.getMessages().argCmd, CCAutoComplete.NONE)}; 36 | } 37 | 38 | @Override 39 | public int getMaxArguments() { 40 | // Random high number 41 | return 100; 42 | } 43 | 44 | @Override 45 | public int getRequiredArguments() { 46 | return 0; 47 | } 48 | 49 | @Override 50 | public boolean onCall(@NotNull String cmdUsed, @NotNull CommandSender player, String[] args) { 51 | if (args.length == 0) { 52 | // Display the help command header 53 | messageChat(player, claimChunk.getMessages().helpHeader); 54 | 55 | // List all the commands 56 | for (var cmd : baseCommand.getCmds()) { 57 | // Only show commands that the user has permission to use 58 | if (cmd.getShouldDisplayInHelp(player)) { 59 | // Display this command's help 60 | messageChat(player, getCommandDisplayStr(cmdUsed, cmd)); 61 | } 62 | } 63 | } else { 64 | // Get the command 65 | var cmd = baseCommand.getSubCmd(args); 66 | Utils.log("Args: %s", String.join(", ", args)); 67 | if (cmd.isEmpty()) { 68 | // Display the command wasn't found 69 | messageChat( 70 | player, 71 | claimChunk 72 | .getMessages() 73 | .helpCmdNotFound 74 | .replace("%%USED%%", cmdUsed) 75 | .replace("%%CMD%%", String.join(" ", args))); 76 | } else { 77 | var ccmd = cmd.get(); 78 | 79 | // Display the command's help header 80 | messageChat( 81 | player, 82 | claimChunk 83 | .getMessages() 84 | .helpCmdHeader 85 | .replace("%%USED%%", cmdUsed) 86 | .replace("%%CMD%%", ccmd.getName())); 87 | 88 | // Display the command's help 89 | messageChat(player, getCommandDisplayStr(cmdUsed, ccmd)); 90 | } 91 | } 92 | return true; 93 | } 94 | 95 | private @NotNull String getCommandDisplayStr(String cmdUsed, CCSubCommand cmd) { 96 | @Nullable String desc = cmd.getDescription(); 97 | 98 | // Create the display string 99 | return claimChunk 100 | .getMessages() 101 | .helpCmd 102 | .replace("%%USED%%", cmdUsed) 103 | .replace("%%CMD%%", cmd.getName()) 104 | .replace("%%ARGS%%", cmd.getUsageArgs()) 105 | .replace( 106 | "%%DESC%%", 107 | desc == null 108 | ? "No description! Oops! Let me know about this please :)" 109 | : desc); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/api/layer/ClaimChunkLayerHandler.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.api.layer; 2 | 3 | import com.cjburkey.claimchunk.Utils; 4 | import com.cjburkey.claimchunk.api.IClaimChunkPlugin; 5 | 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | import java.util.Comparator; 9 | import java.util.Optional; 10 | import java.util.PriorityQueue; 11 | 12 | public class ClaimChunkLayerHandler { 13 | 14 | private final PriorityQueue layerQueue = new PriorityQueue<>(new LayerComparator()); 15 | private final IClaimChunkPlugin claimChunk; 16 | 17 | public ClaimChunkLayerHandler(@NotNull IClaimChunkPlugin claimChunk) { 18 | this.claimChunk = claimChunk; 19 | } 20 | 21 | /** 22 | * Inserts a layer into the queue based on its order ID. 23 | * 24 | * @param layer The layer to insert. 25 | * @param The type representing the layer. 26 | * @return {@code true} if the layer was inserted, or {@code false} if there is already a layer 27 | * with that given class type. 28 | */ 29 | @SuppressWarnings("BooleanMethodIsAlwaysInverted") 30 | public boolean insertLayer(@NotNull T layer) { 31 | // Only register this layer if there isn't already a layer of this type present in the 32 | // queue. 33 | if (getLayer(layer.getClass()).isEmpty()) { 34 | layerQueue.add(new LayerEntry(layer)); 35 | return true; 36 | } 37 | 38 | // Return false if a layer of this type is already in the queue. 39 | return false; 40 | } 41 | 42 | /** 43 | * Retrieves the layer for a given type. There should only ever be one layer for a given class. 44 | * 45 | * @param classType The type of class describing this layer. 46 | * @param The type of layer 47 | * @return An optional containing the layer or empty if no layer of that type is registered. 48 | */ 49 | public @NotNull Optional getLayer(@NotNull Class classType) { 50 | return layerQueue.stream() 51 | .filter(entry -> entry.layer.getClass().equals(classType)) 52 | .findFirst() 53 | .map(entry -> classType.cast(entry.layer)); 54 | } 55 | 56 | /** 57 | * Removes a layer from the queue. 58 | * 59 | * @param classType The class for the type of layer to remove. 60 | * @param The type representing the layer to remove. 61 | * @return Whether the layer was successfully removed. 62 | */ 63 | @SuppressWarnings("unused") 64 | public boolean removeLayer(@NotNull Class classType) { 65 | return layerQueue.removeIf(entry -> entry.layer.getClass().equals(classType)); 66 | } 67 | 68 | /** Enables each layer. */ 69 | public void onEnable() { 70 | Utils.debug("Enabling ClaimChunk modular layer handler"); 71 | layerQueue.forEach(entry -> entry.onEnable(claimChunk)); 72 | } 73 | 74 | /** Disables each layer. */ 75 | public void onDisable() { 76 | Utils.debug("Disabling ClaimChunk modular layer handler"); 77 | layerQueue.forEach(entry -> entry.layer.onDisable(claimChunk)); 78 | } 79 | 80 | // Keeps track of whether the given layer is enabled and its sorting ID 81 | private static class LayerEntry { 82 | boolean enabled; 83 | final int orderId; 84 | final IClaimChunkLayer layer; 85 | 86 | LayerEntry(@NotNull IClaimChunkLayer layer) { 87 | orderId = layer.getOrderId(); 88 | this.layer = layer; 89 | } 90 | 91 | void onEnable(@NotNull IClaimChunkPlugin claimChunk) { 92 | enabled = layer.onEnable(claimChunk); 93 | String name = layer.getClass().getSimpleName(); 94 | Utils.debug(enabled ? "Enabled layer %s" : "Layer %s not enabled", name); 95 | } 96 | } 97 | 98 | // Determines which layers should come first; this could probably be a lot simpler :/ 99 | private record LayerComparator() implements Comparator { 100 | @Override 101 | public int compare(LayerEntry o1, LayerEntry o2) { 102 | if (o1 == null) return 1; 103 | if (o2 == null) return -1; 104 | return Integer.compare(o1.orderId, o2.orderId); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /changelogs/0.0.24-RC1.md: -------------------------------------------------------------------------------- 1 | # ClaimChunk 0.0.24-RC1 2 | 3 | This update allows individual permissions to be granted on a per-chunk basis. These changes required a major overhaul 4 | of the internal workings of the plugin. The commands have changed, and there have been some changes to the format used 5 | to store the data. 6 | 7 | Players will not need to make any changes to their claims as a result of this update; the data conversion process will 8 | convert any existing accesses in such a way that they will continue to function as they did in 0.0.23. 9 | 10 | **Note:** The messages.json and world.txt files will need to be recreated due to new additions to them in this update. 11 | For English language, this can be done by deleting them and running the server. Remember to transfer any custom config 12 | to the new world.txt files after they are recreated. 13 | 14 | Changes: 15 | * Access is now granted on a per-chunk basis, a chunk owner can grant another player access to only specific chunks 16 | rather than all of their chunks. 17 | * When the data is converted from the 0.0.23 format, a player that has previously been given access by a chunk owner 18 | will continue to have access to all the owner's chunks. 19 | * Players are now granted specific permissions on the chunks that they have access to (so for instance, a player could 20 | be granted permission to place blocks in a chunk, but not break them). 21 | * These permissions are: 22 | * break: Allow the player to break blocks 23 | * place: Allow the player to place blocks 24 | * doors: Allow the player to open / close doors and trapdoors 25 | * redstone: Allow the player to press buttons, levers, pressure plates etc... 26 | * interactVehicles: Allow the player to interact with mine carts and boats 27 | * interactEntities: Allow the player to interact with other entities (also allow the player to throw ender pearls 28 | even if preventPearlFromClaims is true in the world profile) 29 | * interactBlocks: Allow the player to interact with blocks 30 | * useContainers: Allow the player to open: 31 | * Chests 32 | * Trapped chests 33 | * Barrels 34 | * Furnaces 35 | * Dispensers and droppers 36 | * Hoppers 37 | * Brewing stands 38 | * Commands: 39 | * The changes have involved significant changes to the `/chunk access` command; it has now been split into three 40 | commands: 41 | * `/chunk access [break:true/false] [place:true/false] [doors:true/false] [redstone:true/false] 42 | [interactVehicles:true/false] [interactEntities:true/false] [interactBlocks:true/false] [useContainers:true/false] 43 | [allChunks:true/false]` 44 | * Give a player permissions on a chunk (or modify their existing permissions) 45 | * All arguments except `player` are optional, and can be provided in any order 46 | * If `allChunks:true` is provided, the changes will be made to all the executing player's chunks. If false 47 | (the default) the changes will apply only to the chunk the player is currently standing in (if it belongs 48 | to them) 49 | * Any permissions not specified will retain their existing values (e.g. if the command is run without the `doors` 50 | option, the player's existing doors permission will remain unchanged) or will default to true if the player does 51 | not have access to the chunk already. 52 | * usage example: `/chunk access examplePlayer break:true place:false allChunks:true` 53 | * `/chunk checkaccess []` 54 | * List the permissions the given player has on the chunk where the executor is standing 55 | * If no player is provided, lists the permissions of all players with access to the chunk 56 | * `/chunk revokeaccess [ALL CHUNKS? true/false]` 57 | * Revoke the given player's access to the chunk 58 | * If all chunks is true, the player's access will be revoked from all the executor's chunks. Otherwise, only the 59 | one they are standing in (if they are the owner) 60 | * Usage example: `/chunk revokeaccess examplePlayer false` 61 | * Removed "am_trusted" placeholder 62 | * Added new "CONTAINER" block class 63 | * Added trapdoors to "DOOR" block class 64 | * Added boats to "VEHICLES" entity class 65 | * Changed default world profile permissions for players in claimed chunks who have not been granted access: 66 | * Interacting with vehicles and monsters is now disallowed 67 | * Damaging monsters is now disallowed 68 | * Interacting with redstone is now disallowed -------------------------------------------------------------------------------- /src/main/java/com/cjburkey/claimchunk/worldguard/WorldGuardApi.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk.worldguard; 2 | 3 | import com.cjburkey.claimchunk.ClaimChunk; 4 | import com.cjburkey.claimchunk.Utils; 5 | import com.cjburkey.claimchunk.chunk.ChunkPos; 6 | import com.sk89q.worldedit.bukkit.BukkitAdapter; 7 | import com.sk89q.worldedit.math.BlockVector3; 8 | import com.sk89q.worldguard.WorldGuard; 9 | import com.sk89q.worldguard.protection.flags.StateFlag; 10 | import com.sk89q.worldguard.protection.flags.registry.FlagConflictException; 11 | import com.sk89q.worldguard.protection.flags.registry.FlagRegistry; 12 | import com.sk89q.worldguard.protection.managers.RegionManager; 13 | import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion; 14 | import com.sk89q.worldguard.protection.regions.ProtectedRegion; 15 | 16 | import java.util.Objects; 17 | 18 | /** 19 | * THIS CLASS IS A RAW IMPLEMENTATION IT WILL CRASH IF WORLD GUARD IS NOT PRESENT USE {@link 20 | * com.cjburkey.claimchunk.worldguard.WorldGuardHandler} instead 21 | */ 22 | class WorldGuardApi { 23 | 24 | private static final String CHUNK_CLAIM_FLAG_NAME = "chunk-claim"; 25 | private static StateFlag FLAG_CHUNK_CLAIM; 26 | 27 | static boolean _init(ClaimChunk claimChunk) { 28 | FLAG_CHUNK_CLAIM = 29 | new StateFlag( 30 | CHUNK_CLAIM_FLAG_NAME, 31 | claimChunk.getConfigHandler().getAllowClaimsInWGRegionsByDefault()); 32 | 33 | FlagRegistry registry = WorldGuard.getInstance().getFlagRegistry(); 34 | try { 35 | registry.register(FLAG_CHUNK_CLAIM); 36 | return true; 37 | } catch (FlagConflictException ignored) { 38 | Utils.log("Flag \"%s\" is already registered with WorldGuard", CHUNK_CLAIM_FLAG_NAME); 39 | // If the flag is already registered, that's ok, we can carry on 40 | if (registry.get(CHUNK_CLAIM_FLAG_NAME) instanceof StateFlag newFlag) { 41 | FLAG_CHUNK_CLAIM = newFlag; 42 | return true; 43 | } 44 | 45 | // Otherwise, something has gone awry. Oops. 46 | Utils.err( 47 | "Failed to retrieve existing `chunk-claim` StateFlag from WorldGuard flag" 48 | + " registry"); 49 | return false; 50 | } catch (Exception e) { 51 | Utils.err("Failed to initialize WorldGuard support"); 52 | //noinspection CallToPrintStackTrace 53 | e.printStackTrace(); 54 | } 55 | return false; 56 | } 57 | 58 | static boolean _isAllowedClaim(ClaimChunk claimChunk, ChunkPos chunk) { 59 | try { 60 | // Generate a region in the given chunk to get all intersecting regions 61 | int bx = chunk.x() << 4; 62 | int bz = chunk.z() << 4; 63 | BlockVector3 pt1 = BlockVector3.at(bx, 0, bz); 64 | BlockVector3 pt2 = BlockVector3.at(bx + 15, 256, bz + 15); 65 | ProtectedCuboidRegion region = new ProtectedCuboidRegion("_", pt1, pt2); 66 | RegionManager regionManager = 67 | WorldGuard.getInstance() 68 | .getPlatform() 69 | .getRegionContainer() 70 | .get( 71 | BukkitAdapter.adapt( 72 | Objects.requireNonNull( 73 | claimChunk.getServer().getWorld(chunk.world()), 74 | "World not found!"))); 75 | 76 | // No regions in this world, claiming should be determined by the config 77 | if (regionManager == null) { 78 | return claimChunk.getConfigHandler().getAllowClaimingInNonWGWorlds(); 79 | } 80 | 81 | // If any regions in the given chunk deny chunk claiming, false is returned 82 | for (ProtectedRegion regionIn : regionManager.getApplicableRegions(region)) { 83 | StateFlag.State flag = regionIn.getFlag(FLAG_CHUNK_CLAIM); 84 | if (flag == StateFlag.State.DENY) return false; 85 | } 86 | 87 | // No objections 88 | return true; 89 | } catch (Exception e) { 90 | //noinspection CallToPrintStackTrace 91 | e.printStackTrace(); 92 | } 93 | 94 | // An error occurred, better to be on the safe side so false is returned 95 | return false; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/test/java/com/cjburkey/claimchunk/CCConfigTests.java: -------------------------------------------------------------------------------- 1 | package com.cjburkey.claimchunk; 2 | 3 | import static org.junit.jupiter.api.Assertions.*; 4 | 5 | import com.cjburkey.claimchunk.config.ccconfig.CCConfig; 6 | import com.cjburkey.claimchunk.config.ccconfig.CCConfigParseError; 7 | import com.cjburkey.claimchunk.config.ccconfig.CCConfigParser; 8 | import com.cjburkey.claimchunk.config.ccconfig.CCConfigWriter; 9 | 10 | import org.junit.jupiter.api.Test; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | class CCConfigTests { 16 | 17 | @Test 18 | void testConfigValueStorage() { 19 | // Initialize a config 20 | final CCConfig config = new CCConfig("", ""); 21 | 22 | // Set some test values 23 | config.set("an_int", "10"); 24 | config.set("a_float", "20.0"); 25 | config.set("a_bool", "true"); 26 | config.set("a_string", "this is my value :)"); 27 | 28 | // Assert the test values 29 | assertEquals(10, config.getInt("an_int", 0)); 30 | assertEquals(20.0f, config.getFloat("a_float", 0.0f), Float.MIN_VALUE); 31 | assert config.getBool("a_bool", false); 32 | assertEquals("this is my value :)", config.getStr("a_string")); 33 | 34 | // Assert defaults when the getting nonexistent values 35 | assertEquals(1, config.getInt("a_fake_int", 1)); 36 | assertEquals(2.0f, config.getFloat("a_fake_float", 2.0f), Float.MIN_VALUE); 37 | assert !config.getBool("a_fake_bool", false); 38 | assertEquals("", config.getStr("a_fake_string")); 39 | } 40 | 41 | @Test 42 | void testConfigToString() { 43 | // Initialize a config 44 | final CCConfig config = new CCConfig("Example comment :)\nA different comment", ""); 45 | 46 | // Set some test values 47 | config.set("bob.an_int", "10"); 48 | config.set("bob.a_float", "20.0"); 49 | config.set("bob.says.a_bool", "true"); 50 | config.set("jim.yells.says.a_bool", "true"); 51 | config.set("jim.says.yells.a_bool", "false"); 52 | config.set("bob.a_string", "this is my value :)"); 53 | config.set("bob.a_different_float", "20.0"); 54 | 55 | // Create the config writer 56 | final CCConfigWriter configWriter = new CCConfigWriter(" ", 4, 1, 0, 1, 0); 57 | final String serializedConfig = configWriter.serialize(config); 58 | 59 | // The expected output 60 | final String expected = 61 | """ 62 | # Example comment :) 63 | # A different comment 64 | 65 | bob: 66 | a_different_float 20.0 ; 67 | a_float 20.0 ; 68 | a_string this is my value :) ; 69 | an_int 10 ; 70 | 71 | bob.says: 72 | a_bool true ; 73 | 74 | jim.says.yells: 75 | a_bool false ; 76 | 77 | jim.yells.says: 78 | a_bool true ; 79 | """; 80 | 81 | assertEquals(serializedConfig, expected); 82 | } 83 | 84 | @Test 85 | void testConfigFromString() { 86 | final String input = 87 | """ 88 | 89 | bob: 90 | a_different_float 30.0 ; 91 | a_float 20.0 ; 92 | a_string this is my value :) ; 93 | an_i9nt 10 ; 94 | 95 | bob.says: 96 | a_bool true ; 97 | 98 | jim.says.yells: 99 | a_bool false ; 100 | 101 | jim.yells.says: 102 | a_bool true ; 103 | """; 104 | 105 | // Initialize a config 106 | final CCConfig config = new CCConfig("", ""); 107 | final CCConfigParser configParser = new CCConfigParser(); 108 | 109 | // Parse the config and make sure there aren't any errors 110 | final List parseErrors = configParser.parse(config, input); 111 | assertEquals(new ArrayList<>(), parseErrors); 112 | 113 | // Ensure all the values were set correctly 114 | assertEquals(10, config.getInt("bob.an_i9nt", 0)); 115 | assertEquals(20.0f, config.getFloat("bob.a_float", 0.0f)); 116 | assertTrue(config.getBool("bob.says.a_bool", false)); 117 | assertTrue(config.getBool("jim.yells.says.a_bool", false)); 118 | assertFalse(config.getBool("jim.says.yells.a_bool", true)); 119 | assertEquals("this is my value :)", config.getStr("bob.a_string")); 120 | assertEquals(30.0f, config.getFloat("bob.a_different_float", 0.0f)); 121 | } 122 | } 123 | --------------------------------------------------------------------------------