From 557f09a4dece921e7bf54f83c06e12fb785bc393 Mon Sep 17 00:00:00 2001
From: bloodshot <jdroque@gmail.com>
Date: Sun, 24 Nov 2019 17:01:05 -0500
Subject: [PATCH] Add sponge support.

---
 settings.gradle                               |    1 +
 sponge/build.gradle                           |  203 ++
 sponge/gradle.properties                      |   11 +
 .../java/com/griefdefender/GDBootstrap.java   |  228 ++
 .../java/com/griefdefender/GDCatalogType.java |   76 +
 .../java/com/griefdefender/GDChatType.java    |   49 +
 .../main/java/com/griefdefender/GDCore.java   |  109 +
 .../java/com/griefdefender/GDDebugData.java   |  224 ++
 .../com/griefdefender/GDEventManager.java     |   67 +
 .../java/com/griefdefender/GDPlayerData.java  |  701 ++++
 .../java/com/griefdefender/GDRelocator.java   |   78 +
 .../java/com/griefdefender/GDTimings.java     |   81 +
 .../java/com/griefdefender/GDVersion.java     |   44 +
 .../griefdefender/GriefDefenderPlugin.java    | 1166 ++++++
 .../griefdefender/cache/EventResultCache.java |   78 +
 .../com/griefdefender/cache/MessageCache.java |  772 ++++
 .../cache/PermissionHolderCache.java          |  154 +
 .../claim/ClaimContextCalculator.java         |   87 +
 .../java/com/griefdefender/claim/GDClaim.java | 3111 +++++++++++++++++
 .../griefdefender/claim/GDClaimManager.java   |  668 ++++
 .../griefdefender/claim/GDClaimResult.java    |   89 +
 .../griefdefender/claim/GDClaimSchematic.java |  187 +
 .../com/griefdefender/claim/GDClaimType.java  |  119 +
 .../com/griefdefender/claim/GDShovelType.java |   49 +
 .../java/com/griefdefender/claim/GDTown.java  |   21 +
 .../com/griefdefender/claim/GDTrustType.java  |   53 +
 .../griefdefender/command/ClaimFlagBase.java  |  973 ++++++
 .../command/ClaimOptionBase.java              |  913 +++++
 .../command/ClaimSubjectType.java             |   42 +
 .../CommandAdjustBonusClaimBlocks.java        |   90 +
 .../command/CommandCallback.java              |   47 +
 .../command/CommandClaimAbandon.java          |  218 ++
 .../command/CommandClaimAbandonAll.java       |  188 +
 .../command/CommandClaimAbandonTop.java       |   48 +
 .../command/CommandClaimAdmin.java            |   52 +
 .../command/CommandClaimBan.java              |  129 +
 .../command/CommandClaimBank.java             |   76 +
 .../command/CommandClaimBasic.java            |   52 +
 .../command/CommandClaimBuy.java              |   97 +
 .../command/CommandClaimBuyBlocks.java        |  143 +
 .../command/CommandClaimClear.java            |  173 +
 .../command/CommandClaimContract.java         |  201 ++
 .../command/CommandClaimCreate.java           |  146 +
 .../command/CommandClaimCuboid.java           |   56 +
 .../command/CommandClaimDelete.java           |  121 +
 .../command/CommandClaimDeleteAll.java        |  110 +
 .../command/CommandClaimDeleteAllAdmin.java   |   90 +
 .../command/CommandClaimDeleteTop.java        |   49 +
 .../command/CommandClaimExpand.java           |  201 ++
 .../command/CommandClaimFarewell.java         |   80 +
 .../command/CommandClaimFlag.java             |   57 +
 .../command/CommandClaimFlagDebug.java        |   67 +
 .../command/CommandClaimFlagGroup.java        |   80 +
 .../command/CommandClaimFlagPlayer.java       |   66 +
 .../command/CommandClaimFlagReset.java        |  102 +
 .../command/CommandClaimGreeting.java         |   81 +
 .../command/CommandClaimIgnore.java           |   67 +
 .../command/CommandClaimInfo.java             |  888 +++++
 .../command/CommandClaimInherit.java          |   69 +
 .../command/CommandClaimList.java             |  246 ++
 .../command/CommandClaimMode.java             |   78 +
 .../command/CommandClaimName.java             |   74 +
 .../command/CommandClaimOption.java           |   85 +
 .../command/CommandClaimOptionGroup.java      |   74 +
 .../command/CommandClaimOptionPlayer.java     |   58 +
 .../command/CommandClaimPermissionGroup.java  |  147 +
 .../command/CommandClaimPermissionPlayer.java |  144 +
 .../command/CommandClaimSchematic.java        |  154 +
 .../command/CommandClaimSell.java             |  129 +
 .../command/CommandClaimSellBlocks.java       |  154 +
 .../command/CommandClaimSetSpawn.java         |   66 +
 .../command/CommandClaimSpawn.java            |  120 +
 .../command/CommandClaimSubdivision.java      |   53 +
 .../command/CommandClaimTown.java             |   53 +
 .../command/CommandClaimTransfer.java         |   86 +
 .../command/CommandClaimUnban.java            |  107 +
 .../command/CommandClaimWorldEdit.java        |   52 +
 .../griefdefender/command/CommandDebug.java   |  116 +
 .../command/CommandException.java             |   92 +
 .../command/CommandGDReload.java              |   48 +
 .../command/CommandGDVersion.java             |   77 +
 .../command/CommandGiveBlocks.java            |   92 +
 .../griefdefender/command/CommandGivePet.java |   31 +
 .../griefdefender/command/CommandHelp.java    |   51 +
 .../griefdefender/command/CommandHelper.java  | 1352 +++++++
 .../command/CommandPagination.java            |   36 +
 .../command/CommandPlayerInfo.java            |  273 ++
 .../command/CommandRestoreClaim.java          |   96 +
 .../command/CommandRestoreNature.java         |   56 +
 .../command/CommandSetAccruedClaimBlocks.java |  115 +
 .../command/CommandTownChat.java              |   63 +
 .../griefdefender/command/CommandTownTag.java |   88 +
 .../command/CommandTrustGroup.java            |  145 +
 .../command/CommandTrustGroupAll.java         |  132 +
 .../command/CommandTrustList.java             |  253 ++
 .../command/CommandTrustPlayer.java           |  167 +
 .../command/CommandTrustPlayerAll.java        |  151 +
 .../command/CommandUntrustGroup.java          |  115 +
 .../command/CommandUntrustGroupAll.java       |  118 +
 .../command/CommandUntrustPlayer.java         |  121 +
 .../command/CommandUntrustPlayerAll.java      |  126 +
 .../command/ComponentMessageException.java    |  104 +
 .../command/gphelper/CommandAccessTrust.java  |  125 +
 .../gphelper/CommandContainerTrust.java       |  125 +
 .../configuration/ClaimDataConfig.java        |  552 +++
 .../configuration/ClaimStorageData.java       |  209 ++
 .../configuration/ClaimTemplateConfig.java    |  130 +
 .../configuration/ClaimTemplateStorage.java   |  133 +
 .../configuration/EconomyDataConfig.java      |  120 +
 .../configuration/GriefDefenderConfig.java    |  230 ++
 .../configuration/IClaimData.java             |   80 +
 .../configuration/MessageDataConfig.java      |   68 +
 .../configuration/MessageStorage.java         |  380 ++
 .../configuration/PlayerDataConfig.java       |   78 +
 .../configuration/PlayerStorageData.java      |  109 +
 .../configuration/TownDataConfig.java         |   81 +
 .../configuration/TownStorageData.java        |   45 +
 .../configuration/category/BanCategory.java   |  184 +
 .../category/BlacklistCategory.java           |   71 +
 .../configuration/category/ClaimCategory.java |   68 +
 .../category/ConfigCategory.java              |   32 +
 .../category/CustomFlagGroupCategory.java     |   72 +
 .../CustomFlagGroupDefinitionCategory.java    |   70 +
 .../category/DefaultPermissionCategory.java   |  205 ++
 .../category/EconomyCategory.java             |   41 +
 .../category/GeneralCategory.java             |   35 +
 .../category/MessageCategory.java             |   44 +
 .../category/MigratorCategory.java            |   53 +
 .../category/ModuleCategory.java              |   56 +
 .../category/OptionCategory.java              |   64 +
 .../category/PlayerDataCategory.java          |   58 +
 .../configuration/category/PvpCategory.java   |   35 +
 .../category/ThreadCategory.java              |   35 +
 .../configuration/category/TownCategory.java  |   45 +
 .../category/VisualCategory.java              |   94 +
 .../serializer/ClaimTypeSerializer.java       |   50 +
 .../serializer/ComponentConfigSerializer.java |   86 +
 .../serializer/CreateModeTypeSerializer.java  |   54 +
 .../serializer/CustomFlagSerializer.java      |  193 +
 .../serializer/GameModeTypeSerializer.java    |   58 +
 .../serializer/WeatherTypeSerializer.java     |   54 +
 .../configuration/type/ConfigBase.java        |   71 +
 .../configuration/type/GlobalConfig.java      |   85 +
 .../economy/GDBankTransaction.java            |  125 +
 .../event/GDAttackPlayerEvent.java            |   45 +
 .../event/GDBorderClaimEvent.java             |  125 +
 .../event/GDCauseStackManager.java            |   97 +
 .../event/GDChangeClaimEvent.java             |   79 +
 .../com/griefdefender/event/GDClaimEvent.java |   82 +
 .../event/GDCreateClaimEvent.java             |   49 +
 .../event/GDFlagPermissionEvent.java          |   82 +
 .../event/GDGroupTrustClaimEvent.java         |   71 +
 .../griefdefender/event/GDLoadClaimEvent.java |   49 +
 .../griefdefender/event/GDOptionEvent.java    |   73 +
 .../event/GDPermissionEvent.java              |   91 +
 .../event/GDRemoveClaimEvent.java             |   83 +
 .../griefdefender/event/GDSaveClaimEvent.java |   49 +
 .../griefdefender/event/GDTaxClaimEvent.java  |   67 +
 .../event/GDTransferClaimEvent.java           |   57 +
 .../event/GDTrustClaimEvent.java              |   51 +
 .../event/GDUserTrustClaimEvent.java          |   72 +
 .../com/griefdefender/event/package-info.java |   25 +
 .../inject/GriefDefenderImplModule.java       |   62 +
 .../internal/EntityRemovalListener.java       |   92 +
 .../griefdefender/internal/NbtDataHelper.java |   61 +
 .../internal/pagination/ActivePagination.java |  218 ++
 .../pagination/GDPaginationBuilder.java       |  137 +
 .../pagination/GDPaginationCalculator.java    |  328 ++
 .../pagination/GDPaginationHolder.java        |  138 +
 .../internal/pagination/GDPaginationList.java |  145 +
 .../pagination/IterablePagination.java        |  127 +
 .../internal/pagination/ListPagination.java   |  107 +
 .../internal/pagination/PaginationList.java   |  274 ++
 .../pagination/TextComponentIterable.java     |   49 +
 .../pagination/TextComponentIterator.java     |  101 +
 .../internal/pagination/font-sizes.json       |   50 +
 .../internal/provider/GDActor.java            |  231 ++
 .../internal/provider/WorldEditProvider.java  |  359 ++
 .../worldedit/cui/MultiSelectionColors.java   |   48 +
 .../worldedit/cui/MultiSelectionType.java     |   36 +
 .../cui/event/MultiSelectionClearEvent.java   |   54 +
 .../cui/event/MultiSelectionColorEvent.java   |   60 +
 .../cui/event/MultiSelectionCuboidEvent.java  |   54 +
 .../cui/event/MultiSelectionGridEvent.java    |   49 +
 .../cui/event/MultiSelectionPointEvent.java   |   65 +
 .../registry/BlockTypeRegistryModule.java     |   69 +
 .../registry/EntityTypeRegistryModule.java    |   90 +
 .../internal/registry/GDBlockType.java        |   50 +
 .../internal/registry/GDEntityType.java       |  104 +
 .../internal/registry/GDItemType.java         |   50 +
 .../registry/ItemTypeRegistryModule.java      |   68 +
 .../internal/util/BlockUtil.java              |  485 +++
 .../griefdefender/internal/util/NMSUtil.java  |  388 ++
 .../internal/util/VecHelper.java              |  202 ++
 .../internal/visual/ClaimVisual.java          |  559 +++
 .../internal/visual/GDClaimVisualType.java    |  155 +
 .../listener/BlockEventHandler.java           |  989 ++++++
 .../listener/CommonEntityEventHandler.java    |  385 ++
 .../listener/EntityEventHandler.java          |  899 +++++
 .../listener/LuckPermsEventHandler.java       |   54 +
 .../listener/MCClansEventHandler.java         |   79 +
 .../listener/NucleusEventHandler.java         |   59 +
 .../listener/PlayerEventHandler.java          | 1696 +++++++++
 .../listener/WorldEventHandler.java           |   97 +
 .../migrator/GPBukkitMigrator.java            |  713 ++++
 .../migrator/GPSpongeMigrator.java            |  308 ++
 .../migrator/RedProtectMigrator.java          |  193 +
 .../migrator/WorldGuardMigrator.java          |  793 +++++
 .../permission/ContextGroupKeys.java          |   36 +
 .../permission/ContextGroups.java             |   49 +
 .../permission/GDPermissionGroup.java         |   47 +
 .../permission/GDPermissionHolder.java        |   72 +
 .../permission/GDPermissionManager.java       | 1429 ++++++++
 .../permission/GDPermissionResult.java        |   57 +
 .../permission/GDPermissionUser.java          |  156 +
 .../permission/GDPermissions.java             |  250 ++
 .../permission/GDResultType.java              |   31 +
 .../permission/flag/CustomFlagData.java       |   65 +
 .../permission/flag/FlagContexts.java         |   82 +
 .../permission/flag/GDActiveFlagData.java     |   94 +
 .../flag/GDCustomFlagDefinition.java          |  108 +
 .../flag/GDCustomFlagDefinitions.java         |  415 +++
 .../griefdefender/permission/flag/GDFlag.java |  119 +
 .../permission/flag/GDFlags.java              |  107 +
 .../permission/option/GDOption.java           |  243 ++
 .../permission/option/OptionBuilder.java      |   69 +
 .../option/type/GDCreateModeType.java         |   52 +
 .../option/type/GDGameModeType.java           |   52 +
 .../permission/option/type/GDWeatherType.java |   52 +
 .../permission/ui/ClaimClickData.java         |   37 +
 .../griefdefender/permission/ui/FlagData.java |  143 +
 .../griefdefender/permission/ui/MenuType.java |   35 +
 .../permission/ui/OptionData.java             |  122 +
 .../griefdefender/permission/ui/UIHelper.java |  187 +
 .../provider/LuckPermsProvider.java           |  885 +++++
 .../provider/MCClansProvider.java             |   41 +
 .../provider/NucleusProvider.java             |   84 +
 .../provider/PermissionProvider.java          |  337 ++
 .../registry/ChatTypeRegistryModule.java      |   91 +
 .../registry/ClaimTypeRegistryModule.java     |   91 +
 .../CreateModeTypeRegistryModule.java         |   91 +
 .../registry/FlagRegistryModule.java          |   92 +
 .../griefdefender/registry/GDRegistry.java    |  116 +
 .../registry/GameModeTypeRegistryModule.java  |   91 +
 .../registry/OptionRegistryModule.java        |  141 +
 .../registry/ResultTypeRegistryModule.java    |   91 +
 .../registry/ShovelTypeRegistryModule.java    |   91 +
 .../registry/TrustTypeRegistryModule.java     |   91 +
 .../registry/WeatherTypeRegistryModule.java   |   91 +
 .../griefdefender/storage/BaseStorage.java    |  443 +++
 .../griefdefender/storage/FileStorage.java    |  512 +++
 .../griefdefender/task/ClaimBlockTask.java    |  122 +
 .../griefdefender/task/ClaimCleanupTask.java  |  152 +
 .../task/ClaimVisualApplyTask.java            |   86 +
 .../task/ClaimVisualRevertTask.java           |   55 +
 .../griefdefender/task/PlayerTickTask.java    |  101 +
 .../com/griefdefender/task/TaxApplyTask.java  |  193 +
 .../text/action/GDCallbackHolder.java         |   83 +
 .../com/griefdefender/util/BlockPosCache.java |   61 +
 .../com/griefdefender/util/BootstrapUtil.java |   65 +
 .../util/CauseContextHelper.java              |  256 ++
 .../griefdefender/util/ClaimClickData.java    |   37 +
 .../com/griefdefender/util/EconomyUtil.java   |  213 ++
 .../com/griefdefender/util/EntityUtils.java   |   72 +
 .../com/griefdefender/util/HttpClient.java    |  107 +
 .../griefdefender/util/PaginationUtil.java    |   93 +
 .../griefdefender/util/PermissionUtil.java    |  242 ++
 .../com/griefdefender/util/PlayerUtil.java    |  207 ++
 .../griefdefender/util/SpongeContexts.java    |   30 +
 .../com/griefdefender/util/SpongeUtil.java    |  106 +
 .../java/com/griefdefender/util/TaskUtil.java |   58 +
 sponge/src/main/resources/1.12.2.json         |  250 ++
 sponge/src/main/resources/LICENSE             |   22 +
 .../resources/META-INF/griefdefender_at.cfg   |    5 +
 .../src/main/resources/assets/lang/en_US.conf |  637 ++++
 .../src/main/resources/assets/lang/fr_FR.conf |  637 ++++
 .../src/main/resources/assets/lang/ru_RU.conf |  634 ++++
 .../internal/pagination/font-sizes.json       |   50 +
 278 files changed, 49238 insertions(+)
 create mode 100644 sponge/build.gradle
 create mode 100644 sponge/gradle.properties
 create mode 100644 sponge/src/main/java/com/griefdefender/GDBootstrap.java
 create mode 100644 sponge/src/main/java/com/griefdefender/GDCatalogType.java
 create mode 100644 sponge/src/main/java/com/griefdefender/GDChatType.java
 create mode 100644 sponge/src/main/java/com/griefdefender/GDCore.java
 create mode 100644 sponge/src/main/java/com/griefdefender/GDDebugData.java
 create mode 100644 sponge/src/main/java/com/griefdefender/GDEventManager.java
 create mode 100644 sponge/src/main/java/com/griefdefender/GDPlayerData.java
 create mode 100644 sponge/src/main/java/com/griefdefender/GDRelocator.java
 create mode 100644 sponge/src/main/java/com/griefdefender/GDTimings.java
 create mode 100644 sponge/src/main/java/com/griefdefender/GDVersion.java
 create mode 100644 sponge/src/main/java/com/griefdefender/GriefDefenderPlugin.java
 create mode 100644 sponge/src/main/java/com/griefdefender/cache/EventResultCache.java
 create mode 100644 sponge/src/main/java/com/griefdefender/cache/MessageCache.java
 create mode 100644 sponge/src/main/java/com/griefdefender/cache/PermissionHolderCache.java
 create mode 100644 sponge/src/main/java/com/griefdefender/claim/ClaimContextCalculator.java
 create mode 100644 sponge/src/main/java/com/griefdefender/claim/GDClaim.java
 create mode 100644 sponge/src/main/java/com/griefdefender/claim/GDClaimManager.java
 create mode 100644 sponge/src/main/java/com/griefdefender/claim/GDClaimResult.java
 create mode 100644 sponge/src/main/java/com/griefdefender/claim/GDClaimSchematic.java
 create mode 100644 sponge/src/main/java/com/griefdefender/claim/GDClaimType.java
 create mode 100644 sponge/src/main/java/com/griefdefender/claim/GDShovelType.java
 create mode 100644 sponge/src/main/java/com/griefdefender/claim/GDTown.java
 create mode 100644 sponge/src/main/java/com/griefdefender/claim/GDTrustType.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/ClaimFlagBase.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/ClaimOptionBase.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/ClaimSubjectType.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandAdjustBonusClaimBlocks.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandCallback.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimAbandon.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimAbandonAll.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimAbandonTop.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimAdmin.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimBan.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimBank.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimBasic.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimBuy.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimBuyBlocks.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimClear.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimContract.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimCreate.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimCuboid.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimDelete.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimDeleteAll.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimDeleteAllAdmin.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimDeleteTop.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimExpand.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimFarewell.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimFlag.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimFlagDebug.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimFlagGroup.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimFlagPlayer.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimFlagReset.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimGreeting.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimIgnore.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimInfo.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimInherit.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimList.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimMode.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimName.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimOption.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimOptionGroup.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimOptionPlayer.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimPermissionGroup.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimPermissionPlayer.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimSchematic.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimSell.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimSellBlocks.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimSetSpawn.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimSpawn.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimSubdivision.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimTown.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimTransfer.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimUnban.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandClaimWorldEdit.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandDebug.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandException.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandGDReload.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandGDVersion.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandGiveBlocks.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandGivePet.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandHelp.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandHelper.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandPagination.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandPlayerInfo.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandRestoreClaim.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandRestoreNature.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandSetAccruedClaimBlocks.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandTownChat.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandTownTag.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandTrustGroup.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandTrustGroupAll.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandTrustList.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandTrustPlayer.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandTrustPlayerAll.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandUntrustGroup.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandUntrustGroupAll.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandUntrustPlayer.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/CommandUntrustPlayerAll.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/ComponentMessageException.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/gphelper/CommandAccessTrust.java
 create mode 100644 sponge/src/main/java/com/griefdefender/command/gphelper/CommandContainerTrust.java
 create mode 100644 sponge/src/main/java/com/griefdefender/configuration/ClaimDataConfig.java
 create mode 100644 sponge/src/main/java/com/griefdefender/configuration/ClaimStorageData.java
 create mode 100644 sponge/src/main/java/com/griefdefender/configuration/ClaimTemplateConfig.java
 create mode 100644 sponge/src/main/java/com/griefdefender/configuration/ClaimTemplateStorage.java
 create mode 100644 sponge/src/main/java/com/griefdefender/configuration/EconomyDataConfig.java
 create mode 100644 sponge/src/main/java/com/griefdefender/configuration/GriefDefenderConfig.java
 create mode 100644 sponge/src/main/java/com/griefdefender/configuration/IClaimData.java
 create mode 100644 sponge/src/main/java/com/griefdefender/configuration/MessageDataConfig.java
 create mode 100644 sponge/src/main/java/com/griefdefender/configuration/MessageStorage.java
 create mode 100644 sponge/src/main/java/com/griefdefender/configuration/PlayerDataConfig.java
 create mode 100644 sponge/src/main/java/com/griefdefender/configuration/PlayerStorageData.java
 create mode 100644 sponge/src/main/java/com/griefdefender/configuration/TownDataConfig.java
 create mode 100644 sponge/src/main/java/com/griefdefender/configuration/TownStorageData.java
 create mode 100644 sponge/src/main/java/com/griefdefender/configuration/category/BanCategory.java
 create mode 100644 sponge/src/main/java/com/griefdefender/configuration/category/BlacklistCategory.java
 create mode 100644 sponge/src/main/java/com/griefdefender/configuration/category/ClaimCategory.java
 create mode 100644 sponge/src/main/java/com/griefdefender/configuration/category/ConfigCategory.java
 create mode 100644 sponge/src/main/java/com/griefdefender/configuration/category/CustomFlagGroupCategory.java
 create mode 100644 sponge/src/main/java/com/griefdefender/configuration/category/CustomFlagGroupDefinitionCategory.java
 create mode 100644 sponge/src/main/java/com/griefdefender/configuration/category/DefaultPermissionCategory.java
 create mode 100644 sponge/src/main/java/com/griefdefender/configuration/category/EconomyCategory.java
 create mode 100644 sponge/src/main/java/com/griefdefender/configuration/category/GeneralCategory.java
 create mode 100644 sponge/src/main/java/com/griefdefender/configuration/category/MessageCategory.java
 create mode 100644 sponge/src/main/java/com/griefdefender/configuration/category/MigratorCategory.java
 create mode 100644 sponge/src/main/java/com/griefdefender/configuration/category/ModuleCategory.java
 create mode 100644 sponge/src/main/java/com/griefdefender/configuration/category/OptionCategory.java
 create mode 100644 sponge/src/main/java/com/griefdefender/configuration/category/PlayerDataCategory.java
 create mode 100644 sponge/src/main/java/com/griefdefender/configuration/category/PvpCategory.java
 create mode 100644 sponge/src/main/java/com/griefdefender/configuration/category/ThreadCategory.java
 create mode 100644 sponge/src/main/java/com/griefdefender/configuration/category/TownCategory.java
 create mode 100644 sponge/src/main/java/com/griefdefender/configuration/category/VisualCategory.java
 create mode 100644 sponge/src/main/java/com/griefdefender/configuration/serializer/ClaimTypeSerializer.java
 create mode 100644 sponge/src/main/java/com/griefdefender/configuration/serializer/ComponentConfigSerializer.java
 create mode 100644 sponge/src/main/java/com/griefdefender/configuration/serializer/CreateModeTypeSerializer.java
 create mode 100644 sponge/src/main/java/com/griefdefender/configuration/serializer/CustomFlagSerializer.java
 create mode 100644 sponge/src/main/java/com/griefdefender/configuration/serializer/GameModeTypeSerializer.java
 create mode 100644 sponge/src/main/java/com/griefdefender/configuration/serializer/WeatherTypeSerializer.java
 create mode 100644 sponge/src/main/java/com/griefdefender/configuration/type/ConfigBase.java
 create mode 100644 sponge/src/main/java/com/griefdefender/configuration/type/GlobalConfig.java
 create mode 100644 sponge/src/main/java/com/griefdefender/economy/GDBankTransaction.java
 create mode 100644 sponge/src/main/java/com/griefdefender/event/GDAttackPlayerEvent.java
 create mode 100644 sponge/src/main/java/com/griefdefender/event/GDBorderClaimEvent.java
 create mode 100644 sponge/src/main/java/com/griefdefender/event/GDCauseStackManager.java
 create mode 100644 sponge/src/main/java/com/griefdefender/event/GDChangeClaimEvent.java
 create mode 100644 sponge/src/main/java/com/griefdefender/event/GDClaimEvent.java
 create mode 100644 sponge/src/main/java/com/griefdefender/event/GDCreateClaimEvent.java
 create mode 100644 sponge/src/main/java/com/griefdefender/event/GDFlagPermissionEvent.java
 create mode 100644 sponge/src/main/java/com/griefdefender/event/GDGroupTrustClaimEvent.java
 create mode 100644 sponge/src/main/java/com/griefdefender/event/GDLoadClaimEvent.java
 create mode 100644 sponge/src/main/java/com/griefdefender/event/GDOptionEvent.java
 create mode 100644 sponge/src/main/java/com/griefdefender/event/GDPermissionEvent.java
 create mode 100644 sponge/src/main/java/com/griefdefender/event/GDRemoveClaimEvent.java
 create mode 100644 sponge/src/main/java/com/griefdefender/event/GDSaveClaimEvent.java
 create mode 100644 sponge/src/main/java/com/griefdefender/event/GDTaxClaimEvent.java
 create mode 100644 sponge/src/main/java/com/griefdefender/event/GDTransferClaimEvent.java
 create mode 100644 sponge/src/main/java/com/griefdefender/event/GDTrustClaimEvent.java
 create mode 100644 sponge/src/main/java/com/griefdefender/event/GDUserTrustClaimEvent.java
 create mode 100644 sponge/src/main/java/com/griefdefender/event/package-info.java
 create mode 100644 sponge/src/main/java/com/griefdefender/inject/GriefDefenderImplModule.java
 create mode 100644 sponge/src/main/java/com/griefdefender/internal/EntityRemovalListener.java
 create mode 100644 sponge/src/main/java/com/griefdefender/internal/NbtDataHelper.java
 create mode 100644 sponge/src/main/java/com/griefdefender/internal/pagination/ActivePagination.java
 create mode 100644 sponge/src/main/java/com/griefdefender/internal/pagination/GDPaginationBuilder.java
 create mode 100644 sponge/src/main/java/com/griefdefender/internal/pagination/GDPaginationCalculator.java
 create mode 100644 sponge/src/main/java/com/griefdefender/internal/pagination/GDPaginationHolder.java
 create mode 100644 sponge/src/main/java/com/griefdefender/internal/pagination/GDPaginationList.java
 create mode 100644 sponge/src/main/java/com/griefdefender/internal/pagination/IterablePagination.java
 create mode 100644 sponge/src/main/java/com/griefdefender/internal/pagination/ListPagination.java
 create mode 100644 sponge/src/main/java/com/griefdefender/internal/pagination/PaginationList.java
 create mode 100644 sponge/src/main/java/com/griefdefender/internal/pagination/TextComponentIterable.java
 create mode 100644 sponge/src/main/java/com/griefdefender/internal/pagination/TextComponentIterator.java
 create mode 100644 sponge/src/main/java/com/griefdefender/internal/pagination/font-sizes.json
 create mode 100644 sponge/src/main/java/com/griefdefender/internal/provider/GDActor.java
 create mode 100644 sponge/src/main/java/com/griefdefender/internal/provider/WorldEditProvider.java
 create mode 100644 sponge/src/main/java/com/griefdefender/internal/provider/worldedit/cui/MultiSelectionColors.java
 create mode 100644 sponge/src/main/java/com/griefdefender/internal/provider/worldedit/cui/MultiSelectionType.java
 create mode 100644 sponge/src/main/java/com/griefdefender/internal/provider/worldedit/cui/event/MultiSelectionClearEvent.java
 create mode 100644 sponge/src/main/java/com/griefdefender/internal/provider/worldedit/cui/event/MultiSelectionColorEvent.java
 create mode 100644 sponge/src/main/java/com/griefdefender/internal/provider/worldedit/cui/event/MultiSelectionCuboidEvent.java
 create mode 100644 sponge/src/main/java/com/griefdefender/internal/provider/worldedit/cui/event/MultiSelectionGridEvent.java
 create mode 100644 sponge/src/main/java/com/griefdefender/internal/provider/worldedit/cui/event/MultiSelectionPointEvent.java
 create mode 100644 sponge/src/main/java/com/griefdefender/internal/registry/BlockTypeRegistryModule.java
 create mode 100644 sponge/src/main/java/com/griefdefender/internal/registry/EntityTypeRegistryModule.java
 create mode 100644 sponge/src/main/java/com/griefdefender/internal/registry/GDBlockType.java
 create mode 100644 sponge/src/main/java/com/griefdefender/internal/registry/GDEntityType.java
 create mode 100644 sponge/src/main/java/com/griefdefender/internal/registry/GDItemType.java
 create mode 100644 sponge/src/main/java/com/griefdefender/internal/registry/ItemTypeRegistryModule.java
 create mode 100644 sponge/src/main/java/com/griefdefender/internal/util/BlockUtil.java
 create mode 100644 sponge/src/main/java/com/griefdefender/internal/util/NMSUtil.java
 create mode 100644 sponge/src/main/java/com/griefdefender/internal/util/VecHelper.java
 create mode 100644 sponge/src/main/java/com/griefdefender/internal/visual/ClaimVisual.java
 create mode 100644 sponge/src/main/java/com/griefdefender/internal/visual/GDClaimVisualType.java
 create mode 100644 sponge/src/main/java/com/griefdefender/listener/BlockEventHandler.java
 create mode 100644 sponge/src/main/java/com/griefdefender/listener/CommonEntityEventHandler.java
 create mode 100644 sponge/src/main/java/com/griefdefender/listener/EntityEventHandler.java
 create mode 100644 sponge/src/main/java/com/griefdefender/listener/LuckPermsEventHandler.java
 create mode 100644 sponge/src/main/java/com/griefdefender/listener/MCClansEventHandler.java
 create mode 100644 sponge/src/main/java/com/griefdefender/listener/NucleusEventHandler.java
 create mode 100644 sponge/src/main/java/com/griefdefender/listener/PlayerEventHandler.java
 create mode 100644 sponge/src/main/java/com/griefdefender/listener/WorldEventHandler.java
 create mode 100644 sponge/src/main/java/com/griefdefender/migrator/GPBukkitMigrator.java
 create mode 100644 sponge/src/main/java/com/griefdefender/migrator/GPSpongeMigrator.java
 create mode 100644 sponge/src/main/java/com/griefdefender/migrator/RedProtectMigrator.java
 create mode 100644 sponge/src/main/java/com/griefdefender/migrator/WorldGuardMigrator.java
 create mode 100644 sponge/src/main/java/com/griefdefender/permission/ContextGroupKeys.java
 create mode 100644 sponge/src/main/java/com/griefdefender/permission/ContextGroups.java
 create mode 100644 sponge/src/main/java/com/griefdefender/permission/GDPermissionGroup.java
 create mode 100644 sponge/src/main/java/com/griefdefender/permission/GDPermissionHolder.java
 create mode 100644 sponge/src/main/java/com/griefdefender/permission/GDPermissionManager.java
 create mode 100644 sponge/src/main/java/com/griefdefender/permission/GDPermissionResult.java
 create mode 100644 sponge/src/main/java/com/griefdefender/permission/GDPermissionUser.java
 create mode 100644 sponge/src/main/java/com/griefdefender/permission/GDPermissions.java
 create mode 100644 sponge/src/main/java/com/griefdefender/permission/GDResultType.java
 create mode 100644 sponge/src/main/java/com/griefdefender/permission/flag/CustomFlagData.java
 create mode 100644 sponge/src/main/java/com/griefdefender/permission/flag/FlagContexts.java
 create mode 100644 sponge/src/main/java/com/griefdefender/permission/flag/GDActiveFlagData.java
 create mode 100644 sponge/src/main/java/com/griefdefender/permission/flag/GDCustomFlagDefinition.java
 create mode 100644 sponge/src/main/java/com/griefdefender/permission/flag/GDCustomFlagDefinitions.java
 create mode 100644 sponge/src/main/java/com/griefdefender/permission/flag/GDFlag.java
 create mode 100644 sponge/src/main/java/com/griefdefender/permission/flag/GDFlags.java
 create mode 100644 sponge/src/main/java/com/griefdefender/permission/option/GDOption.java
 create mode 100644 sponge/src/main/java/com/griefdefender/permission/option/OptionBuilder.java
 create mode 100644 sponge/src/main/java/com/griefdefender/permission/option/type/GDCreateModeType.java
 create mode 100644 sponge/src/main/java/com/griefdefender/permission/option/type/GDGameModeType.java
 create mode 100644 sponge/src/main/java/com/griefdefender/permission/option/type/GDWeatherType.java
 create mode 100644 sponge/src/main/java/com/griefdefender/permission/ui/ClaimClickData.java
 create mode 100644 sponge/src/main/java/com/griefdefender/permission/ui/FlagData.java
 create mode 100644 sponge/src/main/java/com/griefdefender/permission/ui/MenuType.java
 create mode 100644 sponge/src/main/java/com/griefdefender/permission/ui/OptionData.java
 create mode 100644 sponge/src/main/java/com/griefdefender/permission/ui/UIHelper.java
 create mode 100644 sponge/src/main/java/com/griefdefender/provider/LuckPermsProvider.java
 create mode 100644 sponge/src/main/java/com/griefdefender/provider/MCClansProvider.java
 create mode 100644 sponge/src/main/java/com/griefdefender/provider/NucleusProvider.java
 create mode 100644 sponge/src/main/java/com/griefdefender/provider/PermissionProvider.java
 create mode 100644 sponge/src/main/java/com/griefdefender/registry/ChatTypeRegistryModule.java
 create mode 100644 sponge/src/main/java/com/griefdefender/registry/ClaimTypeRegistryModule.java
 create mode 100644 sponge/src/main/java/com/griefdefender/registry/CreateModeTypeRegistryModule.java
 create mode 100644 sponge/src/main/java/com/griefdefender/registry/FlagRegistryModule.java
 create mode 100644 sponge/src/main/java/com/griefdefender/registry/GDRegistry.java
 create mode 100644 sponge/src/main/java/com/griefdefender/registry/GameModeTypeRegistryModule.java
 create mode 100644 sponge/src/main/java/com/griefdefender/registry/OptionRegistryModule.java
 create mode 100644 sponge/src/main/java/com/griefdefender/registry/ResultTypeRegistryModule.java
 create mode 100644 sponge/src/main/java/com/griefdefender/registry/ShovelTypeRegistryModule.java
 create mode 100644 sponge/src/main/java/com/griefdefender/registry/TrustTypeRegistryModule.java
 create mode 100644 sponge/src/main/java/com/griefdefender/registry/WeatherTypeRegistryModule.java
 create mode 100644 sponge/src/main/java/com/griefdefender/storage/BaseStorage.java
 create mode 100644 sponge/src/main/java/com/griefdefender/storage/FileStorage.java
 create mode 100644 sponge/src/main/java/com/griefdefender/task/ClaimBlockTask.java
 create mode 100644 sponge/src/main/java/com/griefdefender/task/ClaimCleanupTask.java
 create mode 100644 sponge/src/main/java/com/griefdefender/task/ClaimVisualApplyTask.java
 create mode 100644 sponge/src/main/java/com/griefdefender/task/ClaimVisualRevertTask.java
 create mode 100644 sponge/src/main/java/com/griefdefender/task/PlayerTickTask.java
 create mode 100644 sponge/src/main/java/com/griefdefender/task/TaxApplyTask.java
 create mode 100644 sponge/src/main/java/com/griefdefender/text/action/GDCallbackHolder.java
 create mode 100644 sponge/src/main/java/com/griefdefender/util/BlockPosCache.java
 create mode 100644 sponge/src/main/java/com/griefdefender/util/BootstrapUtil.java
 create mode 100644 sponge/src/main/java/com/griefdefender/util/CauseContextHelper.java
 create mode 100644 sponge/src/main/java/com/griefdefender/util/ClaimClickData.java
 create mode 100644 sponge/src/main/java/com/griefdefender/util/EconomyUtil.java
 create mode 100644 sponge/src/main/java/com/griefdefender/util/EntityUtils.java
 create mode 100644 sponge/src/main/java/com/griefdefender/util/HttpClient.java
 create mode 100644 sponge/src/main/java/com/griefdefender/util/PaginationUtil.java
 create mode 100644 sponge/src/main/java/com/griefdefender/util/PermissionUtil.java
 create mode 100644 sponge/src/main/java/com/griefdefender/util/PlayerUtil.java
 create mode 100644 sponge/src/main/java/com/griefdefender/util/SpongeContexts.java
 create mode 100644 sponge/src/main/java/com/griefdefender/util/SpongeUtil.java
 create mode 100644 sponge/src/main/java/com/griefdefender/util/TaskUtil.java
 create mode 100644 sponge/src/main/resources/1.12.2.json
 create mode 100644 sponge/src/main/resources/LICENSE
 create mode 100644 sponge/src/main/resources/META-INF/griefdefender_at.cfg
 create mode 100644 sponge/src/main/resources/assets/lang/en_US.conf
 create mode 100644 sponge/src/main/resources/assets/lang/fr_FR.conf
 create mode 100644 sponge/src/main/resources/assets/lang/ru_RU.conf
 create mode 100644 sponge/src/main/resources/com/griefdefender/internal/pagination/font-sizes.json

diff --git a/settings.gradle b/settings.gradle
index 87e30e7..59c8f44 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,2 +1,3 @@
 include "bukkit"
+include "sponge"
 include "GriefDefenderAPI"
\ No newline at end of file
diff --git a/sponge/build.gradle b/sponge/build.gradle
new file mode 100644
index 0000000..fe0a18a
--- /dev/null
+++ b/sponge/build.gradle
@@ -0,0 +1,203 @@
+buildscript {
+    repositories {
+        maven {
+            name = 'forge'
+            url = 'http://files.minecraftforge.net/maven'
+        }
+        maven {
+            url = 'https://plugins.gradle.org/m2/'
+        }
+    }
+
+    dependencies {
+        classpath 'net.minecraftforge.gradle:ForgeGradle:2.3-SNAPSHOT'
+        classpath 'com.github.jengelman.gradle.plugins:shadow:5.1.0'
+    }
+}
+
+plugins {
+    id 'com.github.johnrengelman.shadow' version '5.1.0'
+    id 'org.spongepowered.plugin' version '0.8.1'
+    id 'net.minecrell.vanillagradle.server' version '2.2-4'
+    id 'java'
+}
+
+compileJava.options.encoding = 'UTF-8'
+
+// Environment variables for the build set by the build server
+ext.buildNumber = System.env.BUILD_NUMBER ?: '0'
+
+defaultTasks 'clean', 'build'
+
+sourceCompatibility = '1.8'
+targetCompatibility = '1.8'
+
+archivesBaseName = project.name.toLowerCase()
+
+project.ext.getGitHash = {
+    def command = Runtime.getRuntime().exec("git rev-parse --short HEAD")
+    def result = command.waitFor()
+    return (result == 0) ? command.inputStream.text.trim() : "nogit"
+}
+
+repositories {
+    mavenLocal()
+    maven {
+        name = 'sk89q'
+        url = 'https://maven.sk89q.com/repo'
+    }
+    maven {
+        name = 'bstats'
+        url = 'https://repo.codemc.org/repository/maven-public'
+    }
+    maven {
+        name = 'sponge'
+        url = 'https://repo.spongepowered.org/maven/'
+    }
+    maven {
+        name = 'nucleus'
+        url = 'http://repo.drnaylor.co.uk/artifactory/list/minecraft'
+    }
+    maven {
+        name = 'sonatype_releases'
+        url = 'https://oss.sonatype.org/content/repositories/releases'
+    }
+    maven {
+        name = 'sonatype_snapshots'
+        url = 'https://oss.sonatype.org/content/repositories/snapshots'
+    }
+    maven {
+        name = 'glare'
+        url = 'https://repo.glaremasters.me/repository/bloodshot'
+    }
+    maven {
+        name = 'aikar'
+        url = 'https://repo.aikar.co/content/groups/aikar'
+    }
+    maven {
+        name = 'worldedit'
+        url = 'http://maven.sk89q.com/artifactory/repo'
+    }
+    maven {
+        name = 'jitpack'
+        url = 'https://jitpack.io'
+    }
+}
+
+minecraft {
+    version = project.minecraftVersion
+    mappings = project.mcpMappings
+}
+
+sourceSets {
+    api
+}
+
+dependencies {
+    compileOnly 'com.griefdefender:api:1.0.0-20190906.173641-10'
+    compileOnly "com.griefdefender:reflect-helper:1.0"
+    // Sponge
+    apiCompile "org.spongepowered:spongeapi:$apiVersion"
+
+    compileOnly ("org.spongepowered:spongecommon:$commonVersion:dev") {
+        exclude module: 'testplugins'
+    }
+    // Plugins
+    compileOnly ("io.github.nucleuspowered:nucleus-api:1.14.1-S7.1"){
+        exclude module: 'spongeapi'
+    }
+    compile "com.github.bloodmc:mcclans-api:develop-SNAPSHOT"
+    compileOnly "com.sk89q.worldedit:worldedit-core:6.1.4-SNAPSHOT"
+
+    // required for bootstrap
+    compile "com.googlecode.json-simple:json-simple:1.1.1"
+    compileOnly "aopalliance:aopalliance:1.0"
+    compileOnly "co.aikar:acf-core:0.5.0-SNAPSHOT"
+    compileOnly "co.aikar:acf-sponge:0.5.0-SNAPSHOT"
+    compileOnly "co.aikar:locales:1.0-SNAPSHOT"
+    compileOnly "co.aikar:minecraft-timings:1.0.4"
+    compileOnly "co.aikar:Table:1.0.0-SNAPSHOT"
+    compileOnly "com.flowpowered:flow-math:1.0.3"
+    compileOnly "com.github.ben-manes.caffeine:caffeine:2.7.0"
+    compileOnly "com.squareup.okhttp3:okhttp:3.14.2"
+    compileOnly "com.squareup.okio:okio:2.2.2"
+    compileOnly "commons-io:commons-io:2.6"
+    compileOnly "it.unimi.dsi:fastutil:8.2.3"
+    compileOnly "javax.inject:javax.inject:1"
+    compileOnly "me.lucko:jar-relocator:1.3"
+    compileOnly "me.lucko.luckperms:luckperms-api:4.4"
+    compileOnly "net.jodah:expiringmap:0.5.9"
+    compileOnly "org.apache.commons:commons-lang3:3.9"
+    compileOnly "org.checkerframework:checker:2.8.2"
+    compileOnly "org.jetbrains:annotations:17.0.0"
+    compileOnly "org.jetbrains.kotlin:kotlin-stdlib:1.3.31"
+    compileOnly "org.ow2.asm:asm-debug-all:5.2"
+    compileOnly "org.spongepowered:configurate-core:3.7-SNAPSHOT"
+    compileOnly "org.spongepowered:configurate-gson:3.7-SNAPSHOT"
+    compileOnly "org.spongepowered:configurate-hocon:3.7-SNAPSHOT"
+    compileOnly "org.spongepowered:configurate-yaml:3.7-SNAPSHOT"
+    compileOnly "net.kyori:event-api:3.0.0"
+    compileOnly "net.kyori:event-method:3.0.0"
+    compileOnly "net.kyori:event-method-asm:3.0.0"
+    compileOnly "net.kyori:text-adapter-bukkit:3.0.3"
+    compileOnly "net.kyori:text-adapter-bungeecord:3.0.3"
+    compileOnly "net.kyori:text-adapter-spongeapi:3.0.3"
+    compileOnly "net.kyori:text-api:3.0.2"
+    compileOnly "net.kyori:text-serializer-gson:3.0.2"
+    compileOnly "net.kyori:text-serializer-legacy:3.0.2"
+    compileOnly "net.kyori:text-serializer-plain:3.0.2"
+}
+
+jar {
+    manifest.attributes('FMLAT': 'griefdefender_at.cfg')
+    manifest.attributes('Implementation-Title': 'GriefDefender')
+    manifest.attributes('Implementation-Version': "$version")
+    manifest.attributes('Git-Hash': project.ext.getGitHash())
+    classifier = 'SNAPSHOT'
+    baseName = 'griefdefender-sponge'
+}
+
+if (JavaVersion.current().isJava8Compatible()) {
+    tasks.withType(Javadoc) {
+        options.addStringOption('Xdoclint:none', '-quiet')
+    }
+}
+
+task javadocJar(type: Jar, dependsOn: javadoc) {
+    classifier = 'javadoc'
+    from javadoc.destinationDir
+}
+
+artifacts {
+    archives shadowJar
+}
+
+shadowJar {
+    mainSpec.sourcePaths.clear()
+    dependsOn reobfJar
+
+    classifier = ''
+
+    dependencies {
+        include dependency("com.squareup.okhttp3:okhttp:3.9.1")
+        include dependency("com.squareup.okio:okio:1.13.0")
+        include dependency("com.googlecode.json-simple:json-simple:1.1.1")
+    }
+
+    relocate("aopalliance", "com.griefdefender.lib.aopalliance")
+    relocate("com.github.benmanes.caffeine", "com.griefdefender.lib.caffeine")
+    relocate("it.unimi.dsi", "com.griefdefender.lib.fastutil")
+    relocate("net.jodah", "com.griefdefender.lib.jodah")
+    relocate("okhttp3", "com.griefdefender.lib.okhttp3")
+    relocate("okio", "com.griefdefender.lib.okio")
+    relocate("org.apache.commons.io", "com.griefdefender.lib.commonsio")
+    relocate("org.apache.commons.lang3", "com.griefdefender.lib.commonslang3")
+    relocate("org.checkerframework", "com.griefdefender.lib.checkerframework")
+    relocate("org.jetbrains", "com.griefdefender.lib.jetbrains")
+
+
+    exclude "dummyThing"
+    afterEvaluate {
+        from zipTree(reobfJar.jar)
+    }
+}
\ No newline at end of file
diff --git a/sponge/gradle.properties b/sponge/gradle.properties
new file mode 100644
index 0000000..cafd4bc
--- /dev/null
+++ b/sponge/gradle.properties
@@ -0,0 +1,11 @@
+name=GriefDefender
+group=com.griefdefender
+url=https://github.com/bloodmc/GriefDefender
+version=1.2.2
+apiVersion=7.2.0-SNAPSHOT
+commonVersion=1.12.2-7.1.7-SNAPSHOT
+
+minecraftVersion=1.12.2
+mcpMappings=snapshot_20180808
+
+org.gradle.jvmargs=-Xmx3G
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/GDBootstrap.java b/sponge/src/main/java/com/griefdefender/GDBootstrap.java
new file mode 100644
index 0000000..a6c5b7a
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/GDBootstrap.java
@@ -0,0 +1,228 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender;
+
+import com.google.inject.Inject;
+import com.griefdefender.util.BootstrapUtil;
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.slf4j.Logger;
+import org.spongepowered.api.MinecraftVersion;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.config.ConfigDir;
+import org.spongepowered.api.event.Listener;
+import org.spongepowered.api.event.Order;
+import org.spongepowered.api.event.game.state.GamePreInitializationEvent;
+import org.spongepowered.api.plugin.Plugin;
+import org.spongepowered.api.plugin.PluginContainer;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.net.URLConnection;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.MessageDigest;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+@Plugin(id = "griefdefender", name = "GriefDefender", version = "1.0.0", description = "Designed to defend world from all types of grief.")
+public class GDBootstrap {
+
+    @Inject public PluginContainer pluginContainer;
+    @Inject private Logger logger;
+    @Inject @ConfigDir(sharedRoot = false)
+    private Path configPath;
+
+    private Map<String, File> jarMap = new HashMap<>();
+    private List<String> relocateList = new ArrayList<>();
+    private static GDBootstrap instance;
+    private static final String LIB_ROOT_PATH = "./config/griefdefender/lib/";
+    private static final String USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.75 Safari/535.7";
+
+    public static GDBootstrap getInstance() {
+        return instance;
+    }
+
+    @Listener(order = Order.LAST)
+    public void onPreInit(GamePreInitializationEvent event) {
+        instance = this;
+        final JSONParser parser = new JSONParser();
+        String bukkitJsonVersion = null;
+        this.getLogger().info("Loading libraries...");
+        final MinecraftVersion version = Sponge.getPlatform().getMinecraftVersion();
+        if (Sponge.getPlatform().getMinecraftVersion().getName().contains("1.12.2")) {
+            bukkitJsonVersion = "1.12.2";
+        } else {
+            this.getLogger().error("Detected unsupported version '" + version.getName() + "'. GriefDefender only 1.12.2. GriefDefender will NOT load.");
+            return;
+        }
+        try {
+            final InputStream in = getClass().getResourceAsStream("/" + bukkitJsonVersion + ".json");
+            final BufferedReader reader = new BufferedReader(new InputStreamReader(in));
+            final JSONObject a = (JSONObject) parser.parse(reader);
+            final JSONArray libraries = (JSONArray) a.get("libraries");
+            if (libraries == null) {
+                this.getLogger().error("Resource " + bukkitJsonVersion + ".json is corrupted!. Please contact author for assistance.");
+                return;
+            }
+            final Iterator<JSONObject> iterator = libraries.iterator();
+            while (iterator.hasNext()) {
+                JSONObject lib = iterator.next();
+                final String name = (String) lib.get("name");
+                final String sha1 = (String) lib.get("sha1");
+                final String path = (String) lib.get("path");
+                final String relocate = (String) lib.get("relocate");
+                final String url = (String) lib.get("url");
+                final Path libPath = Paths.get(LIB_ROOT_PATH).resolve(path);
+                final File file = libPath.toFile();
+                downloadLibrary(name, relocate, sha1, url, libPath);
+            }
+        } catch (Throwable t) {
+            t.printStackTrace();
+        }
+        // Inject jar-relocator and asm debug
+        injectRelocatorDeps();
+        // Relocate all GD dependencies and inject
+        GDRelocator.getInstance().relocateJars(this.jarMap);
+        // Boot GD
+        GriefDefenderPlugin.getInstance().onPreInit(event, this.logger, this.configPath, this.pluginContainer);
+        //Sponge.getEventManager().registerListeners(GriefDefenderPlugin.getInstance(), GriefDefenderPlugin.getInstance());
+    }
+
+    public List<String> getRelocateList() {
+        return this.relocateList;
+    }
+
+    private void injectRelocatorDeps() {
+        String name = "org.ow2.asm:asm-debug-all:5.2";
+        File file = this.jarMap.get(name);
+        BootstrapUtil.addUrlToClassLoader(name, file);
+        name = "me.lucko:jar-relocator:1.3";
+        file = this.jarMap.get(name);
+        BootstrapUtil.addUrlToClassLoader(name, file);
+        // inject reflect helper
+        final String javaVersion = System.getProperty("java.version");
+        if (getJavaVersion() >= 11) {
+            name = "com.griefdefender:reflect-helper:2.0";
+        } else {
+            name = "com.griefdefender:reflect-helper:1.0";
+        }
+        file = this.jarMap.get(name);
+        BootstrapUtil.addUrlToClassLoader(name, file);
+    }
+
+    public void downloadLibrary(String name, String relocate, String sha1, String url, Path libPath) {
+        final File file = libPath.toFile();
+        this.jarMap.put(name, file);
+        if (relocate != null && !relocate.isEmpty() && relocate.contains(":")) {
+            this.relocateList.add(relocate);
+        }
+        if (!Files.exists(libPath)) {
+            this.getLogger().info("Downloading library " + name + " ...");
+            try {
+                URL website = new URL(url);
+                URLConnection urlConnection = website.openConnection();
+                // Some maven repos like nexus require a user agent so we just pass one to satisfy it
+                urlConnection.setRequestProperty("User-Agent", USER_AGENT);
+                ReadableByteChannel rbc = Channels.newChannel(urlConnection.getInputStream());
+                if (!Files.exists(libPath)) {
+                    file.getParentFile().mkdirs();
+                }
+                FileOutputStream fos = new FileOutputStream(file);
+                fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
+                fos.close();
+            } catch (IOException e) {
+                this.getLogger().error("An error occured while downloading library '" + name + "'. Skipping...");
+                e.printStackTrace();
+                return;
+            }
+
+            
+            final String hash = getLibraryHash(file);
+            
+            if (hash == null || !sha1.equals(hash)) {
+                this.getLogger().error("Detected invalid hash '" + hash + "' for file '" + libPath + "'. Expected '" + sha1 + "'. Skipping...");
+                try {
+                    Files.delete(libPath);
+                    return;
+                } catch (IOException e) {
+                    e.printStackTrace();
+                    return;
+                }
+            }
+        }
+
+        this.jarMap.put(name, file);
+    }
+
+    private String getLibraryHash(File file) {
+        try {
+            final MessageDigest md = MessageDigest.getInstance("SHA-1");
+            final byte[] data = Files.readAllBytes(file.toPath());
+            final byte[] b = md.digest(data); 
+            StringBuffer buffer = new StringBuffer();
+            for (int i = 0; i < b.length; i++) {
+                if ((0xff & b[i]) < 0x10) {
+                    buffer.append("0" + Integer.toHexString((0xFF & b[i])));
+                } else {
+                    buffer.append(Integer.toHexString(0xFF & b[i]));
+                }
+            }
+            return buffer.toString();
+        } catch (Throwable t) {
+            t.printStackTrace();
+        }
+        return null;
+    }
+
+    public Logger getLogger() {
+        return this.logger;
+    }
+
+    private static int getJavaVersion() {
+        String version = System.getProperty("java.version");
+        if(version.startsWith("1.")) {
+            version = version.substring(2, 3);
+        } else {
+            final int dot = version.indexOf(".");
+            if(dot != -1) { 
+                version = version.substring(0, dot);
+            }
+        }
+        return Integer.parseInt(version);
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/GDCatalogType.java b/sponge/src/main/java/com/griefdefender/GDCatalogType.java
new file mode 100644
index 0000000..b106f3b
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/GDCatalogType.java
@@ -0,0 +1,76 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) SpongePowered <https://www.spongepowered.org>
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.griefdefender.api.CatalogType;
+
+import java.util.StringJoiner;
+
+public abstract class GDCatalogType implements CatalogType {
+
+    private final String id;
+
+    public GDCatalogType(String id) {
+        this.id = checkNotNull(id, "id");
+    }
+
+    @Override
+    public final String getId() {
+        return this.id;
+    }
+
+    @Override
+    public String getName() {
+        return getId();
+    }
+
+    @Override
+    public final int hashCode() {
+        return this.id.hashCode();
+    }
+
+    @Override
+    public final boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final CatalogType other = (CatalogType) obj;
+        return getId().equals(other.getId());
+    }
+
+    @Override
+    public String toString() {
+        return new StringJoiner(", ", GDCatalogType.class.getSimpleName() + "[", "]")
+                .add("id=" + getId())
+                .add("name=" + getName())
+                .toString();
+    }
+
+}
diff --git a/sponge/src/main/java/com/griefdefender/GDChatType.java b/sponge/src/main/java/com/griefdefender/GDChatType.java
new file mode 100644
index 0000000..7e4444b
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/GDChatType.java
@@ -0,0 +1,49 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender;
+
+import com.griefdefender.api.ChatType;
+
+public class GDChatType implements ChatType {
+
+    private final String id;
+    private final String name;
+
+    public GDChatType(String id, String name) {
+        this.id = id;
+        this.name = name;
+    }
+
+    @Override
+    public String getId() {
+        return this.id;
+    }
+
+    @Override
+    public String getName() {
+        return this.name;
+    }
+
+}
diff --git a/sponge/src/main/java/com/griefdefender/GDCore.java b/sponge/src/main/java/com/griefdefender/GDCore.java
new file mode 100644
index 0000000..722c2b6
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/GDCore.java
@@ -0,0 +1,109 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender;
+
+import com.google.common.collect.ImmutableList;
+import com.google.inject.Singleton;
+import com.griefdefender.api.Core;
+import com.griefdefender.api.Group;
+import com.griefdefender.api.Subject;
+import com.griefdefender.api.User;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.ClaimBlockSystem;
+import com.griefdefender.api.claim.ClaimManager;
+import com.griefdefender.api.data.PlayerData;
+import com.griefdefender.api.permission.flag.Flag;
+import com.griefdefender.cache.PermissionHolderCache;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.world.World;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+
+@Singleton
+public class GDCore implements Core {
+
+    @Override
+    public boolean isEnabled(UUID worldUniqueId) {
+        return GriefDefenderPlugin.getInstance().claimsEnabledForWorld(worldUniqueId);
+    }
+
+    @Override
+    public ClaimBlockSystem getClaimBlockSystem() {
+        return GriefDefenderPlugin.getGlobalConfig().getConfig().playerdata.claimBlockSystem;
+    }
+
+    @Override
+    public boolean isEconomyModeEnabled() {
+        return GriefDefenderPlugin.getInstance().isEconomyModeEnabled();
+    }
+
+    @Override
+    public boolean isProtectionModuleEnabled(Flag flag) {
+        return GriefDefenderPlugin.getGlobalConfig().getConfig().modules.isProtectionModuleEnabled(flag.toString());
+    }
+
+    @Override
+    public ClaimManager getClaimManager(UUID worldUniqueId) {
+        return GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(worldUniqueId);
+    }
+
+    @Override
+    public Optional<PlayerData> getPlayerData(UUID worldUniqueId, UUID playerUniqueId) {
+        return Optional.ofNullable(GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(worldUniqueId, playerUniqueId));
+    }
+
+    @Override
+    public List<Claim> getAllPlayerClaims(UUID playerUniqueId) {
+        List<Claim> claimList = new ArrayList<>();
+        for (World world : Sponge.getServer().getWorlds()) {
+            claimList.addAll(this.getClaimManager(world.getUniqueId()).getPlayerClaims(playerUniqueId));
+        }
+
+        return ImmutableList.copyOf(claimList);
+    }
+
+    @Override
+    public Subject getDefaultSubject() {
+        return GriefDefenderPlugin.DEFAULT_HOLDER;
+    }
+
+    @Override
+    public Subject getSubject(String identifier) {
+        return PermissionHolderCache.getInstance().getOrCreateHolder(identifier);
+    }
+
+    @Override
+    public User getUser(UUID uuid) {
+        return PermissionHolderCache.getInstance().getOrCreateUser(uuid);
+    }
+
+    @Override
+    public Group getGroup(String name) {
+        return PermissionHolderCache.getInstance().getOrCreateGroup(name);
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/GDDebugData.java b/sponge/src/main/java/com/griefdefender/GDDebugData.java
new file mode 100644
index 0000000..41eca12
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/GDDebugData.java
@@ -0,0 +1,224 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.griefdefender.api.Tristate;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.util.HttpClient;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import net.kyori.text.event.ClickEvent;
+import net.kyori.text.format.TextColor;
+import net.kyori.text.serializer.plain.PlainComponentSerializer;
+import okhttp3.MediaType;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+import okhttp3.ResponseBody;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.command.CommandSource;
+import org.spongepowered.api.entity.living.player.User;
+import org.spongepowered.api.plugin.PluginContainer;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.zip.GZIPOutputStream;
+
+public class GDDebugData {
+    private static final String BYTEBIN_ENDPOINT = "https://bytebin.lucko.me/post";
+    private static final String DEBUG_VIEWER_URL = "https://griefdefender.github.io/debug/?";
+    private static final MediaType PLAIN_TYPE = MediaType.parse("text/plain; charset=utf-8");
+
+    private static final int MAX_LINES = 5000;
+    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
+    private static final Component GD_TEXT = TextComponent.builder("").append("[", TextColor.WHITE).append("GD", TextColor.AQUA).append("] ", TextColor.WHITE).build();
+
+    private final CommandSource source;
+    private final List<String> header;
+    private final List<String> records;
+    private final long startTime = System.currentTimeMillis();
+    private boolean verbose;
+    private User target;
+
+    public GDDebugData(CommandSource source, User target, boolean verbose) {
+        this.source = source;
+        this.target = target;
+        this.verbose = verbose;
+        this.records = new ArrayList<>();
+        this.header = new ArrayList<>();
+        this.header.add("# GriefDefender Debug Log");
+        this.header.add("#### This file was automatically generated by [GriefDefender](https://github.com/bloodmc/GriefDefender) ");
+        this.header.add("");
+        this.header.add("### Metadata");
+        this.header.add("| Key | Value |");
+        this.header.add("|-----|-------|");
+        this.header.add("| GD Version | " + GriefDefenderPlugin.IMPLEMENTATION_VERSION + "|");
+        this.header.add("| Sponge Version | " + GriefDefenderPlugin.SPONGE_VERSION + "|");
+        final PluginContainer lpContainer = Sponge.getPluginManager().getPlugin("luckperms").orElse(null);
+        if (lpContainer != null) {
+            final String version = lpContainer.getVersion().orElse(null);
+            if (version != null) {
+                this.header.add("| LuckPerms Version | " + version);
+            }
+        }
+        this.header.add("| " + PlainComponentSerializer.INSTANCE.serialize(MessageCache.getInstance().LABEL_USER) + " | " + (this.target == null ? "ALL" : this.target.getName()) + "|");
+        this.header.add("| " + PlainComponentSerializer.INSTANCE.serialize(MessageCache.getInstance().DEBUG_RECORD_START) + " | " + DATE_FORMAT.format(new Date(this.startTime)) + "|");
+    }
+
+    public void addRecord(String flag, String trust, String source, String target, String location, String user, String permission, Tristate result) {
+        if (this.records.size() < MAX_LINES) {
+            this.records.add("| " + flag + " | " + trust + " | " + source + " | " + target + " | " + location + " | " + user + " | " + permission + " | " + result + " | ");
+        } else {
+            TextAdapter.sendComponent(this.source, TextComponent.builder("").append("MAX DEBUG LIMIT REACHED!").append("\n")
+                    .append("Pasting output...", TextColor.GREEN).build());
+            this.pasteRecords();
+            this.records.clear();
+            GriefDefenderPlugin.debugActive = false;
+            TextAdapter.sendComponent(this.source, TextComponent.builder("").append(GD_TEXT).append("Debug ", TextColor.GRAY).append("OFF", TextColor.RED).build());
+        }
+    }
+
+    public CommandSource getSource() {
+        return this.source;
+    }
+
+    public User getTarget() {
+        return this.target;
+    }
+
+    public boolean isRecording() {
+        return !this.verbose;
+    }
+
+    public void setTarget(User user) {
+        this.target = user;
+    }
+
+    public void setVerbose(boolean verbose) {
+        this.verbose = verbose;
+    }
+
+    public void pasteRecords() {
+        if (this.records.isEmpty()) {
+            TextAdapter.sendComponent(this.source, MessageCache.getInstance().DEBUG_NO_RECORDS);
+            return;
+        }
+
+        final long endTime = System.currentTimeMillis();
+        List<String> debugOutput = new ArrayList<>(this.header);
+        final String RECORD_END = PlainComponentSerializer.INSTANCE.serialize(MessageCache.getInstance().DEBUG_RECORD_END);
+        final String TIME_ELAPSED = PlainComponentSerializer.INSTANCE.serialize(MessageCache.getInstance().DEBUG_TIME_ELAPSED);
+        final String OUTPUT = PlainComponentSerializer.INSTANCE.serialize(MessageCache.getInstance().LABEL_OUTPUT);
+        final String FLAG = PlainComponentSerializer.INSTANCE.serialize(MessageCache.getInstance().LABEL_FLAG);
+        final String TRUST = PlainComponentSerializer.INSTANCE.serialize(MessageCache.getInstance().LABEL_TRUST);
+        final String LOCATION = PlainComponentSerializer.INSTANCE.serialize(MessageCache.getInstance().LABEL_LOCATION);
+        final String SOURCE = PlainComponentSerializer.INSTANCE.serialize(MessageCache.getInstance().LABEL_SOURCE);
+        final String TARGET = PlainComponentSerializer.INSTANCE.serialize(MessageCache.getInstance().LABEL_TARGET);
+        final String USER = PlainComponentSerializer.INSTANCE.serialize(MessageCache.getInstance().LABEL_USER);
+        final String PERMISSION = PlainComponentSerializer.INSTANCE.serialize(MessageCache.getInstance().LABEL_PERMISSION);
+        final String RESULT = PlainComponentSerializer.INSTANCE.serialize(MessageCache.getInstance().LABEL_RESULT);
+        debugOutput.add("| " + RECORD_END + " | " + DATE_FORMAT.format(new Date(endTime)) + "|");
+        long elapsed = (endTime - startTime) / 1000L; 
+        debugOutput.add("| " + TIME_ELAPSED + " | " + elapsed + " seconds" + "|");
+        debugOutput.add("");
+        debugOutput.add("### " + OUTPUT) ;
+        debugOutput.add("| " + FLAG + " | " + TRUST + " | " + SOURCE + " | " + TARGET + " | " + LOCATION + " | " + USER + " | " + PERMISSION + " | " + RESULT + " |");
+        debugOutput.add("|------|-------|--------|--------|----------|------|------------|--------|");
+
+        debugOutput.addAll(this.records);
+
+        String content = String.join("\n", debugOutput);
+
+        String pasteId;
+        try {
+            pasteId = postContent(content);
+        } catch (Exception e) {
+            TextAdapter.sendComponent(this.source, GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.DEBUG_ERROR_UPLOAD,
+                    ImmutableMap.of("content", TextComponent.of(e.getMessage(), TextColor.WHITE))));
+            return;
+        }
+
+        String url = DEBUG_VIEWER_URL + pasteId;
+
+        URL jUrl;
+        try {
+            jUrl = new URL(url);
+        } catch (MalformedURLException e) {
+            throw new RuntimeException(e);
+        }
+
+        TextAdapter.sendComponent(this.source, TextComponent.builder()
+                .append(MessageCache.getInstance().DEBUG_PASTE_SUCCESS)
+                .append(" : " + url, TextColor.GREEN)
+                .clickEvent(ClickEvent.openUrl(jUrl.toString())).build());
+    }
+
+    private static String postContent(String content) throws IOException {
+        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
+        try (GZIPOutputStream writer = new GZIPOutputStream(byteOut)) {
+            writer.write(content.getBytes(StandardCharsets.UTF_8));
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+
+        RequestBody body = RequestBody.create(PLAIN_TYPE, byteOut.toByteArray());
+
+        Request.Builder requestBuilder = new Request.Builder()
+                .url(BYTEBIN_ENDPOINT)
+                .header("Content-Encoding", "gzip")
+                .post(body);
+
+        Request request = requestBuilder.build();
+        try (Response response = HttpClient.makeCall(request)) {
+            try (ResponseBody responseBody = response.body()) {
+                if (responseBody == null) {
+                    throw new RuntimeException("No response");
+                }
+
+                try (InputStream inputStream = responseBody.byteStream()) {
+                    try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
+                        JsonObject object = new Gson().fromJson(reader, JsonObject.class);
+                        return object.get("key").getAsString();
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/GDEventManager.java b/sponge/src/main/java/com/griefdefender/GDEventManager.java
new file mode 100644
index 0000000..5bd326c
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/GDEventManager.java
@@ -0,0 +1,67 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender;
+
+import com.google.inject.Singleton;
+import com.griefdefender.api.event.Event;
+import com.griefdefender.api.event.EventManager;
+import net.kyori.event.EventBus;
+import net.kyori.event.SimpleEventBus;
+import net.kyori.event.method.MethodSubscriptionAdapter;
+import net.kyori.event.method.SimpleMethodSubscriptionAdapter;
+import net.kyori.event.method.asm.ASMEventExecutorFactory;
+
+@Singleton
+public class GDEventManager implements EventManager {
+
+    private final EventBus<Event> bus;
+    private final MethodSubscriptionAdapter<Object> adapter;
+
+    public GDEventManager() {
+        this.bus = new SimpleEventBus<Event>(Event.class);
+        this.adapter = new SimpleMethodSubscriptionAdapter<>(bus, new ASMEventExecutorFactory<>());
+    }
+
+    @Override
+    public EventBus<Event> getBus() {
+        return this.bus;
+    }
+
+    @Override
+    public void post(Event event) {
+        this.bus.post(event);
+    }
+
+    @Override
+    public void register(Object listener) {
+        this.adapter.register(listener);
+    }
+
+    @Override
+    public void unregister(Object listener) {
+        this.adapter.unregister(listener);
+    }
+
+}
diff --git a/sponge/src/main/java/com/griefdefender/GDPlayerData.java b/sponge/src/main/java/com/griefdefender/GDPlayerData.java
new file mode 100644
index 0000000..d060048
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/GDPlayerData.java
@@ -0,0 +1,701 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.reflect.TypeToken;
+import com.griefdefender.api.Tristate;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.ClaimType;
+import com.griefdefender.api.claim.ShovelType;
+import com.griefdefender.api.claim.ShovelTypes;
+import com.griefdefender.api.data.PlayerData;
+import com.griefdefender.api.permission.Context;
+import com.griefdefender.api.permission.flag.Flag;
+import com.griefdefender.api.permission.option.Options;
+import com.griefdefender.api.permission.option.type.CreateModeType;
+import com.griefdefender.api.permission.option.type.CreateModeTypes;
+import com.griefdefender.cache.EventResultCache;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.GriefDefenderConfig;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.configuration.PlayerStorageData;
+import com.griefdefender.permission.GDPermissionManager;
+import com.griefdefender.permission.GDPermissionUser;
+import com.griefdefender.permission.GDPermissions;
+import com.griefdefender.util.PermissionUtil;
+import net.kyori.text.Component;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.block.BlockSnapshot;
+import org.spongepowered.api.data.Transaction;
+import org.spongepowered.api.data.key.Keys;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.entity.living.player.User;
+import org.spongepowered.api.scheduler.Task;
+import org.spongepowered.api.service.economy.Currency;
+import org.spongepowered.api.service.economy.account.Account;
+import org.spongepowered.api.world.Location;
+import org.spongepowered.api.world.World;
+
+import java.lang.ref.WeakReference;
+import java.math.BigDecimal;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+
+public class GDPlayerData implements PlayerData {
+
+    public UUID playerID;
+    public UUID worldUniqueId;
+    private WeakReference<GDPermissionUser> playerSubject;
+    private Set<Claim> claimList;
+    private PlayerStorageData playerStorage;
+    public Location<World> lastAfkCheckLocation;
+    public Location<World> lastShovelLocation;
+    public Location<World> endShovelLocation;
+    public Location<World> lastValidInspectLocation;
+    public boolean claimMode = false;
+    public ShovelType shovelMode = ShovelTypes.BASIC;
+
+    public GDClaim claimResizing;
+    public GDClaim claimSubdividing;
+
+    public List<Transaction<BlockSnapshot>> visualBlocks = new ArrayList<>();
+    public UUID visualClaimId;
+    public UUID petRecipientUniqueId;
+    public Task visualRevertTask;
+
+    public boolean ignoreClaims = false;
+
+    public boolean debugClaimPermissions = false;
+    public WeakReference<GDClaim> lastClaim = new WeakReference<>(null);
+
+    public boolean inTown = false;
+    public boolean townChat = false;
+
+    // Always ignore active contexts by default
+    // This prevents protection issues when other plugins call getActiveContext
+    public boolean ignoreActiveContexts = true;
+
+    public EventResultCache eventResultCache;
+
+    // collide event cache
+    public int lastCollideEntityId = 0;
+    public boolean lastCollideEntityResult = false;
+
+    private String playerName;
+
+    public boolean allowFlight = false;
+    public boolean ignoreFallDamage = false;
+
+    // teleport data
+    public int teleportDelay = 0;
+    public Location<World> teleportSourceLocation;
+    public Location<World> teleportLocation;
+
+    public Instant lastPvpTimestamp;
+
+    // cached global option values
+    public int minClaimLevel;
+    private CreateModeType optionClaimCreateMode;
+    private Integer optionMaxAccruedBlocks;
+
+    // cached permission values
+    public boolean canManageAdminClaims = false;
+    public boolean canManageWilderness = false;
+    public boolean canManageGlobalOptions = false;
+    public boolean canManageAdminOptions = false;
+    public boolean canManageOverrideOptions = false;
+    public boolean canManageFlagDefaults = false;
+    public boolean canManageFlagOverrides = false;
+    public boolean bypassBorderCheck = false;
+    public boolean ignoreAdminClaims = false;
+    public boolean ignoreBasicClaims = false;
+    public boolean ignoreTowns = false;
+    public boolean ignoreWilderness = false;
+
+    public boolean dataInitialized = false;
+    public boolean showVisualFillers = true;
+    public boolean useRestoreSchematic = false;
+    private boolean checkedDimensionHeight = false;
+
+    public GDPlayerData(UUID worldUniqueId, UUID playerUniqueId, PlayerStorageData playerStorage, GriefDefenderConfig<?> activeConfig, Set<Claim> claims) {
+        this.worldUniqueId = worldUniqueId;
+        this.playerID = playerUniqueId;
+        this.playerStorage = playerStorage;
+        this.claimList = claims;
+        this.refreshPlayerOptions();
+    }
+
+    public void refreshPlayerOptions() {
+        final GriefDefenderConfig<?> activeConfig = GriefDefenderPlugin.getActiveConfig(this.worldUniqueId);
+        GriefDefenderPlugin.getInstance().executor.execute(() -> {
+            if (this.playerSubject == null || this.playerSubject.get() == null) {
+                GDPermissionUser subject = PermissionHolderCache.getInstance().getOrCreateUser(this.playerID);
+                this.playerSubject = new WeakReference<>(subject);
+            }
+            final GDPermissionUser subject = this.playerSubject.get();
+            final Set<Context> activeContexts = new HashSet<>();
+            PermissionUtil.getInstance().addActiveContexts(activeContexts, subject);
+            // permissions
+            this.bypassBorderCheck = PermissionUtil.getInstance().getPermissionValue(subject, GDPermissions.BYPASS_BORDER_CHECK, activeContexts).asBoolean();
+            this.ignoreAdminClaims = PermissionUtil.getInstance().getPermissionValue(subject, GDPermissions.IGNORE_CLAIMS_ADMIN, activeContexts).asBoolean();
+            this.ignoreTowns = PermissionUtil.getInstance().getPermissionValue(subject, GDPermissions.IGNORE_CLAIMS_TOWN, activeContexts).asBoolean();
+            this.ignoreWilderness = PermissionUtil.getInstance().getPermissionValue(subject, GDPermissions.IGNORE_CLAIMS_WILDERNESS, activeContexts).asBoolean();
+            this.ignoreBasicClaims = PermissionUtil.getInstance().getPermissionValue(subject, GDPermissions.IGNORE_CLAIMS_BASIC, activeContexts).asBoolean();
+            this.canManageAdminClaims = PermissionUtil.getInstance().getPermissionValue(subject, GDPermissions.COMMAND_ADMIN_CLAIMS, activeContexts).asBoolean();
+            this.canManageWilderness = PermissionUtil.getInstance().getPermissionValue(subject, GDPermissions.MANAGE_WILDERNESS, activeContexts).asBoolean();
+            this.canManageOverrideOptions = PermissionUtil.getInstance().getPermissionValue(subject, GDPermissions.MANAGE_OVERRIDE_OPTIONS, activeContexts).asBoolean();
+            this.canManageGlobalOptions = PermissionUtil.getInstance().getPermissionValue(subject, GDPermissions.MANAGE_GLOBAL_OPTIONS, activeContexts).asBoolean();
+            this.canManageAdminOptions = PermissionUtil.getInstance().getPermissionValue(subject, GDPermissions.MANAGE_ADMIN_OPTIONS, activeContexts).asBoolean();
+            this.canManageFlagDefaults = PermissionUtil.getInstance().getPermissionValue(subject, GDPermissions.MANAGE_FLAG_DEFAULTS, activeContexts).asBoolean();
+            this.canManageFlagOverrides = PermissionUtil.getInstance().getPermissionValue(subject, GDPermissions.MANAGE_FLAG_OVERRIDES, activeContexts).asBoolean();
+            this.playerID = subject.getUniqueId();
+            /*if (this.optionMaxClaimLevel > 255 || this.optionMaxClaimLevel <= 0 || this.optionMaxClaimLevel < this.optionMinClaimLevel) {
+                this.optionMaxClaimLevel = 255;
+            }
+            if (this.optionMinClaimLevel < 0 || this.optionMinClaimLevel >= 255 || this.optionMinClaimLevel > this.optionMaxClaimLevel) {
+                this.optionMinClaimLevel = 0;
+            }*/
+            this.dataInitialized = true;
+            this.checkedDimensionHeight = false;
+        });
+    }
+
+    public String getPlayerName() {
+        if (this.playerName == null) {
+            GDPermissionUser user = this.playerSubject.get();
+            if (user == null) {
+                user = PermissionHolderCache.getInstance().getOrCreateUser(this.playerID);
+            }
+            if (user != null) {
+                this.playerName = user.getFriendlyName();
+            }
+            if (this.playerName == null) {
+                this.playerName = "[unknown]";
+            }
+        }
+
+        return this.playerName;
+    }
+
+    public void revertActiveVisual(Player player) {
+        if (this.visualRevertTask != null) {
+            this.visualRevertTask.cancel();
+            this.visualRevertTask = null;
+        }
+
+        this.lastShovelLocation = null;
+        GDClaim claim = null;
+        if (this.visualClaimId != null) {
+            claim = (GDClaim) GriefDefenderPlugin.getInstance().dataStore.getClaim(this.worldUniqueId, this.visualClaimId);
+            if (claim != null) {
+                claim.playersWatching.remove(this.playerID);
+            }
+        }
+        this.visualClaimId = null;
+        if (this.visualBlocks.isEmpty()|| !player.getWorld().equals(this.visualBlocks.get(0).getFinal().getLocation().get().getExtent())) {
+            return;
+        }
+
+        for (int i = 0; i < this.visualBlocks.size(); i++) {
+            BlockSnapshot snapshot = this.visualBlocks.get(i).getOriginal();
+            // If original block does not exist, do not send to player
+            if (snapshot.getState().getType() != snapshot.getLocation().get().getBlockType()) {
+                if (claim != null) {
+                    claim.markVisualDirty = true;
+                }
+                continue;
+            }
+            player.sendBlockChange(snapshot.getPosition(), snapshot.getState());
+        }
+        this.visualBlocks.clear();
+    }
+
+    @Override
+    public int getBlocksAccruedPerHour() {
+        final Integer value = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), this.getSubject(), Options.BLOCKS_ACCRUED_PER_HOUR);
+        if (value == null) {
+            return Options.BLOCKS_ACCRUED_PER_HOUR.getDefaultValue();
+        }
+        return value;
+    }
+
+    @Override
+    public int getChestClaimExpiration() {
+        final Integer value = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), this.getSubject(), Options.CHEST_EXPIRATION);
+        if (value == null) {
+            return Options.CHEST_EXPIRATION.getDefaultValue();
+        }
+        return value;
+    }
+
+    @Override
+    public int getCreateClaimLimit(ClaimType type) {
+        final Integer value = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), this.getSubject(), Options.CREATE_LIMIT, type);
+        if (value == null) {
+            return Options.CREATE_LIMIT.getDefaultValue();
+        }
+        return value;
+    }
+
+    public CreateModeType getCreateMode() {
+        final CreateModeType value = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(CreateModeType.class), this.getSubject(), Options.CREATE_MODE);
+        if (value == null || value == CreateModeTypes.UNDEFINED) {
+            return CreateModeTypes.AREA;
+        }
+        return value;
+    }
+
+    @Override
+    public int getInitialClaimBlocks() {
+        final Integer value = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), this.getSubject(), Options.INITIAL_BLOCKS);
+        if (value == null) {
+            return Options.INITIAL_BLOCKS.getDefaultValue();
+        }
+        return value;
+    }
+
+    public double getInternalEconomyBlockCost() {
+        final Double value = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Double.class), this.getSubject(), Options.ECONOMY_BLOCK_COST);
+        if (value == null) {
+            return Options.ECONOMY_BLOCK_COST.getDefaultValue();
+        }
+        return value;
+    }
+
+    public int getInternalRemainingClaimBlocks() {
+        final int initialClaimBlocks = this.getInitialClaimBlocks();
+        int remainingBlocks = initialClaimBlocks + this.getAccruedClaimBlocks() + this.getBonusClaimBlocks();
+
+        for (Claim claim : this.claimList) {
+            if (claim.isSubdivision()) {
+                continue;
+            }
+
+            GDClaim gpClaim = (GDClaim) claim;
+            if ((gpClaim.parent == null || gpClaim.parent.isAdminClaim()) && claim.getData().requiresClaimBlocks()) {
+                remainingBlocks -= claim.getClaimBlocks();
+            }
+        }
+
+        return remainingBlocks;
+    }
+
+    @Override
+    public int getRemainingClaimBlocks() {
+        final int initialClaimBlocks = this.getInitialClaimBlocks();
+        int remainingBlocks = initialClaimBlocks + this.getAccruedClaimBlocks() + this.getBonusClaimBlocks();
+
+        if (GriefDefenderPlugin.getInstance().isEconomyModeEnabled()) {
+            return this.getInternalEconomyAvailablePurchaseCost();
+        } else {
+            for (Claim claim : this.claimList) {
+                if (claim.isSubdivision()) {
+                    continue;
+                }
+    
+                GDClaim gpClaim = (GDClaim) claim;
+                if ((gpClaim.parent == null || gpClaim.parent.isAdminClaim()) && claim.getData().requiresClaimBlocks()) {
+                    remainingBlocks -= claim.getClaimBlocks();
+                }
+            }
+        }
+
+        return remainingBlocks;
+    }
+
+    public int getInternalEconomyAvailablePurchaseCost() {
+        if (GriefDefenderPlugin.getInstance().isEconomyModeEnabled()) {
+            final Account playerAccount = GriefDefenderPlugin.getInstance().economyService.get().getOrCreateAccount(this.playerID).orElse(null);
+            if (playerAccount == null) {
+                return 0;
+            }
+
+            final Currency defaultCurrency = GriefDefenderPlugin.getInstance().economyService.get().getDefaultCurrency();
+            final BigDecimal currentFunds = playerAccount.getBalance(defaultCurrency);
+            final Double economyBlockCost = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Double.class), this.getSubject(), Options.ECONOMY_BLOCK_COST);
+            return (int) Math.round((currentFunds.doubleValue() / economyBlockCost));
+        }
+        return 0;
+    }
+
+    public int getTotalClaimsCost() {
+        int totalCost = 0;
+        for (Claim claim : this.claimList) {
+            if (claim.isSubdivision()) {
+                continue;
+            }
+
+            final GDClaim gpClaim = (GDClaim) claim;
+            if ((gpClaim.parent == null || gpClaim.parent.isAdminClaim()) && claim.getData().requiresClaimBlocks()) {
+                totalCost += claim.getClaimBlocks();
+            }
+        }
+
+        return totalCost;
+    }
+
+    public double getRemainingChunks() {
+        final double remainingChunks = this.getRemainingClaimBlocks() / 65536.0;
+        return Math.round(remainingChunks * 100.0)/100.0;
+    }
+
+    @Override
+    public int getAccruedClaimBlocks() {
+        return this.playerStorage.getConfig().getAccruedClaimBlocks();
+    }
+
+    public boolean addAccruedClaimBlocks(int newAccruedClaimBlocks) {
+        int currentTotal = this.getAccruedClaimBlocks();
+        if ((currentTotal + newAccruedClaimBlocks) > this.getMaxAccruedClaimBlocks()) {
+            return false;
+        }
+
+        this.playerStorage.getConfig().setAccruedClaimBlocks(currentTotal + newAccruedClaimBlocks);
+        return true;
+    }
+
+    public boolean setAccruedClaimBlocks(int newAccruedClaimBlocks) {
+        if (newAccruedClaimBlocks > this.getMaxAccruedClaimBlocks()) {
+            return false;
+        }
+
+        this.playerStorage.getConfig().setAccruedClaimBlocks(newAccruedClaimBlocks);
+        return true;
+    }
+
+    public int getBonusClaimBlocks() {
+        return this.playerStorage.getConfig().getBonusClaimBlocks();
+    }
+
+    public void setBonusClaimBlocks(int bonusClaimBlocks) {
+        this.playerStorage.getConfig().setBonusClaimBlocks(bonusClaimBlocks);
+    }
+
+    public CreateModeType getClaimCreateMode() {
+        if (this.optionClaimCreateMode == null) {
+            CreateModeType mode = this.getCreateMode();
+            // default to 0 if invalid
+            if (mode == null) {
+                mode = CreateModeTypes.AREA;
+            }
+            this.optionClaimCreateMode = mode;
+        }
+
+        return this.optionClaimCreateMode;
+    }
+
+    public void setClaimCreateMode(CreateModeType mode) {
+        this.optionClaimCreateMode = mode;
+    }
+
+    public boolean canCreateClaim(Player player) {
+        return canCreateClaim(player, false);
+    }
+
+    public boolean canCreateClaim(Player player, boolean sendMessage) {
+        final CreateModeType createMode = this.getClaimCreateMode();
+        if (this.shovelMode == ShovelTypes.BASIC) {
+            if (createMode == CreateModeTypes.AREA && !player.hasPermission(GDPermissions.CLAIM_CREATE_BASIC)) {
+                if (sendMessage) {
+                    GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().PERMISSION_CLAIM_CREATE);
+                }
+                return false;
+            }
+            if (createMode == CreateModeTypes.VOLUME && !player.hasPermission(GDPermissions.CLAIM_CUBOID_BASIC)) {
+                if (sendMessage) {
+                    GriefDefenderPlugin.sendMessage(player,MessageCache.getInstance().PERMISSION_CUBOID);
+                    GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().COMMAND_CUBOID_DISABLED);
+                }
+                return false;
+            }
+        } else if (this.shovelMode == ShovelTypes.SUBDIVISION) {
+            if (createMode == CreateModeTypes.AREA && !player.hasPermission(GDPermissions.CLAIM_CREATE_SUBDIVISION)) {
+                if (sendMessage) {
+                    GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().PERMISSION_CLAIM_CREATE);
+                }
+                return false;
+            } else if (!player.hasPermission(GDPermissions.CLAIM_CUBOID_SUBDIVISION)) {
+                if (sendMessage) {
+                    GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().PERMISSION_CUBOID);
+                    GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().COMMAND_CUBOID_DISABLED);
+                }
+                return false;
+            }
+        } else if (this.shovelMode == ShovelTypes.ADMIN) {
+            if (createMode == CreateModeTypes.AREA && !player.hasPermission(GDPermissions.COMMAND_ADMIN_CLAIMS)) {
+                return false;
+            } else if (!player.hasPermission(GDPermissions.CLAIM_CUBOID_ADMIN)) {
+                return false;
+            }
+        } else if (this.shovelMode == ShovelTypes.TOWN) {
+            if (createMode == CreateModeTypes.AREA && !player.hasPermission(GDPermissions.CLAIM_CREATE_TOWN)) {
+                return false;
+            } else if (!player.hasPermission(GDPermissions.CLAIM_CUBOID_TOWN)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    public void saveAllData() {
+        this.playerStorage.save();
+    }
+
+    public PlayerStorageData getStorageData() {
+        return this.playerStorage;
+    }
+
+    public Set<Claim> getClaims() {
+        return ImmutableSet.copyOf(this.claimList);
+    }
+
+    public Set<Claim> getInternalClaims() {
+        return this.claimList;
+    }
+
+    public int getClaimTypeCount(ClaimType type) {
+        int count = 0;
+        for (Claim claim : this.claimList) {
+            if (claim.getType() == type) {
+                count++;
+            }
+        }
+        return count;
+    }
+
+    public void setLastCollideEntityData(int entityId, boolean result) {
+        this.lastCollideEntityId = entityId;
+        this.lastCollideEntityResult = result;
+    }
+
+    public void setIgnoreClaims(boolean flag) {
+        this.ignoreClaims = flag;
+    }
+
+    @Override
+    public boolean canIgnoreClaim(Claim claim) {
+        if (claim == null || this.ignoreClaims == false) {
+            return false;
+        }
+
+        if (claim.isAdminClaim()) {
+            return this.ignoreAdminClaims;
+        } else if (claim.isWilderness()) {
+            return this.ignoreWilderness;
+        } else if (claim.isTown()) {
+            return this.ignoreTowns;
+        }
+        return this.ignoreBasicClaims;
+    }
+
+    public boolean canManageOption(Player player, GDClaim claim, boolean isGroup) {
+        if (claim.allowEdit(player) != null) {
+            return false;
+        }
+
+        if (claim.isWilderness()) {
+            return player.hasPermission(GDPermissions.MANAGE_WILDERNESS);
+        }
+        if (isGroup) {
+            if (claim.isTown() && player.hasPermission(GDPermissions.COMMAND_CLAIM_OPTIONS_GROUP_TOWN)) {
+                return true;
+            }
+            if (claim.isAdminClaim() && player.hasPermission(GDPermissions.COMMAND_CLAIM_OPTIONS_GROUP_ADMIN)) {
+                return true;
+            }
+            if (claim.isBasicClaim() && player.hasPermission(GDPermissions.COMMAND_CLAIM_OPTIONS_GROUP_BASIC)) {
+                return true;
+            }
+            if (claim.isSubdivision() && player.hasPermission(GDPermissions.COMMAND_CLAIM_OPTIONS_GROUP_SUBDIVISION)) {
+                return true;
+            }
+        } else {
+            if (claim.isTown() && player.hasPermission(GDPermissions.COMMAND_CLAIM_OPTIONS_PLAYER_TOWN)) {
+                return true;
+            }
+            if (claim.isAdminClaim() && player.hasPermission(GDPermissions.COMMAND_CLAIM_OPTIONS_PLAYER_ADMIN)) {
+                return true;
+            }
+            if (claim.isBasicClaim() && player.hasPermission(GDPermissions.COMMAND_CLAIM_OPTIONS_PLAYER_BASIC)) {
+                return true;
+            }
+            if (claim.isSubdivision() && player.hasPermission(GDPermissions.COMMAND_CLAIM_OPTIONS_PLAYER_SUBDIVISION)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    @Override
+    public int getMaxAccruedClaimBlocks() {
+        return GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), this.getSubject(), Options.MAX_ACCRUED_BLOCKS);
+    }
+
+    @Override
+    public double getAbandonedReturnRatio(ClaimType type) {
+        return GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Double.class), this.getSubject(), Options.ABANDON_RETURN_RATIO);
+    }
+
+    @Override
+    public int getMaxClaimX(ClaimType type) {
+        return GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), this.getSubject(), Options.MAX_SIZE_X, type);
+    }
+
+    @Override
+    public int getMaxClaimY(ClaimType type) {
+        return GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), this.getSubject(), Options.MAX_SIZE_Y, type);
+    }
+
+    @Override
+    public int getMaxClaimZ(ClaimType type) {
+        return GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), this.getSubject(), Options.MAX_SIZE_Z, type);
+    }
+
+    @Override
+    public int getMinClaimX(ClaimType type) {
+        return GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), this.getSubject(), Options.MIN_SIZE_X, type);
+    }
+
+    @Override
+    public int getMinClaimY(ClaimType type) {
+        return GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), this.getSubject(), Options.MIN_SIZE_Y, type);
+    }
+
+    @Override
+    public int getMinClaimZ(ClaimType type) {
+        return GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), this.getSubject(), Options.MIN_SIZE_Z, type);
+    }
+
+    @Override
+    public int getMaxClaimLevel() {
+        int maxClaimLevel = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), this.getSubject(), Options.MAX_LEVEL);
+        if (!this.checkedDimensionHeight) {
+            final World world = Sponge.getServer().getWorld(this.worldUniqueId).orElse(null);
+            if (world != null) {
+                final int buildHeight = world.getDimension().getBuildHeight() - 1;
+                if (buildHeight < maxClaimLevel) {
+                    maxClaimLevel = buildHeight;
+                }
+            }
+            this.checkedDimensionHeight = true;
+        }
+        return maxClaimLevel;
+    }
+
+    @Override
+    public int getMinClaimLevel() {
+        return GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), this.getSubject(), Options.MIN_LEVEL);
+    }
+
+    @Override
+    public double getEconomyClaimBlockCost() {
+        return GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Double.class), this.getSubject(), Options.ECONOMY_BLOCK_COST);
+    }
+
+    @Override
+    public double getEconomyClaimBlockReturn() {
+        return GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Double.class), this.getSubject(), Options.ECONOMY_BLOCK_SELL_RETURN);
+    }
+
+    @Override
+    public double getTaxRate(ClaimType type) {
+        return GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Double.class), this.getSubject(), Options.TAX_RATE, type);
+    }
+
+    @Override
+    public String getSubjectId() {
+        return this.getSubject().getIdentifier();
+    }
+
+    public GDPermissionUser getSubject() {
+        this.playerSubject = null;
+        if (this.playerSubject == null || this.playerSubject.get() == null) {
+            GDPermissionUser user = PermissionHolderCache.getInstance().getOrCreateUser(this.playerID);
+            this.playerSubject = new WeakReference<>(user);
+        }
+
+        return this.playerSubject.get();
+    }
+
+    public void sendTaxExpireMessage(Player player, GDClaim claim) {
+        final double taxRate = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Double.class), player, Options.TAX_RATE, claim);
+        final double taxOwed = claim.getClaimBlocks() * taxRate;
+        final double remainingDays = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), player, Options.TAX_EXPIRATION_DAYS_KEEP, claim);
+        final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.TAX_CLAIM_EXPIRED, ImmutableMap.of(
+                "days", remainingDays,
+                "amount", taxOwed));
+        GriefDefenderPlugin.sendClaimDenyMessage(claim, player, message);
+    }
+
+    public double getTotalTax() {
+        double totalTax = 0;
+        final GDPermissionUser subject = this.getSubject();
+        for (Claim claim : this.getInternalClaims()) {
+            double playerTaxRate = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Double.class), subject, Options.TAX_RATE, claim);
+            totalTax += (claim.getClaimBlocks() / 256) * playerTaxRate;
+        }
+
+        return totalTax;
+    }
+
+    public boolean inPvpCombat(World world) {
+        if (this.lastPvpTimestamp == null) {
+            return false;
+        }
+
+        final Instant now = Instant.now();
+        final int combatTimeout = GriefDefenderPlugin.getActiveConfig(world.getProperties()).getConfig().pvp.combatTimeout;
+        if (this.lastPvpTimestamp.plusSeconds(combatTimeout).isBefore(now)) {
+            this.lastPvpTimestamp = null;
+            return false;
+        }
+
+        return true;
+    }
+
+    public void onDisconnect() {
+        this.visualBlocks.clear();
+        this.claimMode = false;
+        this.lastShovelLocation = null;
+        this.eventResultCache = null;
+        this.claimResizing = null;
+        this.claimSubdividing = null;
+        this.visualClaimId = null;
+        if (this.visualRevertTask != null) {
+            this.visualRevertTask.cancel();
+            this.visualRevertTask = null;
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/GDRelocator.java b/sponge/src/main/java/com/griefdefender/GDRelocator.java
new file mode 100644
index 0000000..5807f0f
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/GDRelocator.java
@@ -0,0 +1,78 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender;
+
+import com.griefdefender.util.BootstrapUtil;
+import me.lucko.jarrelocator.JarRelocator;
+import me.lucko.jarrelocator.Relocation;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class GDRelocator {
+
+    private static GDRelocator instance;
+    private List<Relocation> rules;
+
+    public static GDRelocator getInstance() {
+        if (instance == null) {
+            instance = new GDRelocator();
+        }
+        return instance;
+    }
+
+    public GDRelocator() {
+        this.rules = new ArrayList<>();
+        for (String name : GDBootstrap.getInstance().getRelocateList()) {
+            final String[] parts = name.split(":");
+            final String key = parts[0];
+            final String relocated = parts[1];
+            this.rules.add(new Relocation(key, "com.griefdefender.lib." + relocated));
+        }
+    }
+
+    public void relocateJars(Map<String, File> jarMap) {
+        for (Map.Entry<String, File> mapEntry : jarMap.entrySet()) {
+            final String name = mapEntry.getKey();
+            final File input = mapEntry.getValue();
+            final File output = Paths.get(input.getParentFile().getPath()).resolve(input.getName().replace(".jar", "") + "-shaded.jar").toFile();
+            if (!output.exists()) {
+                // Relocate
+                JarRelocator relocator = new JarRelocator(input, output, this.rules);
+        
+                try {
+                    relocator.run();
+                } catch (IOException e) {
+                    throw new RuntimeException("Unable to relocate", e);
+                }
+            }
+            BootstrapUtil.addUrlToClassLoader(name, output);
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/GDTimings.java b/sponge/src/main/java/com/griefdefender/GDTimings.java
new file mode 100644
index 0000000..519923a
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/GDTimings.java
@@ -0,0 +1,81 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender;
+
+import co.aikar.timings.Timing;
+import co.aikar.timings.Timings;
+
+public class GDTimings {
+
+    public static final Timing BLOCK_BREAK_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onBlockBreak");
+    public static final Timing BLOCK_COLLIDE_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onBlockCollide");
+    public static final Timing BLOCK_NOTIFY_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onBlockNotify");
+    public static final Timing BLOCK_PLACE_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onBlockPlace");
+    public static final Timing BLOCK_POST_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onBlockPost");
+    public static final Timing BLOCK_PRE_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onBlockPre");
+    public static final Timing ENTITY_EXPLOSION_PRE_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onEntityExplosionPre");
+    public static final Timing ENTITY_EXPLOSION_DETONATE_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onEntityExplosionDetonate");
+    public static final Timing ENTITY_ATTACK_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onEntityAttack");
+    public static final Timing ENTITY_COLLIDE_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onEntityCollide");
+    public static final Timing ENTITY_DAMAGE_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onEntityDamage");
+    public static final Timing ENTITY_DAMAGE_MONITOR_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onEntityDamageMonitor");
+    public static final Timing ENTITY_DEATH_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onEntityDeath");
+    public static final Timing ENTITY_DROP_ITEM_DEATH_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onEntityDropDeathItem");
+    public static final Timing ENTITY_MOUNT_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onEntityMount");
+    public static final Timing ENTITY_MOVE_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onEntityMove");
+    public static final Timing ENTITY_SPAWN_PRE_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onEntitySpawnPre");
+    public static final Timing ENTITY_SPAWN_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onEntitySpawn");
+    public static final Timing ENTITY_TELEPORT_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onEntityTeleport");
+    public static final Timing PLAYER_CHANGE_HELD_ITEM_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onPlayerChangeHeldItem");
+    public static final Timing PLAYER_CHAT_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onPlayerChat");
+    public static final Timing PLAYER_COMMAND_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onPlayerCommand");
+    public static final Timing PLAYER_DEATH_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onPlayerDeath");
+    public static final Timing PLAYER_DISPENSE_ITEM_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onPlayerDispenseItem");
+    public static final Timing PLAYER_LOGIN_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onPlayerLogin");
+    public static final Timing PLAYER_HANDLE_SHOVEL_ACTION = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onPlayerHandleShovelAction");
+    public static final Timing PLAYER_INTERACT_BLOCK_PRIMARY_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onPlayerInteractBlockPrimary");
+    public static final Timing PLAYER_INTERACT_BLOCK_SECONDARY_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onPlayerInteractBlockSecondary");
+    public static final Timing PLAYER_INTERACT_ENTITY_PRIMARY_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onPlayerInteractEntityPrimary");
+    public static final Timing PLAYER_INTERACT_ENTITY_SECONDARY_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onPlayerInteractEntitySecondary");
+    public static final Timing PLAYER_INTERACT_INVENTORY_CLICK_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onPlayerInteractInventoryClick");
+    public static final Timing PLAYER_INTERACT_INVENTORY_CLOSE_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onPlayerInteractInventoryClose");
+    public static final Timing PLAYER_INTERACT_INVENTORY_OPEN_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onPlayerInteractInventoryOpen");
+    public static final Timing PLAYER_INVESTIGATE_CLAIM = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onPlayerInvestigateClaim");
+    public static final Timing PLAYER_JOIN_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onPlayerJoin");
+    public static final Timing PLAYER_KICK_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onPlayerKick");
+    public static final Timing PLAYER_PICKUP_ITEM_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onPlayerPickupItem");
+    public static final Timing PLAYER_QUIT_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onPlayerQuit");
+    public static final Timing PLAYER_RESPAWN_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onPlayerRespawn");
+    public static final Timing PLAYER_USE_ITEM_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onPlayerUseItem");
+    public static final Timing SIGN_CHANGE_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onSignChange");
+    public static final Timing PROJECTILE_IMPACT_BLOCK_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onProjectileImpactBlock");
+    public static final Timing PROJECTILE_IMPACT_ENTITY_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onProjectileImpactEntity");
+    public static final Timing EXPLOSION_PRE_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onExplosionPre");
+    public static final Timing EXPLOSION_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onExplosionDetonate");
+    public static final Timing CLAIM_GETCLAIM = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "getClaimAt");
+    public static final Timing WORLD_LOAD_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onWorldSave");
+    public static final Timing WORLD_SAVE_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onWorldSave");
+    public static final Timing WORLD_UNLOAD_EVENT = Timings.of(GriefDefenderPlugin.getInstance().pluginContainer, "onWorldSave");
+}
diff --git a/sponge/src/main/java/com/griefdefender/GDVersion.java b/sponge/src/main/java/com/griefdefender/GDVersion.java
new file mode 100644
index 0000000..14dc426
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/GDVersion.java
@@ -0,0 +1,44 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender;
+
+import com.google.inject.Singleton;
+import com.griefdefender.api.Version;
+
+@Singleton
+public class GDVersion implements Version {
+
+    public static final double API_VERSION = 0.91;
+
+    @Override
+    public double getApiVersion() {
+        return API_VERSION;
+    }
+
+    @Override
+    public String getImplementationVersion() {
+        return GriefDefenderPlugin.IMPLEMENTATION_VERSION;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/GriefDefenderPlugin.java b/sponge/src/main/java/com/griefdefender/GriefDefenderPlugin.java
new file mode 100644
index 0000000..e805226
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/GriefDefenderPlugin.java
@@ -0,0 +1,1166 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.RegisteredCommand;
+import co.aikar.commands.RootCommand;
+import co.aikar.commands.SpongeCommandManager;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Maps;
+import com.google.common.reflect.TypeToken;
+import com.google.inject.Guice;
+import com.google.inject.Stage;
+import com.griefdefender.api.GriefDefender;
+import com.griefdefender.api.Tristate;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.ClaimBlockSystem;
+import com.griefdefender.api.claim.ClaimType;
+import com.griefdefender.api.claim.TrustType;
+import com.griefdefender.api.economy.BankTransaction;
+import com.griefdefender.api.permission.flag.Flag;
+import com.griefdefender.api.permission.option.Option;
+import com.griefdefender.api.permission.option.type.CreateModeType;
+import com.griefdefender.api.permission.option.type.GameModeType;
+import com.griefdefender.api.permission.option.type.WeatherType;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.claim.ClaimContextCalculator;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.claim.GDClaimManager;
+import com.griefdefender.command.CommandAdjustBonusClaimBlocks;
+import com.griefdefender.command.CommandCallback;
+import com.griefdefender.command.CommandClaimAbandon;
+import com.griefdefender.command.CommandClaimAbandonAll;
+import com.griefdefender.command.CommandClaimAbandonTop;
+import com.griefdefender.command.CommandClaimAdmin;
+import com.griefdefender.command.CommandClaimBan;
+import com.griefdefender.command.CommandClaimBank;
+import com.griefdefender.command.CommandClaimBasic;
+import com.griefdefender.command.CommandClaimBuy;
+import com.griefdefender.command.CommandClaimBuyBlocks;
+import com.griefdefender.command.CommandClaimClear;
+import com.griefdefender.command.CommandClaimContract;
+import com.griefdefender.command.CommandClaimCreate;
+import com.griefdefender.command.CommandClaimCuboid;
+import com.griefdefender.command.CommandClaimDelete;
+import com.griefdefender.command.CommandClaimDeleteAll;
+import com.griefdefender.command.CommandClaimDeleteAllAdmin;
+import com.griefdefender.command.CommandClaimDeleteTop;
+import com.griefdefender.command.CommandClaimExpand;
+import com.griefdefender.command.CommandClaimFarewell;
+import com.griefdefender.command.CommandClaimFlag;
+import com.griefdefender.command.CommandClaimFlagDebug;
+import com.griefdefender.command.CommandClaimFlagGroup;
+import com.griefdefender.command.CommandClaimFlagPlayer;
+import com.griefdefender.command.CommandClaimFlagReset;
+import com.griefdefender.command.CommandClaimGreeting;
+import com.griefdefender.command.CommandClaimIgnore;
+import com.griefdefender.command.CommandClaimInfo;
+import com.griefdefender.command.CommandClaimInherit;
+import com.griefdefender.command.CommandClaimList;
+import com.griefdefender.command.CommandClaimMode;
+import com.griefdefender.command.CommandClaimName;
+import com.griefdefender.command.CommandClaimOption;
+import com.griefdefender.command.CommandClaimOptionGroup;
+import com.griefdefender.command.CommandClaimOptionPlayer;
+import com.griefdefender.command.CommandClaimPermissionGroup;
+import com.griefdefender.command.CommandClaimPermissionPlayer;
+import com.griefdefender.command.CommandClaimSchematic;
+import com.griefdefender.command.CommandClaimSell;
+import com.griefdefender.command.CommandClaimSellBlocks;
+import com.griefdefender.command.CommandClaimSetSpawn;
+import com.griefdefender.command.CommandClaimSpawn;
+import com.griefdefender.command.CommandClaimSubdivision;
+import com.griefdefender.command.CommandClaimTown;
+import com.griefdefender.command.CommandClaimTransfer;
+import com.griefdefender.command.CommandClaimUnban;
+import com.griefdefender.command.CommandClaimWorldEdit;
+import com.griefdefender.command.CommandDebug;
+import com.griefdefender.command.CommandGDReload;
+import com.griefdefender.command.CommandGDVersion;
+import com.griefdefender.command.CommandGiveBlocks;
+import com.griefdefender.command.CommandGivePet;
+import com.griefdefender.command.CommandHelp;
+import com.griefdefender.command.CommandPagination;
+import com.griefdefender.command.CommandPlayerInfo;
+import com.griefdefender.command.CommandRestoreClaim;
+import com.griefdefender.command.CommandRestoreNature;
+import com.griefdefender.command.CommandSetAccruedClaimBlocks;
+import com.griefdefender.command.CommandTownChat;
+import com.griefdefender.command.CommandTownTag;
+import com.griefdefender.command.CommandTrustGroup;
+import com.griefdefender.command.CommandTrustGroupAll;
+import com.griefdefender.command.CommandTrustList;
+import com.griefdefender.command.CommandTrustPlayer;
+import com.griefdefender.command.CommandTrustPlayerAll;
+import com.griefdefender.command.CommandUntrustGroup;
+import com.griefdefender.command.CommandUntrustGroupAll;
+import com.griefdefender.command.CommandUntrustPlayer;
+import com.griefdefender.command.CommandUntrustPlayerAll;
+import com.griefdefender.command.gphelper.CommandAccessTrust;
+import com.griefdefender.command.gphelper.CommandContainerTrust;
+import com.griefdefender.configuration.GriefDefenderConfig;
+import com.griefdefender.configuration.MessageDataConfig;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.configuration.category.BlacklistCategory;
+import com.griefdefender.configuration.serializer.ClaimTypeSerializer;
+import com.griefdefender.configuration.serializer.ComponentConfigSerializer;
+import com.griefdefender.configuration.serializer.CreateModeTypeSerializer;
+import com.griefdefender.configuration.serializer.CustomFlagSerializer;
+import com.griefdefender.configuration.serializer.GameModeTypeSerializer;
+import com.griefdefender.configuration.serializer.WeatherTypeSerializer;
+import com.griefdefender.configuration.type.ConfigBase;
+import com.griefdefender.configuration.type.GlobalConfig;
+import com.griefdefender.economy.GDBankTransaction;
+import com.griefdefender.inject.GriefDefenderImplModule;
+import com.griefdefender.internal.provider.WorldEditProvider;
+import com.griefdefender.internal.registry.BlockTypeRegistryModule;
+import com.griefdefender.internal.registry.EntityTypeRegistryModule;
+import com.griefdefender.internal.registry.GDBlockType;
+import com.griefdefender.internal.registry.GDEntityType;
+import com.griefdefender.internal.registry.GDItemType;
+import com.griefdefender.internal.registry.ItemTypeRegistryModule;
+import com.griefdefender.internal.util.NMSUtil;
+import com.griefdefender.listener.BlockEventHandler;
+import com.griefdefender.listener.EntityEventHandler;
+import com.griefdefender.listener.MCClansEventHandler;
+import com.griefdefender.listener.NucleusEventHandler;
+import com.griefdefender.listener.PlayerEventHandler;
+import com.griefdefender.listener.WorldEventHandler;
+import com.griefdefender.migrator.GPSpongeMigrator;
+import com.griefdefender.permission.ContextGroupKeys;
+import com.griefdefender.permission.GDPermissionHolder;
+import com.griefdefender.permission.GDPermissionManager;
+import com.griefdefender.permission.GDPermissionUser;
+import com.griefdefender.permission.GDPermissions;
+import com.griefdefender.permission.flag.GDCustomFlagDefinition;
+import com.griefdefender.permission.flag.GDFlags;
+import com.griefdefender.provider.LuckPermsProvider;
+import com.griefdefender.provider.MCClansProvider;
+import com.griefdefender.provider.NucleusProvider;
+import com.griefdefender.provider.PermissionProvider;
+import com.griefdefender.registry.ChatTypeRegistryModule;
+import com.griefdefender.registry.ClaimTypeRegistryModule;
+import com.griefdefender.registry.CreateModeTypeRegistryModule;
+import com.griefdefender.registry.FlagRegistryModule;
+import com.griefdefender.registry.GameModeTypeRegistryModule;
+import com.griefdefender.registry.OptionRegistryModule;
+import com.griefdefender.registry.ResultTypeRegistryModule;
+import com.griefdefender.registry.ShovelTypeRegistryModule;
+import com.griefdefender.registry.TrustTypeRegistryModule;
+import com.griefdefender.registry.WeatherTypeRegistryModule;
+import com.griefdefender.storage.BaseStorage;
+import com.griefdefender.storage.FileStorage;
+import com.griefdefender.task.ClaimBlockTask;
+import com.griefdefender.task.ClaimCleanupTask;
+import com.griefdefender.task.PlayerTickTask;
+import com.griefdefender.util.PermissionUtil;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import net.kyori.text.event.ClickEvent;
+import net.kyori.text.event.HoverEvent;
+import net.kyori.text.format.TextColor;
+import net.kyori.text.serializer.plain.PlainComponentSerializer;
+import ninja.leaping.configurate.objectmapping.serialize.TypeSerializers;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.lang3.LocaleUtils;
+//import org.bstats.sponge.Metrics2;
+import org.slf4j.Logger;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.command.CommandSource;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.entity.living.player.User;
+import org.spongepowered.api.event.Event;
+import org.spongepowered.api.event.Listener;
+import org.spongepowered.api.event.Order;
+import org.spongepowered.api.event.cause.EventContextKey;
+import org.spongepowered.api.event.game.GameReloadEvent;
+import org.spongepowered.api.event.game.state.GameAboutToStartServerEvent;
+import org.spongepowered.api.event.game.state.GamePreInitializationEvent;
+import org.spongepowered.api.event.game.state.GameStartedServerEvent;
+import org.spongepowered.api.event.service.ChangeServiceProviderEvent;
+import org.spongepowered.api.plugin.PluginContainer;
+import org.spongepowered.api.profile.GameProfile;
+import org.spongepowered.api.service.economy.EconomyService;
+import org.spongepowered.api.service.permission.PermissionService;
+import org.spongepowered.api.service.user.UserStorageService;
+import org.spongepowered.api.world.DimensionType;
+import org.spongepowered.api.world.Location;
+import org.spongepowered.api.world.World;
+import org.spongepowered.api.world.storage.WorldProperties;
+import org.spongepowered.common.SpongeImplHooks;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.TreeMap;
+import java.util.UUID;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+public class GriefDefenderPlugin {
+
+    private static GriefDefenderPlugin instance;
+    public static final String MOD_ID = "GriefDefender";
+    public static final EventContextKey<GriefDefenderPlugin> PLUGIN_CONTEXT = EventContextKey.builder(GriefDefenderPlugin.class)
+        .name(MOD_ID)
+        .id(MOD_ID)
+        .build();
+    public static final String API_VERSION = GriefDefenderPlugin.class.getPackage().getSpecificationVersion();
+    public static final String IMPLEMENTATION_NAME = GriefDefenderPlugin.class.getPackage().getImplementationTitle();
+    public static final String IMPLEMENTATION_VERSION =  GriefDefenderPlugin.class.getPackage().getImplementationVersion();
+    public static String SPONGE_VERSION = "unknown";
+    protected PluginContainer pluginContainer;
+    protected Logger logger;
+    //@Inject private Metrics2 metrics;
+    protected Path configPath;
+    public MessageStorage messageStorage;
+    public MessageDataConfig messageData;
+    //public Map<UUID, Random> worldGeneratorRandoms = new HashMap<>();
+    public static ClaimBlockSystem CLAIM_BLOCK_SYSTEM;
+
+    public static final String CONFIG_HEADER = IMPLEMENTATION_VERSION + "\n"
+            + "# If you need help with the configuration or have any issues related to GriefDefender,\n"
+            + "# create a ticket on https://github.com/bloodmc/GriefDefender/issues.\n"
+            + "# Note: If you have not purchased GriefDefender, please consider doing so to get \n"
+            + "# exclusive access to Discord for prompt support.\n";
+
+    // GP Public user info
+    public static final UUID PUBLIC_UUID = UUID.fromString("41C82C87-7AfB-4024-BA57-13D2C99CAE77");
+    public static final UUID WORLD_USER_UUID = UUID.fromString("00000000-0000-0000-0000-000000000000");
+    public static final UUID ADMIN_USER_UUID = UUID.fromString("11111111-1111-1111-1111-111111111111");
+    public static GDPermissionUser PUBLIC_USER;
+    public static GDPermissionUser WORLD_USER;
+    public static final String PUBLIC_NAME = "[GDPublic]";
+    public static final String WORLD_USER_NAME = "[GDWorld]";
+
+    public static GDPermissionHolder DEFAULT_HOLDER;
+    private SpongeCommandManager commandManager;
+    public BaseStorage dataStore;
+
+    public MCClansProvider clanApiProvider;
+    public NucleusProvider nucleusApiProvider;
+    public WorldEditProvider worldEditProvider;
+    private PermissionProvider permissionProvider;
+    public PermissionService permissionService;
+
+    public Optional<EconomyService> economyService;
+    public Executor executor;
+    public boolean permPluginInstalled = false;
+
+    public GDBlockType createVisualBlock;
+    public GDItemType modificationTool;
+    public GDItemType investigationTool;
+    public int maxInspectionDistance = 100;
+
+    public static boolean debugLogging = false;
+    public static boolean debugActive = false;
+    private Map<String, GDDebugData> debugUserMap = new HashMap<>();
+    public static final Component GD_TEXT = TextComponent.builder("").append("[").append("GD", TextColor.AQUA).append("] ").build();
+    public static final List<String> ID_MAP = new ArrayList<>();
+    public static final List<String> ITEM_IDS = new ArrayList<>();
+    public static List<Component> helpComponents = new ArrayList<>();
+
+    public static GriefDefenderPlugin getInstance() {
+        if (instance == null) {
+            instance = new GriefDefenderPlugin();
+        }
+        return instance;
+    }
+
+    public Path getConfigPath() {
+        return this.configPath;
+    }
+
+    public Logger getLogger() {
+        return this.logger;
+    }
+
+    public static void addEventLogEntry(Event event, Location<World> location, String sourceId, String targetId, GDPermissionHolder permissionSubject, String permission, String trust, Tristate result) {
+        final String eventName = event.getClass().getSimpleName().replace('$', '.').replace(".Impl", "");
+        final String eventLocation = location == null ? "none" : location.getBlockPosition().toString();
+        for (GDDebugData debugEntry : GriefDefenderPlugin.getInstance().getDebugUserMap().values()) {
+            final CommandSource debugSource = debugEntry.getSource();
+            final User debugUser = debugEntry.getTarget();
+            if (debugUser != null) {
+                if (permissionSubject == null) {
+                    continue;
+                }
+                // Check event source user
+                if (!permissionSubject.getFriendlyName().equals(debugUser.getUniqueId().toString())) {
+                    continue;
+                }
+            }
+
+            String messageUser = permissionSubject.getFriendlyName();
+            if (permissionSubject instanceof GDPermissionUser) {
+                messageUser = ((GDPermissionUser) permissionSubject).getName();
+            }
+
+            // record
+            if (debugEntry.isRecording()) {
+                permission = permission.replace("griefdefender.flag.", "");
+                String messageFlag = permission;
+                final Flag flag = FlagRegistryModule.getInstance().getById(permission).orElse(null);
+                if (flag != null) {
+                    messageFlag = flag.toString();
+                }
+                String messageSource = sourceId == null ? "none" : sourceId;
+                String messageTarget = targetId == null ? "none" : targetId;
+                if (messageTarget.endsWith(".0")) {
+                    messageTarget = messageTarget.substring(0, messageTarget.length() - 2);
+                }
+                if (trust == null) {
+                    trust = "none";
+                }
+                // Strip minecraft id on bukkit
+                String[] parts = messageSource.split(":");
+                if (parts.length > 1 && parts[0].equalsIgnoreCase("minecraft")) {
+                    messageSource = parts[1];
+                }
+                parts = messageTarget.split(":");
+                if (parts.length > 1 && parts[0].equalsIgnoreCase("minecraft")) {
+                    messageTarget = parts[1];
+                }
+                debugEntry.addRecord(messageFlag, trust, messageSource, messageTarget, eventLocation, messageUser, permission, result);
+                continue;
+            }
+
+            final Component textEvent = TextComponent.builder("")
+                    .append(GD_TEXT)
+                    .append("Event: ", TextColor.GRAY)
+                    .append(eventName == null ? TextComponent.of("Plugin").color(TextColor.GRAY) : TextComponent.of(eventName).color(TextColor.GRAY))
+                    .append("\n").build();
+            final Component textCause = TextComponent.builder("")
+                    .append(GD_TEXT)
+                    .append("Cause: ", TextColor.GRAY)
+                    .append(sourceId, TextColor.LIGHT_PURPLE)
+                    .append("\n").build();
+            final Component textLocation = TextComponent.builder("")
+                    .append(GD_TEXT)
+                    .append("Location: ", TextColor.GRAY)
+                    .append(eventLocation == null ? "NONE" : eventLocation).build();
+            final Component textUser = TextComponent.builder("")
+                    .append("User: ", TextColor.GRAY)
+                    .append(messageUser, TextColor.GOLD)
+                    .append("\n").build();
+            final Component textLocationAndUser = TextComponent.builder("")
+                    .append(textLocation)
+                    .append(" ")
+                    .append(textUser).build();
+            Component textContext = null;
+            Component textPermission = null;
+            if (targetId != null) {
+                textContext = TextComponent.builder("")
+                        .append(GD_TEXT)
+                        .append("Target: ", TextColor.GRAY)
+                        .append(GDPermissionManager.getInstance().getPermissionIdentifier(targetId), TextColor.YELLOW)
+                        .append("\n").build();
+            }
+            if (permission != null) {
+                textPermission = TextComponent.builder("")
+                        .append(GD_TEXT)
+                        .append("Permission: ", TextColor.GRAY)
+                        .append(permission, TextColor.RED)
+                        .append("\n").build();
+            }
+            TextComponent.Builder textBuilder = TextComponent.builder("").append(textEvent);
+            if (textContext != null) {
+                textBuilder.append(textContext);
+            } else {
+                textBuilder.append(textCause);
+            }
+            if (textPermission != null) {
+                textBuilder.append(textPermission);
+            }
+            textBuilder.append(textLocationAndUser);
+            TextAdapter.sendComponent(debugSource, textBuilder.build());
+        }
+    }
+
+    @Listener
+    public void onChangeServiceProvider(ChangeServiceProviderEvent event) {
+        if (event.getNewProvider() instanceof PermissionService && this.validateSpongeVersion()) {
+            ((PermissionService) event.getNewProvider()).registerContextCalculator(new ClaimContextCalculator());
+        }
+    }
+
+    private boolean validateSpongeVersion() {
+        if (Sponge.getPlatform().getContainer(org.spongepowered.api.Platform.Component.IMPLEMENTATION).getName().equals("SpongeForge")) {
+            if (Sponge.getPlatform().getContainer(org.spongepowered.api.Platform.Component.IMPLEMENTATION).getVersion().isPresent()) {
+                try {
+                    SPONGE_VERSION = Sponge.getPlatform().getContainer(org.spongepowered.api.Platform.Component.IMPLEMENTATION).getVersion().get();
+                    String build = SPONGE_VERSION.substring(Math.max(SPONGE_VERSION.length() - 4, 0));
+                    final int minSpongeBuild = 3549;
+                    final int spongeBuild = Integer.parseInt(build);
+                    if (spongeBuild < minSpongeBuild) {
+                        this.logger.error("Unable to initialize plugin. Detected SpongeForge build " + spongeBuild + " but GriefDefender requires"
+                                + " build " + minSpongeBuild + "+.");
+                        return false;
+                    }
+                } catch (NumberFormatException e) {
+
+                }
+            }
+        }
+
+        return true;
+    }
+
+    @Listener(order = Order.LAST)
+    public void onGameReload(GameReloadEvent event) {
+        this.loadConfig();
+        if (event.getSource() instanceof CommandSource) {
+            sendMessage((CommandSource) event.getSource(), MessageCache.getInstance().PLUGIN_RELOAD);
+        }
+    }
+
+    public void onPreInit(GamePreInitializationEvent event, Logger logger, Path path, PluginContainer pluginContainer) {
+        this.logger = logger;
+        this.configPath = path;
+        this.pluginContainer = pluginContainer;
+        if (!validateSpongeVersion() || Sponge.getPlatform().getType().isClient()) {
+            return;
+        }
+
+        this.permissionService = Sponge.getServiceManager().provide(PermissionService.class).orElse(null);
+        if (this.permissionService == null) {
+            this.logger.error("Unable to initialize plugin. GriefPrevention requires a permissions plugin such as LuckPerms.");
+            return;
+        }
+        this.permissionProvider = new LuckPermsProvider();
+
+        instance = this;
+        this.getLogger().info("Grief Prevention boot start.");
+        this.getLogger().info("Finished loading configuration.");
+        DEFAULT_HOLDER = new GDPermissionHolder("default");
+        PUBLIC_USER = new GDPermissionUser(PUBLIC_UUID, PUBLIC_NAME);
+        WORLD_USER = new GDPermissionUser(WORLD_USER_UUID, WORLD_USER_NAME);
+        this.getLogger().info("Registering GriefDefender API...");
+        Guice.createInjector(Stage.PRODUCTION, new GriefDefenderImplModule());
+
+        ChatTypeRegistryModule.getInstance().registerDefaults();
+        ClaimTypeRegistryModule.getInstance().registerDefaults();
+        ShovelTypeRegistryModule.getInstance().registerDefaults();
+        TrustTypeRegistryModule.getInstance().registerDefaults();
+        FlagRegistryModule.getInstance().registerDefaults();
+        ResultTypeRegistryModule.getInstance().registerDefaults();
+        EntityTypeRegistryModule.getInstance().registerDefaults();
+        BlockTypeRegistryModule.getInstance().registerDefaults();
+        ItemTypeRegistryModule.getInstance().registerDefaults();
+        CreateModeTypeRegistryModule.getInstance().registerDefaults();
+        GameModeTypeRegistryModule.getInstance().registerDefaults();
+        WeatherTypeRegistryModule.getInstance().registerDefaults();
+        OptionRegistryModule.getInstance().registerDefaults();
+        GriefDefender.getRegistry().registerBuilderSupplier(Claim.Builder.class, GDClaim.ClaimBuilder::new);
+        GriefDefender.getRegistry().registerBuilderSupplier(BankTransaction.Builder.class, GDBankTransaction.BankTransactionBuilder::new);
+        Sponge.getEventManager().registerListeners(GDBootstrap.getInstance(), GriefDefenderPlugin.getInstance());
+    }
+
+    @Listener
+    public void onServerAboutToStart(GameAboutToStartServerEvent event) {
+        if (!validateSpongeVersion()) {
+            return;
+        }
+
+        this.loadConfig();
+        this.registerBaseCommands();
+        this.executor = Executors.newFixedThreadPool(GriefDefenderPlugin.getGlobalConfig().getConfig().thread.numExecutorThreads);
+        final Path migratedPath = this.configPath.resolve("_gpSpongeMigrated");
+        if (GriefDefenderPlugin.getGlobalConfig().getConfig().migrator.gpSpongeMigrator && !Files.exists(migratedPath)) {
+            GPSpongeMigrator.getInstance().migrateData();
+            try {
+                Files.createFile(this.configPath.resolve("_gpSpongeMigrated"));
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+        this.economyService = Sponge.getServiceManager().provide(EconomyService.class);
+        if (this.economyService.isPresent()) {
+            this.logger.info("GriefDefender economy integration enabled.");
+            if (GriefDefenderPlugin.getGlobalConfig().getConfig().economy.economyMode) {
+                this.logger.info("Economy mode enabled!. Claimblocks will be disabled...");
+            }
+        } else if (GriefDefenderPlugin.getGlobalConfig().getConfig().economy.economyMode) {
+            this.logger.error("No economy plugin found! Unable to initialize economy plugin.");
+            return;
+        }
+
+        if (Sponge.getPluginManager().getPlugin("mcclans").isPresent()) {
+            this.clanApiProvider = new MCClansProvider();
+        }
+        if (Sponge.getPluginManager().getPlugin("nucleus").isPresent()) {
+            this.nucleusApiProvider = new NucleusProvider();
+        }
+        if (Sponge.getPluginManager().getPlugin("worldedit").isPresent() || Sponge.getPluginManager().getPlugin("fastasyncworldedit").isPresent()) {
+            this.worldEditProvider = new WorldEditProvider();
+        }
+
+        if (this.dataStore == null) {
+            try {
+                this.dataStore = new FileStorage();
+                this.dataStore.initialize();
+            } catch (Exception e) {
+                this.getLogger().info("Unable to initialize file storage.");
+                this.getLogger().info(e.getMessage());
+                e.printStackTrace();
+                return;
+            }
+        }
+
+        Sponge.getEventManager().registerListeners(GDBootstrap.getInstance(), new BlockEventHandler(dataStore));
+        Sponge.getEventManager().registerListeners(GDBootstrap.getInstance(), new PlayerEventHandler(dataStore, this));
+        Sponge.getEventManager().registerListeners(GDBootstrap.getInstance(), new EntityEventHandler(dataStore));
+        Sponge.getEventManager().registerListeners(GDBootstrap.getInstance(), new WorldEventHandler());
+        if (this.nucleusApiProvider != null) {
+            Sponge.getEventManager().registerListeners(GDBootstrap.getInstance(), new NucleusEventHandler());
+            this.nucleusApiProvider.registerTokens();
+        }
+        if (this.clanApiProvider != null) {
+            Sponge.getEventManager().registerListeners(GDBootstrap.getInstance(), new MCClansEventHandler());
+        }
+
+        PUBLIC_USER = new GDPermissionUser(Sponge.getServiceManager().provide(UserStorageService.class).get()
+                .getOrCreate(GameProfile.of(GriefDefenderPlugin.PUBLIC_UUID, GriefDefenderPlugin.PUBLIC_NAME)));
+        WORLD_USER = new GDPermissionUser(Sponge.getServiceManager().provide(UserStorageService.class).get()
+                .getOrCreate(GameProfile.of(GriefDefenderPlugin.WORLD_USER_UUID, GriefDefenderPlugin.WORLD_USER_NAME)));
+
+        // run cleanup task
+        int cleanupTaskInterval = GriefDefenderPlugin.getGlobalConfig().getConfig().claim.expirationCleanupInterval;
+        if (cleanupTaskInterval > 0) {
+            ClaimCleanupTask cleanupTask = new ClaimCleanupTask();
+            Sponge.getScheduler().createTaskBuilder().interval(10, TimeUnit.SECONDS).execute(cleanupTask)
+                    .submit(GDBootstrap.getInstance());
+        }
+    }
+
+    @Listener
+    public void onServerStarted(GameStartedServerEvent event) {
+        if (!validateSpongeVersion()) {
+            return;
+        }
+
+        final boolean resetMigration = GriefDefenderPlugin.getGlobalConfig().getConfig().playerdata.resetMigrations;
+        final boolean resetClaimData = GriefDefenderPlugin.getGlobalConfig().getConfig().playerdata.resetAccruedClaimBlocks;
+        final int migration2dRate = GriefDefenderPlugin.getGlobalConfig().getConfig().playerdata.migrateAreaRate;
+        final int migration3dRate = GriefDefenderPlugin.getGlobalConfig().getConfig().playerdata.migrateVolumeRate;
+        boolean migrate = false;
+        if (resetMigration || resetClaimData || (migration2dRate > -1 && GriefDefenderPlugin.CLAIM_BLOCK_SYSTEM == ClaimBlockSystem.AREA)
+                || (migration3dRate > -1 && GriefDefenderPlugin.CLAIM_BLOCK_SYSTEM == ClaimBlockSystem.VOLUME)) {
+            migrate = true;
+        }
+
+        if (migrate) {
+            List<GDPlayerData> playerDataList = new ArrayList<>();
+            if (BaseStorage.USE_GLOBAL_PLAYER_STORAGE) {
+                final GDClaimManager claimWorldManager = this.dataStore.getClaimWorldManager(Sponge.getServer().getDefaultWorld().get().getUniqueId());
+                claimWorldManager.resetPlayerData();
+                playerDataList = new ArrayList<>(claimWorldManager.getPlayerDataMap().values());
+                for (GDPlayerData playerData : playerDataList) {
+                    if (!Sponge.getServer().getPlayer(playerData.playerID).isPresent() && playerData.getClaims().isEmpty()) {
+                        playerData.onDisconnect();
+                        claimWorldManager.removePlayer(playerData.playerID);
+                    }
+                }
+            }
+            if (!BaseStorage.USE_GLOBAL_PLAYER_STORAGE) {
+                for (World world : Sponge.getServer().getWorlds()) {
+                    final GDClaimManager claimWorldManager = this.dataStore.getClaimWorldManager(world.getUniqueId());
+                    playerDataList = new ArrayList<>(claimWorldManager.getPlayerDataMap().values());
+                    for (GDPlayerData playerData : playerDataList) {
+                        if (!Sponge.getServer().getPlayer(playerData.playerID).isPresent() && playerData.getClaims().isEmpty()) {
+                            playerData.onDisconnect();
+                            claimWorldManager.removePlayer(playerData.playerID);
+                        }
+                    }
+                }
+            }
+            GriefDefenderPlugin.getGlobalConfig().getConfig().playerdata.resetMigrations = false;
+            GriefDefenderPlugin.getGlobalConfig().getConfig().playerdata.resetAccruedClaimBlocks = false;
+            GriefDefenderPlugin.getGlobalConfig().getConfig().playerdata.migrateAreaRate = -1;
+            GriefDefenderPlugin.getGlobalConfig().getConfig().playerdata.migrateVolumeRate = -1;
+            GriefDefenderPlugin.getGlobalConfig().save();
+        }
+
+        if (GriefDefenderPlugin.getGlobalConfig().getConfig().migrator.gpBukkitMigrator) {
+            GriefDefenderPlugin.getGlobalConfig().getConfig().migrator.gpBukkitMigrator = false;
+            GriefDefenderPlugin.getGlobalConfig().save();
+        }
+
+        Sponge.getScheduler().createTaskBuilder().intervalTicks(100).execute(new PlayerTickTask())
+                .submit(GDBootstrap.getInstance());
+        Sponge.getScheduler().createTaskBuilder().interval(5, TimeUnit.MINUTES).execute(new ClaimBlockTask())
+                .submit(GDBootstrap.getInstance());
+        this.logger.info("Loaded successfully.");
+    }
+
+    @Listener
+    public void onGameReloadEvent(GameReloadEvent event) {
+        this.loadConfig();
+    }
+
+    public void registerBaseCommands() {
+        /*Sponge.getCommandManager().register(this, CommandSpec.builder()
+        .description(Text.of("Lists detailed information on each command."))
+        .permission(GPPermissions.COMMAND_HELP)
+        .executor(new CommandHelp())
+        .build(), "gphelp");*/
+
+        SpongeCommandManager manager = new SpongeCommandManager(this.pluginContainer);
+        this.commandManager = manager;
+        manager.getCommandReplacements().addReplacement("griefdefender", "gd|griefdefender");
+        manager.registerCommand(new CommandAccessTrust());
+        manager.registerCommand(new CommandAdjustBonusClaimBlocks());
+        manager.registerCommand(new CommandCallback());
+        manager.registerCommand(new CommandClaimAbandon());
+        manager.registerCommand(new CommandClaimAbandonAll());
+        manager.registerCommand(new CommandClaimAbandonTop());
+        manager.registerCommand(new CommandClaimAdmin());
+        manager.registerCommand(new CommandClaimBan());
+        manager.registerCommand(new CommandClaimBank());
+        manager.registerCommand(new CommandClaimBasic());
+        manager.registerCommand(new CommandClaimBuy());
+        manager.registerCommand(new CommandClaimBuyBlocks());
+        manager.registerCommand(new CommandClaimClear());
+        manager.registerCommand(new CommandClaimContract());
+        manager.registerCommand(new CommandClaimCreate());
+        manager.registerCommand(new CommandClaimCuboid());
+        manager.registerCommand(new CommandClaimDelete());
+        manager.registerCommand(new CommandClaimDeleteAll());
+        manager.registerCommand(new CommandClaimDeleteAllAdmin());
+        manager.registerCommand(new CommandClaimDeleteTop());
+        manager.registerCommand(new CommandClaimExpand());
+        manager.registerCommand(new CommandClaimFarewell());
+        manager.registerCommand(new CommandClaimFlag());
+        manager.registerCommand(new CommandClaimFlagDebug());
+        manager.registerCommand(new CommandClaimFlagGroup());
+        manager.registerCommand(new CommandClaimFlagPlayer());
+        manager.registerCommand(new CommandClaimFlagReset());
+        manager.registerCommand(new CommandClaimGreeting());
+        manager.registerCommand(new CommandClaimIgnore());
+        manager.registerCommand(new CommandClaimInfo());
+        manager.registerCommand(new CommandClaimInherit());
+        manager.registerCommand(new CommandClaimList());
+        manager.registerCommand(new CommandClaimMode());
+        manager.registerCommand(new CommandClaimName());
+        manager.registerCommand(new CommandClaimOption());
+        manager.registerCommand(new CommandClaimOptionGroup());
+        manager.registerCommand(new CommandClaimOptionPlayer());
+        manager.registerCommand(new CommandClaimPermissionGroup());
+        manager.registerCommand(new CommandClaimPermissionPlayer());
+        manager.registerCommand(new CommandClaimSchematic());
+        manager.registerCommand(new CommandClaimSell());
+        manager.registerCommand(new CommandClaimSellBlocks());
+        manager.registerCommand(new CommandClaimSetSpawn());
+        manager.registerCommand(new CommandClaimSpawn());
+        manager.registerCommand(new CommandClaimSubdivision());
+        manager.registerCommand(new CommandClaimTown());
+        manager.registerCommand(new CommandClaimTransfer());
+        manager.registerCommand(new CommandClaimUnban());
+        manager.registerCommand(new CommandClaimWorldEdit());
+        manager.registerCommand(new CommandContainerTrust());
+        manager.registerCommand(new CommandDebug());
+        manager.registerCommand(new CommandGDReload());
+        manager.registerCommand(new CommandGDVersion());
+        manager.registerCommand(new CommandGiveBlocks());
+        manager.registerCommand(new CommandGivePet());
+        manager.registerCommand(new CommandHelp());
+        manager.registerCommand(new CommandPagination());
+        manager.registerCommand(new CommandPlayerInfo());
+        manager.registerCommand(new CommandRestoreClaim());
+        manager.registerCommand(new CommandRestoreNature());
+        manager.registerCommand(new CommandSetAccruedClaimBlocks());
+        manager.registerCommand(new CommandTownChat());
+        manager.registerCommand(new CommandTownTag());
+        manager.registerCommand(new CommandTrustGroup());
+        manager.registerCommand(new CommandTrustPlayer());
+        manager.registerCommand(new CommandTrustGroupAll());
+        manager.registerCommand(new CommandTrustPlayerAll());
+        manager.registerCommand(new CommandUntrustGroup());
+        manager.registerCommand(new CommandUntrustPlayer());
+        manager.registerCommand(new CommandUntrustGroupAll());
+        manager.registerCommand(new CommandUntrustPlayerAll());
+        manager.registerCommand(new CommandTrustList());
+        manager.enableUnstableAPI("help");
+
+        final Map<String, Component> helpMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+        // Generate help text
+        RootCommand rootCommand = getCommandManager().getRootCommand("gd");
+        for (BaseCommand child : rootCommand.getChildren()) {
+            for (RegisteredCommand registeredCommand : child.getRegisteredCommands()) {
+                if (helpMap.get(registeredCommand.getPrefSubCommand()) != null) {
+                    continue;
+                }
+                TextComponent permissionText = TextComponent.builder("")
+                        .append("Permission: ", TextColor.GOLD)
+                        .append(registeredCommand.getRequiredPermissions() == null ? "None" : String.join(",", registeredCommand.getRequiredPermissions()), TextColor.GRAY)
+                        .build();
+    
+                TextComponent argumentsText = TextComponent.builder("")
+                        //.append("Arguments: ", TextColor.AQUA)
+                        .append(registeredCommand.getSyntaxText() == null ? "Arguments: None" : registeredCommand.getSyntaxText(), TextColor.GREEN)
+                        .build();
+    
+                final TextComponent hoverText = TextComponent.builder("")
+                    .append("Command: ", TextColor.AQUA)
+                    .append(registeredCommand.getPrefSubCommand() + "\n", TextColor.GREEN)
+                    .append("Description: ", TextColor.AQUA)
+                    .append(registeredCommand.getHelpText() + "\n", TextColor.GREEN)
+                    .append("Arguments: ", TextColor.AQUA)
+                    .append(argumentsText)
+                    .append("\n")
+                    .append(permissionText)
+                    .build();
+    
+                final TextComponent commandText = TextComponent.builder("")
+                        .append("/gd " + registeredCommand.getPrefSubCommand(), TextColor.GREEN)
+                        .hoverEvent(HoverEvent.showText(hoverText))
+                        .clickEvent(ClickEvent.suggestCommand("/gd " + registeredCommand.getPrefSubCommand()))
+                        .build();
+                helpMap.put(registeredCommand.getPrefSubCommand(), commandText);
+            }
+        }
+        helpComponents = new ArrayList<>(helpMap.values());
+
+        NMSUtil.getInstance().populateTabComplete();
+        ID_MAP.add("any");
+        ID_MAP.add(ContextGroupKeys.AMBIENT);
+        ID_MAP.add(ContextGroupKeys.ANIMAL);
+        ID_MAP.add(ContextGroupKeys.AQUATIC);
+        ID_MAP.add(ContextGroupKeys.FOOD);
+        ID_MAP.add(ContextGroupKeys.MONSTER);
+        ID_MAP.add("#minecraft:ambient");
+        ID_MAP.add("#minecraft:animal");
+        ID_MAP.add("#minecraft:aquatic");
+        ID_MAP.add("#minecraft:monster");
+        ID_MAP.add("#minecraft:food");
+
+        // commands
+        ID_MAP.add("griefdefender:cf");
+        ID_MAP.add("griefdefender:cfg");
+        ID_MAP.add("griefdefender:cfp");
+        ID_MAP.add("griefdefender:cog");
+        ID_MAP.add("griefdefender:cop");
+        ID_MAP.add("griefdefender:cpg");
+        ID_MAP.add("griefdefender:cpp");
+        ID_MAP.add("griefdefender:claimflag");
+        ID_MAP.add("griefdefender:claimflaggroup");
+        ID_MAP.add("griefdefender:claimflagplayer");
+        ID_MAP.add("any");
+        ID_MAP.add("unknown");
+
+        // Add our callback command to spam exclusion list
+        // This prevents players from being kicked if clicking callbacks too fast
+        /*if (!org.spigotmc.SpigotConfig.spamExclusions.contains("/gd:callback")) {
+            org.spigotmc.SpigotConfig.spamExclusions.add("/gd:callback");
+        }*/
+        manager.getCommandCompletions().registerCompletion("gdplayers", c -> {
+            return ImmutableList.copyOf(PermissionUtil.getInstance().getAllLoadedPlayerNames());
+        });
+        manager.getCommandCompletions().registerCompletion("gdgroups", c -> {
+            return ImmutableList.copyOf(PermissionUtil.getInstance().getAllLoadedGroupNames());
+        });
+        manager.getCommandCompletions().registerCompletion("gdbantypes", c -> {
+            List<String> tabList = new ArrayList<>();
+            tabList.add("block");
+            tabList.add("entity");
+            tabList.add("item");
+            tabList.add("hand");
+            return ImmutableList.copyOf(tabList);
+        });
+        manager.getCommandCompletions().registerCompletion("gdblockfaces", c -> {
+            List<String> tabList = new ArrayList<>();
+            tabList.add("north");
+            tabList.add("east");
+            tabList.add("south");
+            tabList.add("west");
+            tabList.add("up");
+            tabList.add("down");
+            tabList.add("all");
+            return ImmutableList.copyOf(tabList);
+        });
+        manager.getCommandCompletions().registerCompletion("gdclaimtypes", c -> {
+            List<String> tabList = new ArrayList<>();
+            for (ClaimType type : ClaimTypeRegistryModule.getInstance().getAll()) {
+                tabList.add(type.getName());
+            }
+            return ImmutableList.copyOf(tabList);
+        });
+        manager.getCommandCompletions().registerCompletion("gdtrusttypes", c -> {
+            List<String> tabList = new ArrayList<>();
+            for (TrustType type : TrustTypeRegistryModule.getInstance().getAll()) {
+                tabList.add(type.getName());
+            }
+            return ImmutableList.copyOf(tabList);
+        });
+        manager.getCommandCompletions().registerCompletion("gdflags", c -> {
+            List<String> tabList = new ArrayList<>();
+            for (Flag type : FlagRegistryModule.getInstance().getAll()) {
+                tabList.add(type.getName());
+            }
+            return ImmutableList.copyOf(tabList);
+        });
+        manager.getCommandCompletions().registerCompletion("gdoptions", c -> {
+            List<String> tabList = new ArrayList<>();
+            for (Option type : GriefDefender.getRegistry().getAllOf(Option.class)) {
+                tabList.add(type.getName());
+            }
+            return ImmutableList.copyOf(tabList);
+        });
+        manager.getCommandCompletions().registerCompletion("gdentityids", c -> {
+            List<String> tabList = new ArrayList<>();
+            for (GDEntityType type : EntityTypeRegistryModule.getInstance().getAll()) {
+                tabList.add(type.getName());
+            }
+            return ImmutableList.copyOf(tabList);
+        });
+        manager.getCommandCompletions().registerCompletion("gdmcids", c -> {
+            List<String> tabList = new ArrayList<>();
+            for (GDBlockType type : BlockTypeRegistryModule.getInstance().getAll()) {
+                tabList.add(type.getName());
+            }
+            for (GDItemType type : ItemTypeRegistryModule.getInstance().getAll()) {
+                tabList.add(type.getName());
+            }
+            for (GDEntityType type : EntityTypeRegistryModule.getInstance().getAll()) {
+                tabList.add(type.getName());
+            }
+            return ImmutableList.copyOf(tabList);
+        });
+        manager.getCommandCompletions().registerCompletion("gdtristates", c -> {
+            return ImmutableList.of("true", "false", "undefined");
+        });
+        manager.getCommandCompletions().registerCompletion("gdcontexts", c -> {
+            return ImmutableList.of("context[<override|default|used_item|source|world|server|player|group>]");
+        });
+        manager.getCommandCompletions().registerCompletion("gdworlds", c -> {
+            List<String> tabList = new ArrayList<>();
+            for (World world : Sponge.getServer().getWorlds()) {
+                tabList.add(world.getName().toLowerCase());
+            }
+            return ImmutableList.copyOf(tabList);
+        });
+        manager.getCommandCompletions().registerCompletion("gddummy", c -> {
+            return ImmutableList.of();
+        });
+    }
+
+    public SpongeCommandManager getCommandManager() {
+        return this.commandManager;
+    }
+
+    public void loadConfig() {
+        this.getLogger().info("Loading configuration...");
+        try {
+            TypeSerializers.getDefaultSerializers().registerType(TypeToken.of(Component.class), new ComponentConfigSerializer());
+            TypeSerializers.getDefaultSerializers().registerType(TypeToken.of(ClaimType.class), new ClaimTypeSerializer());
+            TypeSerializers.getDefaultSerializers().registerType(TypeToken.of(CreateModeType.class), new CreateModeTypeSerializer());
+            TypeSerializers.getDefaultSerializers().registerType(TypeToken.of(GameModeType.class), new GameModeTypeSerializer());
+            TypeSerializers.getDefaultSerializers().registerType(TypeToken.of(WeatherType.class), new WeatherTypeSerializer());
+            TypeSerializers.getDefaultSerializers().registerType(TypeToken.of(GDCustomFlagDefinition.class), new CustomFlagSerializer());
+
+            if (Files.notExists(BaseStorage.dataLayerFolderPath)) {
+                Files.createDirectories(BaseStorage.dataLayerFolderPath);
+            }
+
+            Path rootConfigPath = this.getConfigPath().resolve("worlds");
+            BaseStorage.globalConfig = new GriefDefenderConfig<>(GlobalConfig.class, this.getConfigPath().resolve("global.conf"), null);
+            BaseStorage.globalConfig.getConfig().permissionCategory.refreshFlags();
+            BaseStorage.globalConfig.getConfig().permissionCategory.checkOptions();
+            String localeString = BaseStorage.globalConfig.getConfig().message.locale;
+            try {
+                LocaleUtils.toLocale(localeString);
+            } catch (IllegalArgumentException e) {
+                e.printStackTrace();
+                this.logger.error("Could not validate the locale '" + localeString + "'. Defaulting to 'en_US'...");
+                localeString = "en_US";
+            }
+            final Path localePath = this.getConfigPath().resolve("lang").resolve(localeString + ".conf");
+            if (!localePath.toFile().exists()) {
+                // Check for a default locale asset and copy to lang folder
+                /*final Asset asset = GDBootstrap.getInstance().pluginContainer.getAsset("lang/" + localeString + ".conf").orElse(null);
+                if (asset != null) {
+                    asset.copyToDirectory(localePath.getParent());
+                }*/
+                // Check for a default locale asset and copy to lang folder
+                try {
+                    final InputStream in = getClass().getResourceAsStream("/assets/lang/" + localeString + ".conf");
+                    FileUtils.copyInputStreamToFile(in, localePath.toFile());
+                } catch (Throwable t) {
+                    t.printStackTrace();
+                }
+            }
+            messageStorage = new MessageStorage(localePath);
+            messageData = messageStorage.getConfig();
+            MessageCache.getInstance().loadCache();
+            BaseStorage.globalConfig.getConfig().customFlags.initDefaults();
+            BaseStorage.globalConfig.save();
+            BaseStorage.USE_GLOBAL_PLAYER_STORAGE = BaseStorage.globalConfig.getConfig().playerdata.useGlobalPlayerDataStorage;
+            GDFlags.populateFlagStatus();
+            PermissionHolderCache.getInstance().getOrCreatePermissionCache(GriefDefenderPlugin.DEFAULT_HOLDER).invalidateAll();
+            CLAIM_BLOCK_SYSTEM = BaseStorage.globalConfig.getConfig().playerdata.claimBlockSystem;
+            final GDItemType defaultModTool = ItemTypeRegistryModule.getInstance().getById("minecraft:golden_shovel").orElse(null);
+            final GDBlockType defaultCreateVisualBlock = BlockTypeRegistryModule.getInstance().getById("minecraft:diamond_block").orElse(null);
+            this.createVisualBlock = BlockTypeRegistryModule.getInstance().getById(BaseStorage.globalConfig.getConfig().visual.claimCreateStartBlock).orElse(defaultCreateVisualBlock);
+            this.modificationTool  = ItemTypeRegistryModule.getInstance().getById(BaseStorage.globalConfig.getConfig().claim.modificationTool).orElse(defaultModTool);
+            this.investigationTool = ItemTypeRegistryModule.getInstance().getById(BaseStorage.globalConfig.getConfig().claim.investigationTool).orElse(ItemTypeRegistryModule.getInstance().getById("minecraft:stick").get());
+            this.maxInspectionDistance = BaseStorage.globalConfig.getConfig().general.maxClaimInspectionDistance;
+            if (this.dataStore != null) {
+                for (World world : Sponge.getGame().getServer().getWorlds()) {
+                    DimensionType dimType = world.getProperties().getDimensionType();
+                    final String[] parts = dimType.getId().split(":");
+                    final Path dimPath = rootConfigPath.resolve(parts[0]).resolve(dimType.getName());
+                    if (!Files.exists(dimPath.resolve(world.getProperties().getWorldName()))) {
+                        try {
+                            Files.createDirectories(rootConfigPath.resolve(dimType.getId()).resolve(world.getName()));
+                        } catch (IOException e) {
+                            e.printStackTrace();
+                        }
+                    }
+    
+                    GriefDefenderConfig<ConfigBase> dimConfig = new GriefDefenderConfig<>(ConfigBase.class, dimPath.resolve("dimension.conf"), BaseStorage.globalConfig);
+                    GriefDefenderConfig<ConfigBase> worldConfig = new GriefDefenderConfig<>(ConfigBase.class, dimPath.resolve(world.getProperties().getWorldName()).resolve("world.conf"), dimConfig);
+    
+                    BaseStorage.dimensionConfigMap.put(world.getProperties().getUniqueId(), dimConfig);
+                    BaseStorage.worldConfigMap.put(world.getProperties().getUniqueId(), worldConfig);
+    
+                    // refresh player data
+                    final GDClaimManager claimManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(world.getUniqueId());
+                    for (GDPlayerData playerData : claimManager.getPlayerDataMap().values()) {
+                        if (playerData.playerID.equals(WORLD_USER_UUID) || playerData.playerID.equals(ADMIN_USER_UUID) || playerData.playerID.equals(PUBLIC_UUID)) {
+                            continue;
+                        }
+                        playerData.refreshPlayerOptions();
+                    }
+
+                    if (GriefDefenderPlugin.getGlobalConfig().getConfig().migrator.gpBukkitMigrator) {
+                        GriefDefenderPlugin.getGlobalConfig().getConfig().migrator.gpBukkitMigrator = false;
+                        GriefDefenderPlugin.getGlobalConfig().save();
+                    }
+                    if (this.worldEditProvider != null) {
+                        this.getLogger().info("Loading schematics for world " + world.getName() + "...");
+                        this.worldEditProvider.loadSchematics(world);
+                    }
+                }
+                // refresh default permissions
+                this.dataStore.setDefaultGlobalPermissions();
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    public static void sendClaimDenyMessage(GDClaim claim, CommandSource src, Component message) {
+        if (claim.getData() != null && !claim.getData().allowDenyMessages()) {
+            return;
+        }
+
+        sendMessage(src, message);
+    }
+
+    public static void sendMessage(CommandSource src, Component message) {
+        if (src == null) {
+            return;
+        }
+        if (src instanceof Player && SpongeImplHooks.isFakePlayer((net.minecraft.entity.Entity) src)) {
+            return;
+        }
+        if (message == TextComponent.empty() || message == null) {
+            return;
+        }
+
+        if (src == null) {
+            GriefDefenderPlugin.getInstance().getLogger().warn(PlainComponentSerializer.INSTANCE.serialize(message));
+        } else {
+            TextAdapter.sendComponent(src, message);
+        }
+    }
+
+    public static GriefDefenderConfig<?> getActiveConfig(WorldProperties worldProperties) {
+        return getActiveConfig(worldProperties.getUniqueId());
+    }
+
+    public static GriefDefenderConfig<? extends ConfigBase> getActiveConfig(UUID worldUniqueId) {
+        GriefDefenderConfig<ConfigBase> config = BaseStorage.worldConfigMap.get(worldUniqueId);
+        if (config != null) {
+            return config;
+        }
+
+        config = BaseStorage.dimensionConfigMap.get(worldUniqueId);
+        if (config != null) {
+            return config;
+        }
+
+        return BaseStorage.globalConfig;
+    }
+
+    public static GriefDefenderConfig<GlobalConfig> getGlobalConfig() {
+        return BaseStorage.globalConfig;
+    }
+
+    public boolean claimsEnabledForWorld(UUID worldUniqueId) {
+        return GriefDefenderPlugin.getActiveConfig(worldUniqueId).getConfig().claim.claimsEnabled == 1;
+    }
+
+    public int getSeaLevel(World world) {
+        return world.getDimension().getMinimumSpawnHeight();
+    }
+
+    public Map<String, GDDebugData> getDebugUserMap() {
+        return this.debugUserMap;
+    }
+
+    public static GDPermissionUser getOrCreateUser(UUID uuid) {
+        if (uuid == null) {
+            return null;
+        }
+
+        if (uuid == PUBLIC_UUID) {
+            return PUBLIC_USER;
+        }
+        if (uuid == WORLD_USER_UUID) {
+            return WORLD_USER;
+        }
+
+        // check the cache
+        return PermissionHolderCache.getInstance().getOrCreateUser(uuid);
+    }
+
+    public static boolean isSourceIdBlacklisted(String flag, Object source, WorldProperties worldProperties) {
+        return isSourceIdBlacklisted(flag, source, worldProperties.getUniqueId());
+    }
+
+    public static boolean isSourceIdBlacklisted(String flag, Object source, UUID worldUniqueId) {
+        final List<String> flagList = GriefDefenderPlugin.getGlobalConfig().getConfig().blacklist.flagIdBlacklist.get(flag);
+        final boolean checkFlag = flagList != null && !flagList.isEmpty();
+        final boolean checkGlobal = !GriefDefenderPlugin.getGlobalConfig().getConfig().blacklist.globalSourceBlacklist.isEmpty();
+        if (!checkFlag && !checkGlobal) {
+            return false;
+        }
+
+        final GriefDefenderConfig<?> activeConfig = GriefDefenderPlugin.getActiveConfig(worldUniqueId);
+        final String id = GDPermissionManager.getInstance().getPermissionIdentifier(source);
+        final String idNoMeta = GDPermissionManager.getInstance().getIdentifierWithoutMeta(id);
+
+        // Check global
+        if (checkGlobal) {
+            final BlacklistCategory blacklistCategory = activeConfig.getConfig().blacklist;
+            final List<String> globalSourceBlacklist = blacklistCategory.getGlobalSourceBlacklist();
+            if (globalSourceBlacklist == null) {
+                return false;
+            }
+            for (String str : globalSourceBlacklist) {
+                if (FilenameUtils.wildcardMatch(id, str)) {
+                    return true;
+                }
+                if (FilenameUtils.wildcardMatch(idNoMeta, str)) {
+                    return true;
+                }
+            }
+        }
+        // Check flag
+        if (checkFlag) {
+            for (String str : flagList) {
+                if (FilenameUtils.wildcardMatch(id, str)) {
+                    return true;
+                }
+                if (FilenameUtils.wildcardMatch(idNoMeta, str)) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    public static boolean isTargetIdBlacklisted(String flag, Object target, WorldProperties worldProperties) {
+        return isTargetIdBlacklisted(flag, target, worldProperties.getUniqueId());
+    }
+
+    public static boolean isTargetIdBlacklisted(String flag, Object target, UUID worldUniqueId) {
+        final List<String> flagList = GriefDefenderPlugin.getGlobalConfig().getConfig().blacklist.flagIdBlacklist.get(flag);
+        final boolean checkFlag = flagList != null && !flagList.isEmpty();
+        final boolean checkGlobal = !GriefDefenderPlugin.getGlobalConfig().getConfig().blacklist.globalTargetBlacklist.isEmpty();
+        if (!checkFlag && !checkGlobal) {
+            return false;
+        }
+
+        final GriefDefenderConfig<?> activeConfig = GriefDefenderPlugin.getActiveConfig(worldUniqueId);
+        final String id = GDPermissionManager.getInstance().getPermissionIdentifier(target);
+        final String idNoMeta = GDPermissionManager.getInstance().getIdentifierWithoutMeta(id);
+
+        // Check global
+        if (checkGlobal) {
+            final BlacklistCategory blacklistCategory = activeConfig.getConfig().blacklist;
+            final List<String> globalTargetBlacklist = blacklistCategory.getGlobalTargetBlacklist();
+            if (globalTargetBlacklist == null) {
+                return false;
+            }
+            for (String str : globalTargetBlacklist) {
+                if (FilenameUtils.wildcardMatch(id, str)) {
+                    return true;
+                }
+                if (FilenameUtils.wildcardMatch(idNoMeta, str)) {
+                    return true;
+                }
+            }
+        }
+        // Check flag
+        if (checkFlag) {
+            for (String str : flagList) {
+                if (FilenameUtils.wildcardMatch(id, str)) {
+                    return true;
+                }
+                if (FilenameUtils.wildcardMatch(idNoMeta, str)) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    public boolean isEconomyModeEnabled() {
+        return GriefDefenderPlugin.getGlobalConfig().getConfig().economy.economyMode;
+    }
+
+    public WorldEditProvider getWorldEditProvider() {
+        return this.worldEditProvider;
+    }
+
+    public PermissionProvider getPermissionProvider() {
+        return this.permissionProvider;
+    }
+}
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/cache/EventResultCache.java b/sponge/src/main/java/com/griefdefender/cache/EventResultCache.java
new file mode 100644
index 0000000..e330cfd
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/cache/EventResultCache.java
@@ -0,0 +1,78 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.cache;
+
+import java.util.UUID;
+
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.Tristate;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.TrustType;
+import com.griefdefender.api.claim.TrustTypes;
+import com.griefdefender.api.permission.flag.Flag;
+import com.griefdefender.claim.GDClaim;
+
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.spongepowered.api.Sponge;
+
+public class EventResultCache {
+
+    public final String eventFlag;
+    public final Tristate lastResult;
+    public final int lastTickCounter;
+    public final UUID lastClaim;
+    public final String lastTrust;
+
+    public EventResultCache(Claim claim, String flag, Tristate result) {
+        this(claim, flag, result, null);
+    }
+
+    public EventResultCache(Claim claim, String flag, Tristate result, String trust) {
+        this.eventFlag = flag;
+        this.lastClaim = claim.getUniqueId();
+        this.lastResult = result;
+        this.lastTickCounter = Sponge.getServer().getRunningTimeTicks();
+        this.lastTrust = trust == null ? "cache" : trust;
+    }
+
+    public Tristate checkEventResultCache(GDClaim claim) {
+        return this.checkEventResultCache(claim, null);
+    }
+
+    public Tristate checkEventResultCache(GDClaim claim, String flag) {
+        if (Sponge.getServer().getRunningTimeTicks() > this.lastTickCounter) {
+            return Tristate.UNDEFINED;
+        }
+
+        if (claim.getUniqueId().equals(this.lastClaim)) {
+            if (flag != null && this.eventFlag != null && !this.eventFlag.equalsIgnoreCase(flag)) {
+                return Tristate.UNDEFINED;
+            }
+            return this.lastResult;
+        }
+
+        return Tristate.UNDEFINED;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/cache/MessageCache.java b/sponge/src/main/java/com/griefdefender/cache/MessageCache.java
new file mode 100644
index 0000000..5894189
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/cache/MessageCache.java
@@ -0,0 +1,772 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.cache;
+
+import com.griefdefender.api.permission.flag.Flag;
+import com.griefdefender.api.permission.option.Option;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.permission.flag.GDFlag;
+import com.griefdefender.permission.option.GDOption;
+import com.griefdefender.registry.FlagRegistryModule;
+import com.griefdefender.registry.OptionRegistryModule;
+
+import net.kyori.text.Component;
+
+public class MessageCache {
+
+    private static MessageCache instance;
+
+    static {
+        instance = new MessageCache();
+    }
+
+    public static MessageCache getInstance() {
+        return instance;
+    }
+
+    public Component ABANDON_ALL_DELAY_WARNING;
+    public Component ABANDON_ALL_WARNING;
+    public Component ABANDON_CLAIM_MISSING;
+    public Component ABANDON_TOP_LEVEL;
+    public Component ABANDON_TOWN_CHILDREN;
+    public Component ABANDON_WARNING;
+    public Component BANK_CLICK_VIEW_TRANSACTIONS;
+    public Component BANK_DEPOSIT_NO_FUNDS;
+    public Component BANK_TITLE_TRANSACTIONS;
+    public Component BANK_TAX_SYSTEM_DISABLED;
+    public Component CLAIM_AUTOMATIC_NOTIFICATION;
+    public Component CLAIM_CHEST_CONFIRMATION;
+    public Component CLAIM_CHILDREN_WARNING;
+    public Component CLAIM_DISABLED_WORLD;
+    public Component CLAIM_FAREWELL_CLEAR;
+    public Component CLAIM_GREETING_CLEAR;
+    public Component CLAIM_IGNORE;
+    public Component CLAIM_NO_CLAIMS;
+    public Component CLAIM_NOT_FOUND;
+    public Component CLAIM_NOT_YOURS;
+    public Component CLAIM_OWNER_ALREADY;
+    public Component CLAIM_OWNER_ONLY;
+    public Component CLAIM_RESPECTING;
+    public Component CLAIM_RESTORE_SUCCESS;
+    public Component CLAIM_TOO_FAR;
+    public Component CLAIMINFO_UI_ADMIN_SETTINGS;
+    public Component CLAIMINFO_UI_BANK_INFO;
+    public Component CLAIMINFO_UI_CLAIM_EXPIRATION;
+    public Component CLAIMINFO_UI_CLICK_ADMIN;
+    public Component CLAIMINFO_UI_CLICK_BANK;
+    public Component CLAIMINFO_UI_CLICK_TOGGLE;
+    public Component CLAIMINFO_UI_DENY_MESSAGES;
+    public Component CLAIMINFO_UI_FLAG_OVERRIDES;
+    public Component CLAIMINFO_UI_FOR_SALE;
+    public Component CLAIMINFO_UI_INHERIT_PARENT;
+    public Component CLAIMINFO_UI_LAST_ACTIVE;
+    public Component CLAIMINFO_UI_NORTH_CORNERS;
+    public Component CLAIMINFO_UI_PVP_OVERRIDES;
+    public Component CLAIMINFO_UI_REQUIRES_CLAIM_BLOCKS;
+    public Component CLAIMINFO_UI_RETURN_BANKINFO;
+    public Component CLAIMINFO_UI_RETURN_CLAIMINFO;
+    public Component CLAIMINFO_UI_RETURN_SETTINGS;
+    public Component CLAIMINFO_UI_SIZE_RESTRICTIONS;
+    public Component CLAIMINFO_UI_SOUTH_CORNERS;
+    public Component CLAIMINFO_UI_TELEPORT_FEATURE;
+    public Component CLAIMINFO_UI_TELEPORT_SPAWN;
+    public Component CLAIMINFO_UI_TITLE_CLAIMINFO;
+    public Component CLAIMINFO_UI_TOWN_SETTINGS;
+    public Component CLAIMLIST_UI_CLICK_INFO;
+    public Component CLAIMLIST_UI_CLICK_PURCHASE;
+    public Component CLAIMLIST_UI_CLICK_VIEW_CHILDREN;
+    public Component CLAIMLIST_UI_CLICK_VIEW_CLAIMS;
+    public Component CLAIMLIST_UI_NO_CLAIMS_FOUND;
+    public Component CLAIMLIST_UI_RETURN_CLAIMSLIST;
+    public Component CLAIMLIST_UI_TITLE;
+    public Component CLAIMLIST_UI_TITLE_CHILD_CLAIMS;
+    public Component COMMAND_CLAIMBUY_TITLE;
+    public Component COMMAND_CLAIMCLEAR_UUID_DENY;
+    public Component COMMAND_CLAIMFLAGDEBUG_DISABLED;
+    public Component COMMAND_CLAIMFLAGDEBUG_ENABLED;
+    public Component COMMAND_CLAIMINFO_NOT_FOUND;
+    public Component COMMAND_CLAIMINFO_UUID_REQUIRED;
+    public Component COMMAND_CLAIMINHERIT_DISABLED;
+    public Component COMMAND_CLAIMINHERIT_ENABLED;
+    public Component COMMAND_CLAIMMODE_DISABLED;
+    public Component COMMAND_CLAIMMODE_ENABLED;
+    public Component COMMAND_CUBOID_DISABLED;
+    public Component COMMAND_CUBOID_ENABLED;
+    public Component COMMAND_INHERIT_ONLY_CHILD;
+    public Component COMMAND_INVALID;
+    public Component COMMAND_INVALID_PLAYER_GROUP;
+    public Component COMMAND_NOT_AVAILABLE_ECONOMY;
+    public Component COMMAND_PET_CONFIRMATION;
+    public Component COMMAND_PET_TRANSFER_READY;
+    public Component COMMAND_PET_TRANSFER_CANCEL;
+    public Component COMMAND_WORLDEDIT_MISSING;
+    public Component CREATE_CANCEL;
+    public Component CREATE_CUBOID_DISABLED;
+    public Component CREATE_OVERLAP;
+    public Component CREATE_OVERLAP_SHORT;
+    public Component CREATE_SUBDIVISION_FAIL;
+    public Component CREATE_SUBDIVISION_ONLY;
+    public Component DEBUG_NO_RECORDS;
+    public Component DEBUG_PASTE_SUCCESS;
+    public Component DEBUG_RECORD_END;
+    public Component DEBUG_RECORD_START;
+    public Component DEBUG_TIME_ELAPSED;
+    public Component ECONOMY_BLOCK_BUY_INVALID;
+    public Component ECONOMY_BLOCK_BUY_SELL_DISABLED;
+    public Component ECONOMY_BLOCK_NOT_AVAILABLE;
+    public Component ECONOMY_BLOCK_ONLY_BUY;
+    public Component ECONOMY_BLOCK_ONLY_SELL;
+    public Component ECONOMY_CLAIM_NOT_FOR_SALE;
+    public Component ECONOMY_CLAIM_SALE_CANCELLED;
+    public Component ECONOMY_NOT_INSTALLED;
+    public Component ECONOMY_VIRTUAL_NOT_SUPPORTED;
+    public Component FEATURE_NOT_AVAILABLE;
+    public Component FLAG_DESCRIPTION_CUSTOM_BLOCK_BREAK;
+    public Component FLAG_DESCRIPTION_CUSTOM_BLOCK_GROW;
+    public Component FLAG_DESCRIPTION_CUSTOM_BLOCK_PLACE;
+    public Component FLAG_DESCRIPTION_CUSTOM_BLOCK_SPREAD;
+    public Component FLAG_DESCRIPTION_CUSTOM_ENDERPEARL;
+    public Component FLAG_DESCRIPTION_CUSTOM_EXIT_PLAYER;
+    public Component FLAG_DESCRIPTION_CUSTOM_EXPLOSION_BLOCK;
+    public Component FLAG_DESCRIPTION_CUSTOM_EXPLOSION_ENTITY;
+    public Component FLAG_DESCRIPTION_CUSTOM_EXP_DROP;
+    public Component FLAG_DESCRIPTION_CUSTOM_FALL_DAMAGE;
+    public Component FLAG_DESCRIPTION_CUSTOM_INTERACT_BLOCK;
+    public Component FLAG_DESCRIPTION_CUSTOM_INTERACT_ENTITY;
+    public Component FLAG_DESCRIPTION_CUSTOM_INTERACT_INVENTORY;
+    public Component FLAG_DESCRIPTION_CUSTOM_INVINCIBLE;
+    public Component FLAG_DESCRIPTION_CUSTOM_ITEM_DROP;
+    public Component FLAG_DESCRIPTION_CUSTOM_ITEM_PICKUP;
+    public Component FLAG_DESCRIPTION_CUSTOM_MONSTER_DAMAGE;
+    public Component FLAG_DESCRIPTION_CUSTOM_PISTONS;
+    public Component FLAG_DESCRIPTION_CUSTOM_PORTAL_USE;
+    public Component FLAG_DESCRIPTION_CUSTOM_SPAWN_AQUATIC;
+    public Component FLAG_DESCRIPTION_CUSTOM_SPAWN_AMBIENT;
+    public Component FLAG_DESCRIPTION_CUSTOM_SPAWN_ANIMAL;
+    public Component FLAG_DESCRIPTION_CUSTOM_SPAWN_MONSTER;
+    public Component FLAG_DESCRIPTION_CUSTOM_TELEPORT_FROM;
+    public Component FLAG_DESCRIPTION_CUSTOM_TELEPORT_TO;
+    public Component FLAG_DESCRIPTION_CUSTOM_USE;
+    public Component FLAG_DESCRIPTION_CUSTOM_VEHICLE_DESTROY;
+    public Component FLAG_DESCRIPTION_CUSTOM_WITHER_DAMAGE;
+    public Component FLAG_DESCRIPTION_CUSTOM_BLOCK_TRAMPLING;
+    public Component FLAG_DESCRIPTION_CUSTOM_CHEST_ACCESS;
+    public Component FLAG_DESCRIPTION_CUSTOM_CHORUS_FRUIT_TELEPORT;
+    public Component FLAG_DESCRIPTION_CUSTOM_CROP_GROWTH;
+    public Component FLAG_DESCRIPTION_CUSTOM_DAMAGE_ANIMALS;
+    public Component FLAG_DESCRIPTION_CUSTOM_ENDERMAN_GRIEF;
+    public Component FLAG_DESCRIPTION_CUSTOM_ENTER_PLAYER;
+    public Component FLAG_DESCRIPTION_CUSTOM_EXPLOSION_CREEPER;
+    public Component FLAG_DESCRIPTION_CUSTOM_EXPLOSION_TNT;
+    public Component FLAG_DESCRIPTION_CUSTOM_FIRE_DAMAGE;
+    public Component FLAG_DESCRIPTION_CUSTOM_FIRE_SPREAD;
+    public Component FLAG_DESCRIPTION_CUSTOM_GRASS_GROWTH;
+    public Component FLAG_DESCRIPTION_CUSTOM_ICE_FORM;
+    public Component FLAG_DESCRIPTION_CUSTOM_ICE_MELT;
+    public Component FLAG_DESCRIPTION_CUSTOM_LAVA_FLOW;
+    public Component FLAG_DESCRIPTION_CUSTOM_LEAF_DECAY;
+    public Component FLAG_DESCRIPTION_CUSTOM_LIGHTNING;
+    public Component FLAG_DESCRIPTION_CUSTOM_LIGHTER;
+    public Component FLAG_DESCRIPTION_CUSTOM_MUSHROOM_GROWTH;
+    public Component FLAG_DESCRIPTION_CUSTOM_MYCELIUM_SPREAD;
+    public Component FLAG_DESCRIPTION_CUSTOM_PVP;
+    public Component FLAG_DESCRIPTION_CUSTOM_RIDE;
+    public Component FLAG_DESCRIPTION_CUSTOM_SLEEP;
+    public Component FLAG_DESCRIPTION_CUSTOM_SNOW_FALL;
+    public Component FLAG_DESCRIPTION_CUSTOM_SNOW_MELT;
+    public Component FLAG_DESCRIPTION_CUSTOM_SNOWMAN_TRAIL;
+    public Component FLAG_DESCRIPTION_CUSTOM_SOIL_DRY;
+    public Component FLAG_DESCRIPTION_CUSTOM_VEHICLE_PLACE;
+    public Component FLAG_DESCRIPTION_CUSTOM_VINE_GROWTH;
+    public Component FLAG_DESCRIPTION_CUSTOM_WATER_FLOW;
+    public Component FLAG_DESCRIPTION_BLOCK_BREAK;
+    public Component FLAG_DESCRIPTION_BLOCK_GROW;
+    public Component FLAG_DESCRIPTION_BLOCK_MODIFY;
+    public Component FLAG_DESCRIPTION_BLOCK_PLACE;
+    public Component FLAG_DESCRIPTION_BLOCK_SPREAD;
+    public Component FLAG_DESCRIPTION_COLLIDE_BLOCK;
+    public Component FLAG_DESCRIPTION_COLLIDE_ENTITY;
+    public Component FLAG_DESCRIPTION_COMMAND_EXECUTE;
+    public Component FLAG_DESCRIPTION_COMMAND_EXECUTE_PVP;
+    public Component FLAG_DESCRIPTION_ENTER_CLAIM;
+    public Component FLAG_DESCRIPTION_ENTITY_CHUNK_SPAWN;
+    public Component FLAG_DESCRIPTION_ENTITY_DAMAGE;
+    public Component FLAG_DESCRIPTION_ENTITY_RIDING;
+    public Component FLAG_DESCRIPTION_ENTITY_SPAWN;
+    public Component FLAG_DESCRIPTION_ENTITY_TELEPORT_FROM;
+    public Component FLAG_DESCRIPTION_ENTITY_TELEPORT_TO;
+    public Component FLAG_DESCRIPTION_EXIT_CLAIM;
+    public Component FLAG_DESCRIPTION_EXPLOSION_BLOCK;
+    public Component FLAG_DESCRIPTION_EXPLOSION_ENTITY;
+    public Component FLAG_DESCRIPTION_INTERACT_BLOCK_PRIMARY;
+    public Component FLAG_DESCRIPTION_INTERACT_BLOCK_SECONDARY;
+    public Component FLAG_DESCRIPTION_INTERACT_ENTITY_PRIMARY;
+    public Component FLAG_DESCRIPTION_INTERACT_ENTITY_SECONDARY;
+    public Component FLAG_DESCRIPTION_INTERACT_INVENTORY;
+    public Component FLAG_DESCRIPTION_INTERACT_ITEM_PRIMARY;
+    public Component FLAG_DESCRIPTION_INTERACT_ITEM_SECONDARY;
+    public Component FLAG_DESCRIPTION_INTERACT_INVENTORY_CLICK;
+    public Component FLAG_DESCRIPTION_ITEM_DROP;
+    public Component FLAG_DESCRIPTION_ITEM_PICKUP;
+    public Component FLAG_DESCRIPTION_ITEM_SPAWN;
+    public Component FLAG_DESCRIPTION_ITEM_USE;
+    public Component FLAG_DESCRIPTION_LEAF_DECAY;
+    public Component FLAG_DESCRIPTION_LIQUID_FLOW;
+    public Component FLAG_DESCRIPTION_PORTAL_USE;
+    public Component FLAG_DESCRIPTION_PROJECTILE_IMPACT_BLOCK;
+    public Component FLAG_DESCRIPTION_PROJECTILE_IMPACT_ENTITY;
+    public Component FLAG_RESET_SUCCESS;
+    public Component FLAG_RESET_WARNING;
+    public Component FLAG_UI_CLICK_ALLOW;
+    public Component FLAG_UI_CLICK_DENY;
+    public Component FLAG_UI_CLICK_REMOVE;
+    public Component FLAG_UI_INFO_CLAIM;
+    public Component FLAG_UI_INFO_DEFAULT;
+    public Component FLAG_UI_INFO_INHERIT;
+    public Component FLAG_UI_INFO_OVERRIDE;
+    public Component FLAG_UI_OVERRIDE_NO_PERMISSION;
+    public Component FLAG_UI_RETURN_FLAGS;
+    public Component LABEL_ACCESSORS;
+    public Component LABEL_AREA;
+    public Component LABEL_BLOCKS;
+    public Component LABEL_BUILDERS;
+    public Component LABEL_BUY;
+    public Component LABEL_CHILDREN;
+    public Component LABEL_CONFIRM;
+    public Component LABEL_CONTAINERS;
+    public Component LABEL_CONTEXT;
+    public Component LABEL_CREATED;
+    public Component LABEL_DISPLAYING;
+    public Component LABEL_EXPIRED;
+    public Component LABEL_FAREWELL;
+    public Component LABEL_FLAG;
+    public Component LABEL_GREETING;
+    public Component LABEL_GROUP;
+    public Component LABEL_INHERIT;
+    public Component LABEL_LOCATION;
+    public Component LABEL_MANAGERS;
+    public Component LABEL_NAME;
+    public Component LABEL_NO;
+    public Component LABEL_OUTPUT;
+    public Component LABEL_OWNER;
+    public Component LABEL_PERMISSION;
+    public Component LABEL_PLAYER;
+    public Component LABEL_PRICE;
+    public Component LABEL_RAID;
+    public Component LABEL_RESIZABLE;
+    public Component LABEL_RESULT;
+    public Component LABEL_SCHEMATIC;
+    public Component LABEL_SOURCE;
+    public Component LABEL_SPAWN;
+    public Component LABEL_TARGET;
+    public Component LABEL_TRUST;
+    public Component LABEL_TYPE;
+    public Component LABEL_WORLD;
+    public Component LABEL_UNKNOWN;
+    public Component LABEL_USER;
+    public Component LABEL_YES;
+    public Component MODE_ADMIN;
+    public Component MODE_BASIC;
+    public Component MODE_NATURE;
+    public Component MODE_SUBDIVISION;
+    public Component MODE_TOWN;
+    public Component OPTION_DESCRIPTION_ABANDON_DELAY;
+    public Component OPTION_DESCRIPTION_ABANDON_RETURN_RATIO;
+    public Component OPTION_DESCRIPTION_BLOCKS_ACCRUED_PER_HOUR;
+    public Component OPTION_DESCRIPTION_CHEST_EXPIRATION;
+    public Component OPTION_DESCRIPTION_CREATE_LIMIT;
+    public Component OPTION_DESCRIPTION_CREATE_MODE;
+    public Component OPTION_DESCRIPTION_ECONOMY_BLOCK_COST;
+    public Component OPTION_DESCRIPTION_ECONOMY_BLOCK_SELL_RETURN;
+    public Component OPTION_DESCRIPTION_EXPIRATION;
+    public Component OPTION_DESCRIPTION_INITIAL_BLOCKS;
+    public Component OPTION_DESCRIPTION_MAX_ACCRUED_BLOCKS;
+    public Component OPTION_DESCRIPTION_MAX_LEVEL;
+    public Component OPTION_DESCRIPTION_MAX_SIZE_X;
+    public Component OPTION_DESCRIPTION_MAX_SIZE_Y;
+    public Component OPTION_DESCRIPTION_MAX_SIZE_Z;
+    public Component OPTION_DESCRIPTION_MIN_LEVEL;
+    public Component OPTION_DESCRIPTION_MIN_SIZE_X;
+    public Component OPTION_DESCRIPTION_MIN_SIZE_Y;
+    public Component OPTION_DESCRIPTION_MIN_SIZE_Z;
+    public Component OPTION_DESCRIPTION_PLAYER_COMMAND;
+    public Component OPTION_DESCRIPTION_PLAYER_DENY_FLIGHT;
+    public Component OPTION_DESCRIPTION_PLAYER_DENY_GODMODE;
+    public Component OPTION_DESCRIPTION_PLAYER_DENY_HUNGER;
+    public Component OPTION_DESCRIPTION_PLAYER_GAMEMODE;
+    public Component OPTION_DESCRIPTION_PLAYER_HEALTH_REGEN;
+    public Component OPTION_DESCRIPTION_PLAYER_KEEP_INVENTORY;
+    public Component OPTION_DESCRIPTION_PLAYER_KEEP_LEVEL;
+    public Component OPTION_DESCRIPTION_PLAYER_WALK_SPEED;
+    public Component OPTION_DESCRIPTION_PLAYER_WEATHER;
+    public Component OPTION_DESCRIPTION_RADIUS_LIST;
+    public Component OPTION_DESCRIPTION_RADIUS_INSPECT;
+    public Component OPTION_DESCRIPTION_TAX_EXPIRATION;
+    public Component OPTION_DESCRIPTION_TAX_EXPIRATION_DAYS_KEEP;
+    public Component OPTION_DESCRIPTION_TAX_RATE;
+    public Component OPTION_PLAYER_DENY_FLIGHT;
+    public Component OWNER_ADMIN;
+    public Component PERMISSION_ASSIGN_WITHOUT_HAVING;
+    public Component PERMISSION_CLAIM_CREATE;
+    public Component PERMISSION_CLAIM_ENTER;
+    public Component PERMISSION_CLAIM_EXIT;
+    public Component PERMISSION_CLAIM_LIST;
+    public Component PERMISSION_CLAIM_RESET_FLAGS_SELF;
+    public Component PERMISSION_CLAIM_RESIZE;
+    public Component PERMISSION_CLAIM_SALE;
+    public Component PERMISSION_CLAIM_TRANSFER_ADMIN;
+    public Component PERMISSION_CLEAR;
+    public Component PERMISSION_CLEAR_ALL;
+    public Component PERMISSION_COMMAND_TRUST;
+    public Component PERMISSION_CUBOID;
+    public Component PERMISSION_EDIT_CLAIM;
+    public Component PERMISSION_FIRE_SPREAD;
+    public Component PERMISSION_FLAG_DEFAULTS;
+    public Component PERMISSION_FLAG_OVERRIDES;
+    public Component PERMISSION_FLAG_USE;
+    public Component PERMISSION_FLOW_LIQUID;
+    public Component PERMISSION_GLOBAL_OPTION;
+    public Component PERMISSION_GRANT;
+    public Component PERMISSION_GROUP_OPTION;
+    public Component PERMISSION_OPTION_DEFAULTS;
+    public Component PERMISSION_OPTION_OVERRIDES;
+    public Component PERMISSION_OPTION_USE;
+    public Component PERMISSION_OVERRIDE_DENY;
+    public Component PERMISSION_PLAYER_ADMIN_FLAGS;
+    public Component PERMISSION_PLAYER_OPTION;
+    public Component PERMISSION_PLAYER_VIEW_OTHERS;
+    public Component PERMISSION_VISUAL_CLAIMS_NEARBY;
+    public Component PLAYERINFO_UI_TITLE;
+    public Component PLUGIN_EVENT_CANCEL;
+    public Component PLUGIN_RELOAD;
+    public Component PVP_CLAIM_NOT_ALLOWED;
+    public Component PVP_SOURCE_NOT_ALLOWED;
+    public Component PVP_TARGET_NOT_ALLOWED;
+    public Component RESIZE_OVERLAP;
+    public Component RESIZE_OVERLAP_SUBDIVISION;
+    public Component RESIZE_SAME_LOCATION;
+    public Component RESIZE_START;
+    public Component SCHEMATIC_ABANDON_ALL_RESTORE_WARNING;
+    public Component SCHEMATIC_ABANDON_RESTORE_WARNING;
+    public Component SCHEMATIC_CREATE;
+    public Component SCHEMATIC_CREATE_COMPLETE;
+    public Component SCHEMATIC_CREATE_FAIL;
+    public Component SCHEMATIC_NONE;
+    public Component SPAWN_NOT_SET;
+    public Component TELEPORT_MOVE_CANCEL;
+    public Component TELEPORT_NO_SAFE_LOCATION;
+    public Component TITLE_ACCESSOR;
+    public Component TITLE_ALL;
+    public Component TITLE_BUILDER;
+    public Component TITLE_CLAIM;
+    public Component TITLE_CONTAINER;
+    public Component TITLE_DEFAULT;
+    public Component TITLE_INHERIT;
+    public Component TITLE_MANAGER;
+    public Component TITLE_OWN;
+    public Component TITLE_OVERRIDE;
+    public Component TOWN_CHAT_DISABLED;
+    public Component TOWN_CHAT_ENABLED;
+    public Component TOWN_NOT_FOUND;
+    public Component TOWN_NOT_IN;
+    public Component TOWN_OWNER;
+    public Component TOWN_TAG_CLEAR;
+    public Component TOWN_TAX_NO_CLAIMS;
+    public Component TRUST_CLICK_SHOW_LIST;
+    public Component TRUST_INVALID;
+    public Component TRUST_LIST_HEADER;
+    public Component TRUST_NO_CLAIMS;
+    public Component TRUST_SELF;
+    public Component UI_CLICK_CONFIRM;
+    public Component UNTRUST_NO_CLAIMS;
+    public Component UNTRUST_SELF;
+
+    public void loadCache() {
+        ABANDON_ALL_DELAY_WARNING = MessageStorage.MESSAGE_DATA.getMessage("abandon-all-delay-warning");
+        ABANDON_ALL_WARNING = MessageStorage.MESSAGE_DATA.getMessage("abandon-all-warning");
+        ABANDON_CLAIM_MISSING = MessageStorage.MESSAGE_DATA.getMessage("abandon-claim-missing");
+        ABANDON_TOP_LEVEL = MessageStorage.MESSAGE_DATA.getMessage("abandon-top-level");
+        ABANDON_TOWN_CHILDREN = MessageStorage.MESSAGE_DATA.getMessage("abandon-town-children");
+        ABANDON_WARNING = MessageStorage.MESSAGE_DATA.getMessage("abandon-warning");
+        BANK_CLICK_VIEW_TRANSACTIONS = MessageStorage.MESSAGE_DATA.getMessage("bank-click-view-transactions");
+        BANK_DEPOSIT_NO_FUNDS = MessageStorage.MESSAGE_DATA.getMessage("bank-deposit-no-funds");
+        BANK_TAX_SYSTEM_DISABLED = MessageStorage.MESSAGE_DATA.getMessage("bank-tax-system-disabled");
+        BANK_TITLE_TRANSACTIONS = MessageStorage.MESSAGE_DATA.getMessage("bank-title-transactions");
+        CLAIM_AUTOMATIC_NOTIFICATION = MessageStorage.MESSAGE_DATA.getMessage("claim-automatic-notification");
+        CLAIM_CHEST_CONFIRMATION = MessageStorage.MESSAGE_DATA.getMessage("claim-chest-confirmation");
+        CLAIM_CHILDREN_WARNING = MessageStorage.MESSAGE_DATA.getMessage("claim-children-warning");
+        CLAIM_DISABLED_WORLD = MessageStorage.MESSAGE_DATA.getMessage("claim-disabled-world");
+        CLAIM_FAREWELL_CLEAR = MessageStorage.MESSAGE_DATA.getMessage("claim-farewell-clear");
+        CLAIM_GREETING_CLEAR = MessageStorage.MESSAGE_DATA.getMessage("claim-greeting-clear");
+        CLAIM_IGNORE = MessageStorage.MESSAGE_DATA.getMessage("claim-ignore");
+        CLAIM_NO_CLAIMS = MessageStorage.MESSAGE_DATA.getMessage("claim-no-claims");
+        CLAIM_NOT_FOUND = MessageStorage.MESSAGE_DATA.getMessage("claim-not-found");
+        CLAIM_NOT_YOURS = MessageStorage.MESSAGE_DATA.getMessage("claim-not-yours");
+        CLAIM_OWNER_ALREADY = MessageStorage.MESSAGE_DATA.getMessage("claim-owner-already");
+        CLAIM_OWNER_ONLY = MessageStorage.MESSAGE_DATA.getMessage("claim-owner-only");
+        CLAIM_RESPECTING = MessageStorage.MESSAGE_DATA.getMessage("claim-respecting");
+        CLAIM_RESTORE_SUCCESS = MessageStorage.MESSAGE_DATA.getMessage("claim-restore-success");
+        CLAIM_TOO_FAR = MessageStorage.MESSAGE_DATA.getMessage("claim-too-far");
+        CLAIMINFO_UI_ADMIN_SETTINGS = MessageStorage.MESSAGE_DATA.getMessage("claiminfo-ui-admin-settings");
+        CLAIMINFO_UI_BANK_INFO = MessageStorage.MESSAGE_DATA.getMessage("claiminfo-ui-bank-info");
+        CLAIMINFO_UI_CLAIM_EXPIRATION = MessageStorage.MESSAGE_DATA.getMessage("claiminfo-ui-claim-expiration");
+        CLAIMINFO_UI_CLICK_ADMIN = MessageStorage.MESSAGE_DATA.getMessage("claiminfo-ui-click-admin");
+        CLAIMINFO_UI_CLICK_BANK = MessageStorage.MESSAGE_DATA.getMessage("claiminfo-ui-click-bank");
+        CLAIMINFO_UI_CLICK_TOGGLE = MessageStorage.MESSAGE_DATA.getMessage("claiminfo-ui-click-toggle");
+        CLAIMINFO_UI_DENY_MESSAGES = MessageStorage.MESSAGE_DATA.getMessage("claiminfo-ui-deny-messages");
+        CLAIMINFO_UI_FLAG_OVERRIDES = MessageStorage.MESSAGE_DATA.getMessage("claiminfo-ui-flag-overrides");
+        CLAIMINFO_UI_FOR_SALE = MessageStorage.MESSAGE_DATA.getMessage("claiminfo-ui-for-sale");
+        CLAIMINFO_UI_INHERIT_PARENT = MessageStorage.MESSAGE_DATA.getMessage("claiminfo-ui-inherit-parent");
+        CLAIMINFO_UI_LAST_ACTIVE = MessageStorage.MESSAGE_DATA.getMessage("claiminfo-ui-last-active");
+        CLAIMINFO_UI_NORTH_CORNERS = MessageStorage.MESSAGE_DATA.getMessage("claiminfo-ui-north-corners");
+        CLAIMINFO_UI_PVP_OVERRIDES = MessageStorage.MESSAGE_DATA.getMessage("claiminfo-ui-pvp-overrides");
+        CLAIMINFO_UI_REQUIRES_CLAIM_BLOCKS = MessageStorage.MESSAGE_DATA.getMessage("claiminfo-ui-requires-claim-blocks");
+        CLAIMINFO_UI_RETURN_BANKINFO = MessageStorage.MESSAGE_DATA.getMessage("claiminfo-ui-return-bankinfo");
+        CLAIMINFO_UI_RETURN_CLAIMINFO = MessageStorage.MESSAGE_DATA.getMessage("claiminfo-ui-return-claiminfo");
+        CLAIMINFO_UI_RETURN_SETTINGS = MessageStorage.MESSAGE_DATA.getMessage("claiminfo-ui-return-settings");
+        CLAIMINFO_UI_SIZE_RESTRICTIONS = MessageStorage.MESSAGE_DATA.getMessage("claiminfo-ui-size-restrictions");
+        CLAIMINFO_UI_SOUTH_CORNERS = MessageStorage.MESSAGE_DATA.getMessage("claiminfo-ui-south-corners");
+        CLAIMINFO_UI_TELEPORT_FEATURE = MessageStorage.MESSAGE_DATA.getMessage("claiminfo-ui-teleport-feature");
+        CLAIMINFO_UI_TELEPORT_SPAWN = MessageStorage.MESSAGE_DATA.getMessage("claiminfo-ui-teleport-spawn");
+        CLAIMINFO_UI_TITLE_CLAIMINFO = MessageStorage.MESSAGE_DATA.getMessage("claiminfo-ui-title-claiminfo");
+        CLAIMINFO_UI_TOWN_SETTINGS = MessageStorage.MESSAGE_DATA.getMessage("claiminfo-ui-town-settings");
+        CLAIMLIST_UI_CLICK_INFO = MessageStorage.MESSAGE_DATA.getMessage("claimlist-ui-click-info");
+        CLAIMLIST_UI_CLICK_PURCHASE = MessageStorage.MESSAGE_DATA.getMessage("claimlist-ui-click-purchase");
+        CLAIMLIST_UI_CLICK_VIEW_CHILDREN = MessageStorage.MESSAGE_DATA.getMessage("claimlist-ui-click-view-children");
+        CLAIMLIST_UI_CLICK_VIEW_CLAIMS = MessageStorage.MESSAGE_DATA.getMessage("claimlist-ui-click-view-claims");
+        CLAIMLIST_UI_NO_CLAIMS_FOUND = MessageStorage.MESSAGE_DATA.getMessage("claimlist-ui-no-claims-found");
+        CLAIMLIST_UI_RETURN_CLAIMSLIST = MessageStorage.MESSAGE_DATA.getMessage("claimlist-ui-return-claimlist");
+        CLAIMLIST_UI_TITLE = MessageStorage.MESSAGE_DATA.getMessage("claimlist-ui-title");
+        CLAIMLIST_UI_TITLE_CHILD_CLAIMS = MessageStorage.MESSAGE_DATA.getMessage("claimlist-ui-title-child-claims");
+        COMMAND_CLAIMBUY_TITLE = MessageStorage.MESSAGE_DATA.getMessage("command-claimbuy-title");
+        COMMAND_CLAIMCLEAR_UUID_DENY = MessageStorage.MESSAGE_DATA.getMessage("command-claimclear-uuid-deny");
+        COMMAND_CLAIMFLAGDEBUG_DISABLED = MessageStorage.MESSAGE_DATA.getMessage("command-claimflagdebug-disabled");
+        COMMAND_CLAIMFLAGDEBUG_ENABLED = MessageStorage.MESSAGE_DATA.getMessage("command-claimflagdebug-enabled");
+        COMMAND_CLAIMINFO_NOT_FOUND = MessageStorage.MESSAGE_DATA.getMessage("command-claiminfo-not-found");
+        COMMAND_CLAIMINFO_UUID_REQUIRED = MessageStorage.MESSAGE_DATA.getMessage("command-claiminfo-uuid-required");
+        COMMAND_CLAIMINHERIT_DISABLED = MessageStorage.MESSAGE_DATA.getMessage("command-claiminherit-disabled");
+        COMMAND_CLAIMINHERIT_ENABLED = MessageStorage.MESSAGE_DATA.getMessage("command-claiminherit-enabled");
+        COMMAND_CLAIMMODE_DISABLED = MessageStorage.MESSAGE_DATA.getMessage("command-claimmode-disabled");
+        COMMAND_CLAIMMODE_ENABLED = MessageStorage.MESSAGE_DATA.getMessage("command-claimmode-enabled");
+        COMMAND_CUBOID_DISABLED = MessageStorage.MESSAGE_DATA.getMessage("command-cuboid-disabled");
+        COMMAND_CUBOID_ENABLED = MessageStorage.MESSAGE_DATA.getMessage("command-cuboid-enabled");
+        COMMAND_INHERIT_ONLY_CHILD = MessageStorage.MESSAGE_DATA.getMessage("command-inherit-only-child");
+        COMMAND_INVALID = MessageStorage.MESSAGE_DATA.getMessage("command-invalid");
+        COMMAND_INVALID_PLAYER_GROUP = MessageStorage.MESSAGE_DATA.getMessage("command-invalid-player-group");
+        COMMAND_NOT_AVAILABLE_ECONOMY = MessageStorage.MESSAGE_DATA.getMessage("command-not-available-economy");
+        COMMAND_PET_CONFIRMATION = MessageStorage.MESSAGE_DATA.getMessage("command-pet-confirmation");
+        COMMAND_PET_TRANSFER_READY = MessageStorage.MESSAGE_DATA.getMessage("command-pet-transfer-ready");
+        COMMAND_PET_TRANSFER_CANCEL = MessageStorage.MESSAGE_DATA.getMessage("command-pet-transfer-cancel");
+        COMMAND_WORLDEDIT_MISSING = MessageStorage.MESSAGE_DATA.getMessage("command-worldedit-missing");
+        CREATE_CANCEL = MessageStorage.MESSAGE_DATA.getMessage("create-cancel");
+        CREATE_CUBOID_DISABLED = MessageStorage.MESSAGE_DATA.getMessage("create-cuboid-disabled");
+        CREATE_OVERLAP = MessageStorage.MESSAGE_DATA.getMessage("create-overlap");
+        CREATE_OVERLAP_SHORT = MessageStorage.MESSAGE_DATA.getMessage("create-overlap-short");
+        CREATE_SUBDIVISION_FAIL = MessageStorage.MESSAGE_DATA.getMessage("create-subdivision-fail");
+        CREATE_SUBDIVISION_ONLY = MessageStorage.MESSAGE_DATA.getMessage("create-subdivision-only");
+        DEBUG_NO_RECORDS = MessageStorage.MESSAGE_DATA.getMessage("debug-no-records");
+        DEBUG_PASTE_SUCCESS = MessageStorage.MESSAGE_DATA.getMessage("debug-paste-success");
+        DEBUG_RECORD_END = MessageStorage.MESSAGE_DATA.getMessage("debug-record-end");
+        DEBUG_RECORD_START = MessageStorage.MESSAGE_DATA.getMessage("debug-record-start");
+        DEBUG_TIME_ELAPSED = MessageStorage.MESSAGE_DATA.getMessage("debug-time-elapsed");
+        ECONOMY_BLOCK_BUY_INVALID = MessageStorage.MESSAGE_DATA.getMessage("economy-block-buy-invalid");
+        ECONOMY_BLOCK_BUY_SELL_DISABLED = MessageStorage.MESSAGE_DATA.getMessage("economy-block-buy-sell-disabled");
+        ECONOMY_BLOCK_NOT_AVAILABLE = MessageStorage.MESSAGE_DATA.getMessage("economy-block-not-available");
+        ECONOMY_BLOCK_ONLY_BUY = MessageStorage.MESSAGE_DATA.getMessage("economy-block-only-buy");
+        ECONOMY_BLOCK_ONLY_SELL = MessageStorage.MESSAGE_DATA.getMessage("economy-block-only-sell");
+        ECONOMY_CLAIM_NOT_FOR_SALE = MessageStorage.MESSAGE_DATA.getMessage("economy-claim-not-for-sale");
+        ECONOMY_CLAIM_SALE_CANCELLED = MessageStorage.MESSAGE_DATA.getMessage("economy-claim-sale-cancelled");
+        ECONOMY_NOT_INSTALLED = MessageStorage.MESSAGE_DATA.getMessage("economy-not-installed");
+        ECONOMY_VIRTUAL_NOT_SUPPORTED = MessageStorage.MESSAGE_DATA.getMessage("economy-virtual-not-supported");
+        FEATURE_NOT_AVAILABLE = MessageStorage.MESSAGE_DATA.getMessage("feature-not-available");
+        FLAG_DESCRIPTION_CUSTOM_BLOCK_BREAK = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-block-break");
+        FLAG_DESCRIPTION_CUSTOM_BLOCK_GROW = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-block-grow");
+        FLAG_DESCRIPTION_CUSTOM_BLOCK_PLACE = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-block-place");
+        FLAG_DESCRIPTION_CUSTOM_BLOCK_SPREAD = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-block-spread");
+        FLAG_DESCRIPTION_CUSTOM_ENDERPEARL = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-enderpearl");
+        FLAG_DESCRIPTION_CUSTOM_EXIT_PLAYER = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-exit-player");
+        FLAG_DESCRIPTION_CUSTOM_EXPLOSION_BLOCK = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-explosion-block");
+        FLAG_DESCRIPTION_CUSTOM_EXPLOSION_ENTITY = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-explosion-entity");
+        FLAG_DESCRIPTION_CUSTOM_EXP_DROP = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-exp-drop");
+        FLAG_DESCRIPTION_CUSTOM_FALL_DAMAGE = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-fall-damage");
+        FLAG_DESCRIPTION_CUSTOM_INTERACT_BLOCK = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-interact-block");
+        FLAG_DESCRIPTION_CUSTOM_INTERACT_ENTITY = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-interact-entity");
+        FLAG_DESCRIPTION_CUSTOM_INTERACT_INVENTORY = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-interact-inventory");
+        FLAG_DESCRIPTION_CUSTOM_INVINCIBLE = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-invincible");
+        FLAG_DESCRIPTION_CUSTOM_ITEM_DROP = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-item-drop");
+        FLAG_DESCRIPTION_CUSTOM_ITEM_PICKUP = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-item-pickup");
+        FLAG_DESCRIPTION_CUSTOM_MONSTER_DAMAGE = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-monster-damage");
+        FLAG_DESCRIPTION_CUSTOM_PISTONS = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-pistons");
+        FLAG_DESCRIPTION_CUSTOM_PORTAL_USE = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-portal-use");
+        FLAG_DESCRIPTION_CUSTOM_SPAWN_AMBIENT = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-spawn-ambient");
+        FLAG_DESCRIPTION_CUSTOM_SPAWN_ANIMAL = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-spawn-animal");
+        FLAG_DESCRIPTION_CUSTOM_SPAWN_AQUATIC = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-spawn-aquatic");
+        FLAG_DESCRIPTION_CUSTOM_SPAWN_MONSTER = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-spawn-monster");
+        FLAG_DESCRIPTION_CUSTOM_TELEPORT_FROM = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-teleport-from");
+        FLAG_DESCRIPTION_CUSTOM_TELEPORT_TO = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-teleport-to");
+        FLAG_DESCRIPTION_CUSTOM_USE = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-use");
+        FLAG_DESCRIPTION_CUSTOM_VEHICLE_DESTROY = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-vehicle-destroy");
+        FLAG_DESCRIPTION_CUSTOM_WITHER_DAMAGE = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-wither-damage");
+        FLAG_DESCRIPTION_CUSTOM_BLOCK_TRAMPLING = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-block-trampling");
+        FLAG_DESCRIPTION_CUSTOM_CHEST_ACCESS = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-chest-access");
+        FLAG_DESCRIPTION_CUSTOM_CHORUS_FRUIT_TELEPORT = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-chorus-fruit-teleport");
+        FLAG_DESCRIPTION_CUSTOM_CROP_GROWTH = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-crop-growth");
+        FLAG_DESCRIPTION_CUSTOM_DAMAGE_ANIMALS = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-damage-animals");
+        FLAG_DESCRIPTION_CUSTOM_ENDERMAN_GRIEF = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-enderman-grief");
+        FLAG_DESCRIPTION_CUSTOM_ENTER_PLAYER = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-enter-player");
+        FLAG_DESCRIPTION_CUSTOM_EXPLOSION_CREEPER = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-explosion-creeper");
+        FLAG_DESCRIPTION_CUSTOM_EXPLOSION_TNT = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-explosion-tnt");
+        FLAG_DESCRIPTION_CUSTOM_FIRE_DAMAGE = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-fire-damage");
+        FLAG_DESCRIPTION_CUSTOM_FIRE_SPREAD = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-fire-spread");
+        FLAG_DESCRIPTION_CUSTOM_GRASS_GROWTH = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-grass-growth");
+        FLAG_DESCRIPTION_CUSTOM_ICE_FORM = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-ice-form");
+        FLAG_DESCRIPTION_CUSTOM_ICE_MELT = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-ice-melt");
+        FLAG_DESCRIPTION_CUSTOM_LAVA_FLOW = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-lava-flow");
+        FLAG_DESCRIPTION_CUSTOM_LEAF_DECAY = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-leaf-decay");
+        FLAG_DESCRIPTION_CUSTOM_LIGHTNING = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-lightning");
+        FLAG_DESCRIPTION_CUSTOM_LIGHTER = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-lighter");
+        FLAG_DESCRIPTION_CUSTOM_MUSHROOM_GROWTH = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-mushroom-growth");
+        FLAG_DESCRIPTION_CUSTOM_MYCELIUM_SPREAD = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-mycelium-spread");
+        FLAG_DESCRIPTION_CUSTOM_PVP = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-pvp");
+        FLAG_DESCRIPTION_CUSTOM_RIDE = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-ride");
+        FLAG_DESCRIPTION_CUSTOM_SLEEP = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-sleep");
+        FLAG_DESCRIPTION_CUSTOM_SNOW_FALL = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-snow-fall");
+        FLAG_DESCRIPTION_CUSTOM_SNOW_MELT = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-snow-melt");
+        FLAG_DESCRIPTION_CUSTOM_SNOWMAN_TRAIL = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-snowman-trail");
+        FLAG_DESCRIPTION_CUSTOM_SOIL_DRY = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-soil-dry");
+        FLAG_DESCRIPTION_CUSTOM_VEHICLE_PLACE = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-vehicle-place");
+        FLAG_DESCRIPTION_CUSTOM_VINE_GROWTH = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-vine-growth");
+        FLAG_DESCRIPTION_CUSTOM_WATER_FLOW = MessageStorage.MESSAGE_DATA.getMessage("flag-description-custom-water-flow");
+        FLAG_DESCRIPTION_BLOCK_BREAK = MessageStorage.MESSAGE_DATA.getMessage("flag-description-block-break");
+        FLAG_DESCRIPTION_BLOCK_GROW = MessageStorage.MESSAGE_DATA.getMessage("flag-description-block-grow");
+        FLAG_DESCRIPTION_BLOCK_MODIFY = MessageStorage.MESSAGE_DATA.getMessage("flag-description-block-modify");
+        FLAG_DESCRIPTION_BLOCK_PLACE = MessageStorage.MESSAGE_DATA.getMessage("flag-description-block-place");
+        FLAG_DESCRIPTION_BLOCK_SPREAD = MessageStorage.MESSAGE_DATA.getMessage("flag-description-block-spread");
+        FLAG_DESCRIPTION_COLLIDE_BLOCK = MessageStorage.MESSAGE_DATA.getMessage("flag-description-collide-block");
+        FLAG_DESCRIPTION_COLLIDE_ENTITY = MessageStorage.MESSAGE_DATA.getMessage("flag-description-collide-entity");
+        FLAG_DESCRIPTION_COMMAND_EXECUTE = MessageStorage.MESSAGE_DATA.getMessage("flag-description-command-execute");
+        FLAG_DESCRIPTION_COMMAND_EXECUTE_PVP = MessageStorage.MESSAGE_DATA.getMessage("flag-description-command-execute-pvp");
+        FLAG_DESCRIPTION_ENTER_CLAIM = MessageStorage.MESSAGE_DATA.getMessage("flag-description-enter-claim");
+        FLAG_DESCRIPTION_ENTITY_CHUNK_SPAWN = MessageStorage.MESSAGE_DATA.getMessage("flag-description-entity-chunk-spawn");
+        FLAG_DESCRIPTION_ENTITY_DAMAGE = MessageStorage.MESSAGE_DATA.getMessage("flag-description-entity-damage");
+        FLAG_DESCRIPTION_ENTITY_RIDING = MessageStorage.MESSAGE_DATA.getMessage("flag-description-entity-riding");
+        FLAG_DESCRIPTION_ENTITY_SPAWN = MessageStorage.MESSAGE_DATA.getMessage("flag-description-entity-spawn");
+        FLAG_DESCRIPTION_ENTITY_TELEPORT_FROM = MessageStorage.MESSAGE_DATA.getMessage("flag-description-entity-teleport-from");
+        FLAG_DESCRIPTION_ENTITY_TELEPORT_TO = MessageStorage.MESSAGE_DATA.getMessage("flag-description-entity-teleport-to");
+        FLAG_DESCRIPTION_EXIT_CLAIM = MessageStorage.MESSAGE_DATA.getMessage("flag-description-exit-claim");
+        FLAG_DESCRIPTION_EXPLOSION_BLOCK = MessageStorage.MESSAGE_DATA.getMessage("flag-description-explosion-block");
+        FLAG_DESCRIPTION_EXPLOSION_ENTITY = MessageStorage.MESSAGE_DATA.getMessage("flag-description-explosion-entity");
+        FLAG_DESCRIPTION_INTERACT_BLOCK_PRIMARY = MessageStorage.MESSAGE_DATA.getMessage("flag-description-block-primary");
+        FLAG_DESCRIPTION_INTERACT_BLOCK_SECONDARY = MessageStorage.MESSAGE_DATA.getMessage("flag-description-block-secondary");
+        FLAG_DESCRIPTION_INTERACT_ENTITY_PRIMARY = MessageStorage.MESSAGE_DATA.getMessage("flag-description-entity-primary");
+        FLAG_DESCRIPTION_INTERACT_ENTITY_SECONDARY = MessageStorage.MESSAGE_DATA.getMessage("flag-description-entity-secondary");
+        FLAG_DESCRIPTION_INTERACT_ITEM_PRIMARY = MessageStorage.MESSAGE_DATA.getMessage("flag-description-interact-item-primary");
+        FLAG_DESCRIPTION_INTERACT_ITEM_SECONDARY = MessageStorage.MESSAGE_DATA.getMessage("flag-description-interact-item-secondary");
+        FLAG_DESCRIPTION_INTERACT_INVENTORY = MessageStorage.MESSAGE_DATA.getMessage("flag-description-interact-inventory");
+        FLAG_DESCRIPTION_INTERACT_INVENTORY_CLICK = MessageStorage.MESSAGE_DATA.getMessage("flag-description-interact-inventory-click");
+        FLAG_DESCRIPTION_ITEM_DROP = MessageStorage.MESSAGE_DATA.getMessage("flag-description-item-drop");
+        FLAG_DESCRIPTION_ITEM_PICKUP = MessageStorage.MESSAGE_DATA.getMessage("flag-description-item-pickup");
+        FLAG_DESCRIPTION_ITEM_SPAWN = MessageStorage.MESSAGE_DATA.getMessage("flag-description-item-spawn");
+        FLAG_DESCRIPTION_ITEM_USE = MessageStorage.MESSAGE_DATA.getMessage("flag-description-item-use");
+        FLAG_DESCRIPTION_LEAF_DECAY = MessageStorage.MESSAGE_DATA.getMessage("flag-description-leaf-decay");
+        FLAG_DESCRIPTION_LIQUID_FLOW = MessageStorage.MESSAGE_DATA.getMessage("flag-description-liquid-flow");
+        FLAG_DESCRIPTION_PORTAL_USE = MessageStorage.MESSAGE_DATA.getMessage("flag-description-portal-use");
+        FLAG_DESCRIPTION_PROJECTILE_IMPACT_BLOCK = MessageStorage.MESSAGE_DATA.getMessage("flag-description-projectile-impact-block");
+        FLAG_DESCRIPTION_PROJECTILE_IMPACT_ENTITY = MessageStorage.MESSAGE_DATA.getMessage("flag-description-projectile-impact-entity");
+        FLAG_RESET_SUCCESS = MessageStorage.MESSAGE_DATA.getMessage("flag-reset-success");
+        FLAG_RESET_WARNING = MessageStorage.MESSAGE_DATA.getMessage("flag-reset-warning");
+        FLAG_UI_CLICK_ALLOW = MessageStorage.MESSAGE_DATA.getMessage("flag-ui-click-allow");
+        FLAG_UI_CLICK_DENY = MessageStorage.MESSAGE_DATA.getMessage("flag-ui-click-deny");
+        FLAG_UI_CLICK_REMOVE = MessageStorage.MESSAGE_DATA.getMessage("flag-ui-click-remove");
+        FLAG_UI_INFO_CLAIM = MessageStorage.MESSAGE_DATA.getMessage("flag-ui-info-claim");
+        FLAG_UI_INFO_DEFAULT = MessageStorage.MESSAGE_DATA.getMessage("flag-ui-info-default");
+        FLAG_UI_INFO_INHERIT = MessageStorage.MESSAGE_DATA.getMessage("flag-ui-info-inherit");
+        FLAG_UI_INFO_OVERRIDE = MessageStorage.MESSAGE_DATA.getMessage("flag-ui-info-override");
+        FLAG_UI_OVERRIDE_NO_PERMISSION = MessageStorage.MESSAGE_DATA.getMessage("flag-ui-override-no-permission");
+        FLAG_UI_RETURN_FLAGS = MessageStorage.MESSAGE_DATA.getMessage("flag-ui-return-flags");
+        LABEL_ACCESSORS = MessageStorage.MESSAGE_DATA.getMessage("label-accessors");
+        LABEL_AREA = MessageStorage.MESSAGE_DATA.getMessage("label-area");
+        LABEL_BLOCKS = MessageStorage.MESSAGE_DATA.getMessage("label-blocks");
+        LABEL_BUILDERS = MessageStorage.MESSAGE_DATA.getMessage("label-builders");
+        LABEL_BUY = MessageStorage.MESSAGE_DATA.getMessage("label-buy");
+        LABEL_CHILDREN = MessageStorage.MESSAGE_DATA.getMessage("label-children");
+        LABEL_CONFIRM = MessageStorage.MESSAGE_DATA.getMessage("label-confirm");
+        LABEL_CONTAINERS = MessageStorage.MESSAGE_DATA.getMessage("label-containers");
+        LABEL_CONTEXT = MessageStorage.MESSAGE_DATA.getMessage("label-context");
+        LABEL_CREATED = MessageStorage.MESSAGE_DATA.getMessage("label-created");
+        LABEL_DISPLAYING = MessageStorage.MESSAGE_DATA.getMessage("label-displaying");
+        LABEL_EXPIRED = MessageStorage.MESSAGE_DATA.getMessage("label-expired");
+        LABEL_FAREWELL = MessageStorage.MESSAGE_DATA.getMessage("label-farewell");
+        LABEL_FLAG = MessageStorage.MESSAGE_DATA.getMessage("label-flag");
+        LABEL_GREETING = MessageStorage.MESSAGE_DATA.getMessage("label-greeting");
+        LABEL_GROUP = MessageStorage.MESSAGE_DATA.getMessage("label-group");
+        LABEL_INHERIT = MessageStorage.MESSAGE_DATA.getMessage("label-inherit");
+        LABEL_LOCATION = MessageStorage.MESSAGE_DATA.getMessage("label-location");
+        LABEL_MANAGERS = MessageStorage.MESSAGE_DATA.getMessage("label-managers");
+        LABEL_NAME = MessageStorage.MESSAGE_DATA.getMessage("label-name");
+        LABEL_NO = MessageStorage.MESSAGE_DATA.getMessage("label-no");
+        LABEL_OUTPUT = MessageStorage.MESSAGE_DATA.getMessage("label-output");
+        LABEL_OWNER = MessageStorage.MESSAGE_DATA.getMessage("label-owner");
+        LABEL_PERMISSION = MessageStorage.MESSAGE_DATA.getMessage("label-permission");
+        LABEL_PLAYER = MessageStorage.MESSAGE_DATA.getMessage("label-player");
+        LABEL_PRICE = MessageStorage.MESSAGE_DATA.getMessage("label-price");
+        LABEL_RAID = MessageStorage.MESSAGE_DATA.getMessage("label-raid");
+        LABEL_RESIZABLE = MessageStorage.MESSAGE_DATA.getMessage("label-resizable");
+        LABEL_RESULT = MessageStorage.MESSAGE_DATA.getMessage("label-result");
+        LABEL_SCHEMATIC = MessageStorage.MESSAGE_DATA.getMessage("label-schematic");
+        LABEL_SOURCE = MessageStorage.MESSAGE_DATA.getMessage("label-source");
+        LABEL_SPAWN = MessageStorage.MESSAGE_DATA.getMessage("label-spawn");
+        LABEL_TARGET = MessageStorage.MESSAGE_DATA.getMessage("label-target");
+        LABEL_TRUST = MessageStorage.MESSAGE_DATA.getMessage("label-trust");
+        LABEL_TYPE = MessageStorage.MESSAGE_DATA.getMessage("label-type");
+        LABEL_UNKNOWN = MessageStorage.MESSAGE_DATA.getMessage("label-unknown");
+        LABEL_USER = MessageStorage.MESSAGE_DATA.getMessage("label-user");
+        LABEL_WORLD = MessageStorage.MESSAGE_DATA.getMessage("label-world");
+        LABEL_YES = MessageStorage.MESSAGE_DATA.getMessage("label-yes");
+        MODE_ADMIN = MessageStorage.MESSAGE_DATA.getMessage("mode-admin");
+        MODE_BASIC = MessageStorage.MESSAGE_DATA.getMessage("mode-basic");
+        MODE_NATURE = MessageStorage.MESSAGE_DATA.getMessage("mode-nature");
+        MODE_SUBDIVISION = MessageStorage.MESSAGE_DATA.getMessage("mode-subdivision");
+        MODE_TOWN = MessageStorage.MESSAGE_DATA.getMessage("mode-town");
+        OPTION_DESCRIPTION_ABANDON_DELAY = MessageStorage.MESSAGE_DATA.getMessage("option-description-abandon-delay");
+        OPTION_DESCRIPTION_ABANDON_RETURN_RATIO = MessageStorage.MESSAGE_DATA.getMessage("option-description-abandon-return-ratio");
+        OPTION_DESCRIPTION_BLOCKS_ACCRUED_PER_HOUR = MessageStorage.MESSAGE_DATA.getMessage("option-description-blocks-accrued-per-hour");
+        OPTION_DESCRIPTION_CHEST_EXPIRATION = MessageStorage.MESSAGE_DATA.getMessage("option-description-chest-expiration");
+        OPTION_DESCRIPTION_CREATE_LIMIT = MessageStorage.MESSAGE_DATA.getMessage("option-description-create-limit");
+        OPTION_DESCRIPTION_CREATE_MODE = MessageStorage.MESSAGE_DATA.getMessage("option-description-create-mode");
+        OPTION_DESCRIPTION_ECONOMY_BLOCK_COST = MessageStorage.MESSAGE_DATA.getMessage("option-description-economy-block-cost");
+        OPTION_DESCRIPTION_ECONOMY_BLOCK_SELL_RETURN = MessageStorage.MESSAGE_DATA.getMessage("option-description-economy-block-sell-return");
+        OPTION_DESCRIPTION_EXPIRATION = MessageStorage.MESSAGE_DATA.getMessage("option-description-expiration");
+        OPTION_DESCRIPTION_INITIAL_BLOCKS = MessageStorage.MESSAGE_DATA.getMessage("option-description-initial-blocks");
+        OPTION_DESCRIPTION_MAX_ACCRUED_BLOCKS = MessageStorage.MESSAGE_DATA.getMessage("option-description-max-accrued-blocks");
+        OPTION_DESCRIPTION_MAX_LEVEL = MessageStorage.MESSAGE_DATA.getMessage("option-description-max-level");
+        OPTION_DESCRIPTION_MAX_SIZE_X = MessageStorage.MESSAGE_DATA.getMessage("option-description-max-size-x");
+        OPTION_DESCRIPTION_MAX_SIZE_Y = MessageStorage.MESSAGE_DATA.getMessage("option-description-max-size-y");
+        OPTION_DESCRIPTION_MAX_SIZE_Z = MessageStorage.MESSAGE_DATA.getMessage("option-description-max-size-z");
+        OPTION_DESCRIPTION_MIN_LEVEL = MessageStorage.MESSAGE_DATA.getMessage("option-description-min-level");
+        OPTION_DESCRIPTION_MIN_SIZE_X = MessageStorage.MESSAGE_DATA.getMessage("option-description-min-size-x");
+        OPTION_DESCRIPTION_MIN_SIZE_Y = MessageStorage.MESSAGE_DATA.getMessage("option-description-min-size-y");
+        OPTION_DESCRIPTION_MIN_SIZE_Z = MessageStorage.MESSAGE_DATA.getMessage("option-description-min-size-z");
+        OPTION_DESCRIPTION_PLAYER_COMMAND = MessageStorage.MESSAGE_DATA.getMessage("option-description-player-command");
+        OPTION_DESCRIPTION_PLAYER_DENY_FLIGHT = MessageStorage.MESSAGE_DATA.getMessage("option-description-player-deny-flight");
+        OPTION_DESCRIPTION_PLAYER_DENY_GODMODE = MessageStorage.MESSAGE_DATA.getMessage("option-description-player-deny-godmode");
+        OPTION_DESCRIPTION_PLAYER_DENY_HUNGER = MessageStorage.MESSAGE_DATA.getMessage("option-description-player-deny-hunger");
+        OPTION_DESCRIPTION_PLAYER_GAMEMODE = MessageStorage.MESSAGE_DATA.getMessage("option-description-player-gamemode");
+        OPTION_DESCRIPTION_PLAYER_HEALTH_REGEN = MessageStorage.MESSAGE_DATA.getMessage("option-description-player-health-regen");
+        OPTION_DESCRIPTION_PLAYER_KEEP_INVENTORY = MessageStorage.MESSAGE_DATA.getMessage("option-description-player-keep-inventory");
+        OPTION_DESCRIPTION_PLAYER_KEEP_LEVEL = MessageStorage.MESSAGE_DATA.getMessage("option-description-player-keep-level");
+        OPTION_DESCRIPTION_PLAYER_WALK_SPEED = MessageStorage.MESSAGE_DATA.getMessage("option-description-player-walk-speed");
+        OPTION_DESCRIPTION_PLAYER_WEATHER = MessageStorage.MESSAGE_DATA.getMessage("option-description-player-weather");
+        OPTION_DESCRIPTION_RADIUS_LIST = MessageStorage.MESSAGE_DATA.getMessage("option-description-radius-list");
+        OPTION_DESCRIPTION_RADIUS_INSPECT = MessageStorage.MESSAGE_DATA.getMessage("option-description-radius-inspect");
+        OPTION_DESCRIPTION_TAX_EXPIRATION = MessageStorage.MESSAGE_DATA.getMessage("option-description-tax-expiration");
+        OPTION_DESCRIPTION_TAX_EXPIRATION_DAYS_KEEP = MessageStorage.MESSAGE_DATA.getMessage("option-description-tax-expiration-days-keep");
+        OPTION_DESCRIPTION_TAX_RATE = MessageStorage.MESSAGE_DATA.getMessage("option-description-tax-rate");
+        OPTION_PLAYER_DENY_FLIGHT = MessageStorage.MESSAGE_DATA.getMessage("option-player-deny-flight");
+        OWNER_ADMIN = MessageStorage.MESSAGE_DATA.getMessage("owner-admin");
+        PERMISSION_ASSIGN_WITHOUT_HAVING = MessageStorage.MESSAGE_DATA.getMessage("permission-assign-without-having");
+        PERMISSION_CLAIM_CREATE = MessageStorage.MESSAGE_DATA.getMessage("permission-claim-create");
+        PERMISSION_CLAIM_ENTER = MessageStorage.MESSAGE_DATA.getMessage("permission-claim-enter");
+        PERMISSION_CLAIM_EXIT = MessageStorage.MESSAGE_DATA.getMessage("permission-claim-exit");
+        PERMISSION_CLAIM_LIST = MessageStorage.MESSAGE_DATA.getMessage("permission-claim-list");
+        PERMISSION_CLAIM_RESET_FLAGS_SELF = MessageStorage.MESSAGE_DATA.getMessage("permission-claim-reset-flags-self");
+        PERMISSION_CLAIM_RESIZE = MessageStorage.MESSAGE_DATA.getMessage("permission-claim-resize");
+        PERMISSION_CLAIM_SALE = MessageStorage.MESSAGE_DATA.getMessage("permission-claim-sale");
+        PERMISSION_CLAIM_TRANSFER_ADMIN = MessageStorage.MESSAGE_DATA.getMessage("permission-claim-transfer-admin");
+        PERMISSION_CLEAR = MessageStorage.MESSAGE_DATA.getMessage("permission-clear");
+        PERMISSION_CLEAR_ALL = MessageStorage.MESSAGE_DATA.getMessage("permission-clear-all");
+        PERMISSION_COMMAND_TRUST = MessageStorage.MESSAGE_DATA.getMessage("permission-command-trust");
+        PERMISSION_CUBOID = MessageStorage.MESSAGE_DATA.getMessage("permission-cuboid");
+        PERMISSION_EDIT_CLAIM = MessageStorage.MESSAGE_DATA.getMessage("permission-edit-claim");
+        PERMISSION_FIRE_SPREAD = MessageStorage.MESSAGE_DATA.getMessage("permission-fire-spread");
+        PERMISSION_FLAG_DEFAULTS = MessageStorage.MESSAGE_DATA.getMessage("permission-flag-defaults");
+        PERMISSION_FLAG_OVERRIDES = MessageStorage.MESSAGE_DATA.getMessage("permission-flag-overrides");
+        PERMISSION_FLAG_USE = MessageStorage.MESSAGE_DATA.getMessage("permission-flag-use");
+        PERMISSION_FLOW_LIQUID = MessageStorage.MESSAGE_DATA.getMessage("permission-flow-liquid");
+        PERMISSION_GLOBAL_OPTION = MessageStorage.MESSAGE_DATA.getMessage("permission-global-option");
+        PERMISSION_GRANT = MessageStorage.MESSAGE_DATA.getMessage("permission-grant");
+        PERMISSION_GROUP_OPTION = MessageStorage.MESSAGE_DATA.getMessage("permission-group-option");
+        PERMISSION_OPTION_DEFAULTS = MessageStorage.MESSAGE_DATA.getMessage("permission-option-defaults");
+        PERMISSION_OPTION_OVERRIDES = MessageStorage.MESSAGE_DATA.getMessage("permission-option-overrides");
+        PERMISSION_OPTION_USE = MessageStorage.MESSAGE_DATA.getMessage("permission-option-use");
+        PERMISSION_OVERRIDE_DENY = MessageStorage.MESSAGE_DATA.getMessage("permission-override-deny");
+        PERMISSION_PLAYER_ADMIN_FLAGS = MessageStorage.MESSAGE_DATA.getMessage("permission-player-admin-flags");
+        PERMISSION_PLAYER_OPTION = MessageStorage.MESSAGE_DATA.getMessage("permission-player-option");
+        PERMISSION_PLAYER_VIEW_OTHERS = MessageStorage.MESSAGE_DATA.getMessage("permission-player-view-others");
+        PERMISSION_VISUAL_CLAIMS_NEARBY = MessageStorage.MESSAGE_DATA.getMessage("permission-visual-claims-nearby");
+        PLAYERINFO_UI_TITLE = MessageStorage.MESSAGE_DATA.getMessage("playerinfo-ui-title");
+        PLUGIN_EVENT_CANCEL = MessageStorage.MESSAGE_DATA.getMessage("plugin-event-cancel");
+        PLUGIN_RELOAD = MessageStorage.MESSAGE_DATA.getMessage("plugin-reload");
+        PVP_CLAIM_NOT_ALLOWED = MessageStorage.MESSAGE_DATA.getMessage("pvp-claim-not-allowed");
+        PVP_SOURCE_NOT_ALLOWED = MessageStorage.MESSAGE_DATA.getMessage("pvp-source-not-allowed");
+        PVP_TARGET_NOT_ALLOWED = MessageStorage.MESSAGE_DATA.getMessage("pvp-target-not-allowed");
+        RESIZE_OVERLAP = MessageStorage.MESSAGE_DATA.getMessage("resize-overlap");
+        RESIZE_OVERLAP_SUBDIVISION = MessageStorage.MESSAGE_DATA.getMessage("resize-overlap-subdivision");
+        RESIZE_SAME_LOCATION = MessageStorage.MESSAGE_DATA.getMessage("resize-same-location");
+        RESIZE_START = MessageStorage.MESSAGE_DATA.getMessage("resize-start");
+        SCHEMATIC_ABANDON_ALL_RESTORE_WARNING = MessageStorage.MESSAGE_DATA.getMessage("schematic-abandon-all-restore-warning");
+        SCHEMATIC_ABANDON_RESTORE_WARNING = MessageStorage.MESSAGE_DATA.getMessage("schematic-abandon-restore-warning");
+        SCHEMATIC_CREATE = MessageStorage.MESSAGE_DATA.getMessage("schematic-create");
+        SCHEMATIC_CREATE_COMPLETE = MessageStorage.MESSAGE_DATA.getMessage("schematic-create-complete");
+        SCHEMATIC_CREATE_FAIL = MessageStorage.MESSAGE_DATA.getMessage("schematic-create-fail");
+        SCHEMATIC_NONE = MessageStorage.MESSAGE_DATA.getMessage("schematic-none");
+        SPAWN_NOT_SET = MessageStorage.MESSAGE_DATA.getMessage("spawn-not-set");
+        TELEPORT_MOVE_CANCEL = MessageStorage.MESSAGE_DATA.getMessage("teleport-move-cancel");
+        TELEPORT_NO_SAFE_LOCATION = MessageStorage.MESSAGE_DATA.getMessage("teleport-no-safe-location");
+        TITLE_ACCESSOR = MessageStorage.MESSAGE_DATA.getMessage("title-accessor");
+        TITLE_ALL = MessageStorage.MESSAGE_DATA.getMessage("title-all");
+        TITLE_BUILDER = MessageStorage.MESSAGE_DATA.getMessage("title-builder");
+        TITLE_CLAIM = MessageStorage.MESSAGE_DATA.getMessage("title-claim");
+        TITLE_CONTAINER = MessageStorage.MESSAGE_DATA.getMessage("title-container");
+        TITLE_DEFAULT = MessageStorage.MESSAGE_DATA.getMessage("title-default");
+        TITLE_INHERIT = MessageStorage.MESSAGE_DATA.getMessage("title-inherit");
+        TITLE_MANAGER = MessageStorage.MESSAGE_DATA.getMessage("title-manager");
+        TITLE_OWN = MessageStorage.MESSAGE_DATA.getMessage("title-own");
+        TITLE_OVERRIDE = MessageStorage.MESSAGE_DATA.getMessage("title-override");
+        TOWN_CHAT_DISABLED = MessageStorage.MESSAGE_DATA.getMessage("town-chat-disabled");
+        TOWN_CHAT_ENABLED = MessageStorage.MESSAGE_DATA.getMessage("town-chat-enabled");
+        TOWN_NOT_FOUND = MessageStorage.MESSAGE_DATA.getMessage("town-not-found");
+        TOWN_NOT_IN = MessageStorage.MESSAGE_DATA.getMessage("town-not-in");
+        TOWN_OWNER = MessageStorage.MESSAGE_DATA.getMessage("town-owner");
+        TOWN_TAG_CLEAR = MessageStorage.MESSAGE_DATA.getMessage("town-tag-clear");
+        TOWN_TAX_NO_CLAIMS = MessageStorage.MESSAGE_DATA.getMessage("town-tax-no-claims");
+        TRUST_CLICK_SHOW_LIST = MessageStorage.MESSAGE_DATA.getMessage("trust-click-show-list");
+        TRUST_INVALID = MessageStorage.MESSAGE_DATA.getMessage("trust-invalid");
+        TRUST_LIST_HEADER = MessageStorage.MESSAGE_DATA.getMessage("trust-list-header");
+        TRUST_NO_CLAIMS = MessageStorage.MESSAGE_DATA.getMessage("trust-no-claims");
+        TRUST_SELF = MessageStorage.MESSAGE_DATA.getMessage("trust-self");
+        UI_CLICK_CONFIRM = MessageStorage.MESSAGE_DATA.getMessage("ui-click-confirm");
+        UNTRUST_NO_CLAIMS = MessageStorage.MESSAGE_DATA.getMessage("untrust-no-claims");
+        UNTRUST_SELF = MessageStorage.MESSAGE_DATA.getMessage("untrust-self");
+
+        // reload Flag/Option caches
+        for (Option option : OptionRegistryModule.getInstance().getAll()) {
+            ((GDOption) option).reloadDescription();
+        }
+        for (Flag flag : FlagRegistryModule.getInstance().getAll()) {
+            ((GDFlag) flag).reloadDescription();
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/cache/PermissionHolderCache.java b/sponge/src/main/java/com/griefdefender/cache/PermissionHolderCache.java
new file mode 100644
index 0000000..a74f417
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/cache/PermissionHolderCache.java
@@ -0,0 +1,154 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.cache;
+
+import com.github.benmanes.caffeine.cache.Cache;
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.Tristate;
+import com.griefdefender.internal.util.NMSUtil;
+import com.griefdefender.permission.GDPermissionGroup;
+import com.griefdefender.permission.GDPermissionHolder;
+import com.griefdefender.permission.GDPermissionUser;
+import com.griefdefender.util.PermissionUtil;
+
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.entity.living.player.User;
+import org.spongepowered.api.service.permission.Subject;
+import org.spongepowered.api.service.user.UserStorageService;
+
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+
+public class PermissionHolderCache {
+
+    private static PermissionHolderCache instance;
+    private final Cache<UUID, GDPermissionUser> userCache = Caffeine.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES)
+            .build();
+    private final Cache<String, GDPermissionGroup> groupCache = Caffeine.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES)
+            .build();
+    private final ConcurrentHashMap<GDPermissionHolder, Cache<Integer, Tristate>> permissionCache = new ConcurrentHashMap<>();
+
+    public GDPermissionUser getOrCreateUser(User user) {
+        if (user == null) {
+            return null;
+        }
+
+        return this.getOrCreateUser(user.getUniqueId());
+    }
+
+    public GDPermissionUser getOrCreateUser(UUID uuid) {
+        if (uuid == null) {
+            return null;
+        }
+        if (uuid.equals(GriefDefenderPlugin.PUBLIC_UUID)) {
+            return GriefDefenderPlugin.PUBLIC_USER;
+        }
+        if (uuid.equals(GriefDefenderPlugin.WORLD_USER_UUID)) {
+            return GriefDefenderPlugin.WORLD_USER;
+        }
+
+        GDPermissionUser holder = this.userCache.getIfPresent(uuid);
+        if (holder != null) {
+            return holder;
+        }
+
+        holder = new GDPermissionUser(uuid);
+        this.userCache.put(uuid, holder);
+        return holder;
+    }
+
+    public GDPermissionUser getOrCreateUser(String username) {
+        if (username == null) {
+            return null;
+        }
+
+        final UUID uuid = PermissionUtil.getInstance().lookupUserUniqueId(username);
+        if (uuid != null) {
+            return this.getOrCreateUser(uuid);
+        }
+        User user = Sponge.getGame().getServiceManager().provide(UserStorageService.class).get().get(username).orElse(null);
+        if (user == null) {
+            user = NMSUtil.getInstance().createUserFromCache(username);
+        }
+        if (user != null) {
+            return this.getOrCreateUser(user);
+        }
+
+        return null;
+    }
+
+    public GDPermissionGroup getOrCreateGroup(String groupName) {
+        if (groupName == null) {
+            return null;
+        }
+        GDPermissionGroup holder = this.groupCache.getIfPresent(groupName);
+        if (holder != null) {
+            return holder;
+        }
+
+        holder = new GDPermissionGroup(groupName);
+        this.groupCache.put(groupName, holder);
+        return holder;
+    }
+
+    public GDPermissionHolder getOrCreateHolder(String identifier) {
+        if (identifier == null) {
+            return null;
+        }
+        UUID uuid = null;
+        try {
+            uuid = UUID.fromString(identifier);
+        } catch (IllegalArgumentException e) {
+            return this.getOrCreateGroup(identifier);
+        }
+
+        return this.getOrCreateUser(uuid);
+    }
+
+    public Cache<Integer, Tristate> getOrCreatePermissionCache(GDPermissionHolder holder) {
+        Cache<Integer, Tristate> cache = this.permissionCache.get(holder);
+        if (cache == null) {
+            cache = Caffeine.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES).build();
+            this.permissionCache.put(holder, cache);
+        }
+        return cache;
+    }
+
+    public void invalidateAllPermissionCache() {
+        for (Cache<Integer, Tristate> cache : this.permissionCache.values()) {
+            cache.invalidateAll();
+        }
+    }
+
+    static {
+        instance = new PermissionHolderCache();
+    }
+
+    public static PermissionHolderCache getInstance() {
+        return instance;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/claim/ClaimContextCalculator.java b/sponge/src/main/java/com/griefdefender/claim/ClaimContextCalculator.java
new file mode 100644
index 0000000..632cb77
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/claim/ClaimContextCalculator.java
@@ -0,0 +1,87 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.claim;
+
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.service.context.Context;
+import org.spongepowered.api.service.context.ContextCalculator;
+import org.spongepowered.api.service.permission.Subject;
+
+import java.util.Set;
+import java.util.UUID;
+
+public class ClaimContextCalculator implements ContextCalculator<Subject> {
+
+    @Override
+    public void accumulateContexts(Subject calculable, Set<Context> accumulator) {
+        if (calculable.getCommandSource().isPresent() && calculable.getCommandSource().get() instanceof Player) {
+            Player player = (Player) calculable.getCommandSource().get();
+            GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getPlayerData(player.getWorld(), player.getUniqueId());
+            if (playerData == null) {
+                return;
+            }
+            if (playerData.ignoreActiveContexts) {
+                playerData.ignoreActiveContexts = false;
+                return;
+            }
+
+            GDClaim sourceClaim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(playerData, player.getLocation());
+            if (sourceClaim != null) {
+                if (playerData == null || playerData.canIgnoreClaim(sourceClaim)) {
+                    return;
+                }
+
+                if (sourceClaim.parent != null && sourceClaim.getData().doesInheritParent()) {
+                    accumulator.add(sourceClaim.parent.getSpongeContext());
+                } else {
+                    accumulator.add(sourceClaim.getSpongeContext());
+                }
+            }
+        }
+
+    }
+
+    @Override
+    public boolean matches(Context context, Subject subject) {
+        if (context.equals("gd_claim")) {
+            if (subject.getCommandSource().isPresent() && subject.getCommandSource().get() instanceof Player) {
+                Player player = (Player) subject.getCommandSource().get();
+                GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getPlayerData(player.getWorld(), player.getUniqueId());
+                if (playerData == null) {
+                    return false;
+                }
+
+                GDClaim playerClaim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(playerData, player.getLocation());
+                if (playerClaim != null && playerClaim.getUniqueId().equals(UUID.fromString(context.getValue()))) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/claim/GDClaim.java b/sponge/src/main/java/com/griefdefender/claim/GDClaim.java
new file mode 100644
index 0000000..0982268
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/claim/GDClaim.java
@@ -0,0 +1,3111 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.claim;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.flowpowered.math.vector.Vector3i;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.reflect.TypeToken;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.GriefDefender;
+import com.griefdefender.api.Tristate;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.ClaimBlockSystem;
+import com.griefdefender.api.claim.ClaimContexts;
+import com.griefdefender.api.claim.ClaimManager;
+import com.griefdefender.api.claim.ClaimResult;
+import com.griefdefender.api.claim.ClaimResultType;
+import com.griefdefender.api.claim.ClaimSchematic;
+import com.griefdefender.api.claim.ClaimType;
+import com.griefdefender.api.claim.ClaimTypes;
+import com.griefdefender.api.claim.ShovelTypes;
+import com.griefdefender.api.claim.TrustType;
+import com.griefdefender.api.claim.TrustTypes;
+import com.griefdefender.api.data.ClaimData;
+import com.griefdefender.api.event.EventCause;
+import com.griefdefender.api.permission.Context;
+import com.griefdefender.api.permission.option.Options;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.configuration.ClaimDataConfig;
+import com.griefdefender.configuration.ClaimStorageData;
+import com.griefdefender.configuration.IClaimData;
+import com.griefdefender.configuration.MessageDataConfig;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.configuration.TownDataConfig;
+import com.griefdefender.configuration.TownStorageData;
+import com.griefdefender.event.GDCauseStackManager;
+import com.griefdefender.event.GDChangeClaimEvent;
+import com.griefdefender.event.GDCreateClaimEvent;
+import com.griefdefender.event.GDRemoveClaimEvent;
+import com.griefdefender.event.GDGroupTrustClaimEvent;
+import com.griefdefender.event.GDSaveClaimEvent;
+import com.griefdefender.event.GDTransferClaimEvent;
+import com.griefdefender.event.GDUserTrustClaimEvent;
+import com.griefdefender.internal.util.BlockUtil;
+import com.griefdefender.internal.visual.ClaimVisual;
+import com.griefdefender.permission.GDPermissionHolder;
+import com.griefdefender.permission.GDPermissionManager;
+import com.griefdefender.permission.GDPermissionUser;
+import com.griefdefender.permission.GDPermissions;
+import com.griefdefender.registry.TrustTypeRegistryModule;
+import com.griefdefender.storage.BaseStorage;
+import com.griefdefender.storage.FileStorage;
+import com.griefdefender.util.EconomyUtil;
+import com.griefdefender.util.PermissionUtil;
+import com.griefdefender.util.SpongeContexts;
+import com.griefdefender.util.SpongeUtil;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.format.TextColor;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.command.CommandSource;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.entity.living.player.User;
+import org.spongepowered.api.event.cause.Cause;
+import org.spongepowered.api.event.cause.EventContext;
+import org.spongepowered.api.service.economy.Currency;
+import org.spongepowered.api.service.economy.EconomyService;
+import org.spongepowered.api.service.economy.account.Account;
+import org.spongepowered.api.service.economy.transaction.ResultType;
+import org.spongepowered.api.service.economy.transaction.TransactionResult;
+import org.spongepowered.api.world.Chunk;
+import org.spongepowered.api.world.Location;
+import org.spongepowered.api.world.World;
+
+import java.io.File;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.UUID;
+
+public class GDClaim implements Claim {
+
+    public static final BaseStorage DATASTORE = GriefDefenderPlugin.getInstance().dataStore;
+    // Note: 2D cuboids will ignore the upper Y value while 3D cuboids do not
+    public Vector3i lesserBoundaryCorner;
+    public Vector3i greaterBoundaryCorner;
+    private World world;
+    private ClaimType type = ClaimTypes.BASIC;
+    private Set<Long> chunkHashes;
+    private final int hashCode;
+    private final GDClaimManager worldClaimManager;
+    private final Claim wildernessClaim;
+
+    // Permission Context
+    private final Context context;
+    private final Context overrideClaimContext;
+    private final org.spongepowered.api.service.context.Context spongeContext;
+    private final org.spongepowered.api.service.context.Context spongeOverrideClaimContext;
+
+    private UUID id = null;
+    private UUID ownerUniqueId;
+    public boolean cuboid = false;
+    public boolean markVisualDirty = false;
+
+    protected ClaimStorageData claimStorage;
+    protected IClaimData claimData;
+
+    public GDClaim parent = null;
+    public Set<Claim> children = new HashSet<>();
+    public ClaimVisual claimVisual;
+    public List<UUID> playersWatching = new ArrayList<>();
+    public Map<String, ClaimSchematic> schematics = new HashMap<>();
+
+    private GDPlayerData ownerPlayerData;
+    private Account economyAccount;
+    private static final int MAX_AREA = GriefDefenderPlugin.CLAIM_BLOCK_SYSTEM == ClaimBlockSystem.VOLUME ? 2560000 : 10000;
+
+    public GDClaim(World world, Vector3i point1, Vector3i point2, ClaimType type, UUID ownerUniqueId, boolean cuboid) {
+        this(world, point1, point2, type, ownerUniqueId, cuboid, null);
+    }
+
+    public GDClaim(World world, Vector3i point1, Vector3i point2, ClaimType type, UUID ownerUniqueId, boolean cuboid, GDClaim parent) {
+        int minx = Math.min(point1.getX(), point2.getX());
+        int miny = Math.min(point1.getY(), point2.getY());
+        int minz = Math.min(point1.getZ(), point2.getZ());
+        int maxx = Math.max(point1.getX(), point2.getX());
+        int maxy = Math.max(point1.getY(), point2.getY());
+        int maxz = Math.max(point1.getZ(), point2.getZ());
+
+        this.world = world;
+        this.lesserBoundaryCorner = new Vector3i(minx, miny, minz);
+        this.greaterBoundaryCorner = new Vector3i(maxx, maxy, maxz);
+        if (ownerUniqueId != null) {
+            this.ownerUniqueId = ownerUniqueId;
+            this.ownerPlayerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(this.world.getUniqueId(), this.ownerUniqueId);
+        }
+        this.type = type;
+        this.id = UUID.randomUUID();
+        this.context = new Context("gd_claim", this.id.toString());
+        this.overrideClaimContext = new Context("gd_claim_override", this.id.toString());
+        this.spongeContext = SpongeUtil.getSpongeContext(this.context);
+        this.spongeOverrideClaimContext = SpongeUtil.getSpongeContext(this.overrideClaimContext);
+        this.cuboid = cuboid;
+        this.parent = parent;
+        this.hashCode = this.id.hashCode();
+        this.worldClaimManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(this.world.getUniqueId());
+        if (this.type == ClaimTypes.WILDERNESS) {
+            this.wildernessClaim = this;
+        } else {
+            this.wildernessClaim = this.worldClaimManager.getWildernessClaim();
+        }
+    }
+
+    // Used for visualizations
+    public GDClaim(World world, Vector3i lesserBoundaryCorner, Vector3i greaterBoundaryCorner, ClaimType type, boolean cuboid) {
+        this(world, lesserBoundaryCorner, greaterBoundaryCorner, UUID.randomUUID(), type, null, cuboid);
+    }
+
+    // Used at server startup
+    public GDClaim(World world, Vector3i lesserBoundaryCorner, Vector3i greaterBoundaryCorner, UUID claimId, ClaimType type, UUID ownerUniqueId, boolean cuboid) {
+        this.id = claimId;
+        this.overrideClaimContext = new Context("gd_claim_override", this.id.toString());
+        this.lesserBoundaryCorner = lesserBoundaryCorner;
+        this.greaterBoundaryCorner = greaterBoundaryCorner;
+        this.world = world;
+        if (ownerUniqueId != null) {
+            this.ownerUniqueId = ownerUniqueId;
+            this.ownerPlayerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(this.world.getUniqueId(), this.ownerUniqueId);
+        }
+        this.type = type;
+        this.cuboid = cuboid;
+        this.context = new Context("gd_claim", this.id.toString());
+        this.spongeContext = SpongeUtil.getSpongeContext(this.context);
+        this.spongeOverrideClaimContext = SpongeUtil.getSpongeContext(this.overrideClaimContext);
+        this.hashCode = this.id.hashCode();
+        this.worldClaimManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(this.world.getUniqueId());
+        if (this.type == ClaimTypes.WILDERNESS) {
+            this.wildernessClaim = this;
+        } else {
+            this.wildernessClaim = this.worldClaimManager.getWildernessClaim();
+        }
+    }
+
+    public void initializeClaimData(GDClaim parent) {
+        Path claimDataFolderPath = null;
+        // check if main world
+        if (parent != null) {
+            claimDataFolderPath = parent.getClaimStorage().filePath.getParent().resolve(this.type.getName().toLowerCase());
+        } else {
+            claimDataFolderPath = BaseStorage.worldConfigMap.get(this.world.getUniqueId()).getPath().getParent().resolve("ClaimData").resolve(this.type.getName().toLowerCase());
+        }
+        try {
+            if (Files.notExists(claimDataFolderPath)) {
+                Files.createDirectories(claimDataFolderPath);
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        File claimFile = new File(claimDataFolderPath + File.separator + this.id);
+        if (this.isTown()) {
+            this.claimStorage = new TownStorageData(claimFile.toPath(), this.world.getUniqueId(), this.ownerUniqueId, this.cuboid);
+        } else {
+            this.claimStorage = new ClaimStorageData(claimFile.toPath(), this.world.getUniqueId(), this.ownerUniqueId, this.type, this.cuboid);
+        }
+        this.claimData = this.claimStorage.getConfig();
+        this.parent = parent;
+        if (parent != null) {
+            this.claimStorage.getConfig().setParent(parent.getUniqueId());
+        }
+        this.updateClaimStorageData();
+    }
+
+    public ClaimType getType() {
+        return this.type;
+    }
+
+    public void setType(ClaimType type) {
+        this.type = type;
+        if (this.claimData != null) {
+            this.claimData.setType(type);
+        }
+    }
+
+    public ClaimVisual getVisualizer() {
+        if (this.claimVisual == null || this.markVisualDirty) {
+            this.claimVisual = new ClaimVisual(this, ClaimVisual.getClaimVisualType(this));
+            this.markVisualDirty = false;
+        }
+        return this.claimVisual;
+    }
+
+    public void resetVisuals() {
+        List<UUID> playersWatching = new ArrayList<>(this.playersWatching);
+        for (UUID playerUniqueId : playersWatching) {
+            final Player spongePlayer = Sponge.getServer().getPlayer(playerUniqueId).orElse(null);
+            final GDPlayerData data = this.worldClaimManager.getOrCreatePlayerData(playerUniqueId);
+            if (spongePlayer != null) {
+                data.revertActiveVisual(spongePlayer);
+            }
+        }
+        this.claimVisual = null;
+    }
+
+    public GDPlayerData getOwnerPlayerData() {
+        if (this.ownerPlayerData == null && this.ownerUniqueId != null) {
+            this.ownerPlayerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(this.world.getUniqueId(), this.ownerUniqueId);
+        }
+
+        return this.ownerPlayerData;
+    }
+
+    public UUID getOwnerUniqueId() {
+        if (this.isAdminClaim()) {
+            return GriefDefenderPlugin.ADMIN_USER_UUID;
+        }
+        if (this.ownerUniqueId == null) {
+            if (this.parent != null) {
+                return this.parent.getOwnerUniqueId();
+            }
+
+            return GriefDefenderPlugin.ADMIN_USER_UUID;
+        }
+
+        return this.ownerUniqueId;
+    }
+
+    public void setOwnerUniqueId(UUID uniqueId) {
+        this.ownerUniqueId = uniqueId;
+    }
+
+    public boolean isAdminClaim() {
+        return this.type == ClaimTypes.ADMIN;
+    }
+
+    @Override
+    public boolean isCuboid() {
+        if (this.claimData != null) {
+            return this.claimData.isCuboid();
+        }
+
+        return this.cuboid;
+    }
+
+    @Override
+    public boolean isInTown() {
+        if (this.isTown()) {
+            return true;
+        }
+
+        GDClaim parent = this.parent;
+        while (parent != null) {
+            if (parent.isTown()) {
+                return true;
+            }
+            parent = parent.parent;
+        }
+
+        return false;
+    }
+
+    @Override
+    public Optional<Claim> getTown() {
+        return Optional.ofNullable(this.getTownClaim());
+    }
+
+    @Nullable
+    public GDClaim getTownClaim() {
+        if (this.isTown()) {
+            return this;
+        }
+
+        if (this.parent == null) {
+            return null;
+        }
+
+        GDClaim parent = this.parent;
+        while (parent != null) {
+            if (parent.isTown()) {
+                return parent;
+            }
+            parent = parent.parent;
+        }
+
+        return null;
+    }
+
+    @Override
+    public UUID getUniqueId() {
+        return this.id;
+    }
+
+    public Optional<Component> getName() {
+        if (this.claimData == null) {
+            return Optional.empty();
+        }
+        return this.claimData.getName();
+    }
+
+    public Component getFriendlyNameType() {
+        return this.getFriendlyNameType(false);
+    }
+
+    public Component getFriendlyNameType(boolean upper) {
+        if (this.type == ClaimTypes.ADMIN) {
+            if (upper) {
+                return TextComponent.of(this.type.getName().toUpperCase(), TextColor.RED);
+            }
+            return TextComponent.of("Admin", TextColor.RED);
+        }
+
+        if (this.type == ClaimTypes.BASIC) {
+            if (upper) {
+                return TextComponent.of(this.type.getName().toUpperCase(), TextColor.YELLOW);
+            }
+            return TextComponent.of("Basic", TextColor.YELLOW);
+        }
+
+        if (this.type == ClaimTypes.SUBDIVISION) {
+            if (upper) {
+                return TextComponent.of(this.type.getName().toUpperCase(), TextColor.AQUA);
+            }
+            return TextComponent.of("Subdivision", TextColor.AQUA);
+        }
+
+        if (upper) {
+            return TextComponent.of(this.type.getName().toUpperCase(), TextColor.GREEN);
+        }
+        return TextComponent.of("Town", TextColor.GREEN);
+    }
+
+    @Override
+    public int getClaimBlocks() {
+        if (GriefDefenderPlugin.CLAIM_BLOCK_SYSTEM == ClaimBlockSystem.VOLUME) {
+            return this.getVolume();
+        }
+
+        return this.getArea();
+    }
+
+    @Override
+    public int getArea() {
+        final int claimWidth = this.greaterBoundaryCorner.getX() - this.lesserBoundaryCorner.getX() + 1;
+        final int claimLength = this.greaterBoundaryCorner.getZ() - this.lesserBoundaryCorner.getZ() + 1;
+
+        return claimWidth * claimLength;
+    }
+
+    @Override
+    public int getVolume() {
+        final int claimWidth = this.greaterBoundaryCorner.getX() - this.lesserBoundaryCorner.getX() + 1;
+        final int claimLength = this.greaterBoundaryCorner.getZ() - this.lesserBoundaryCorner.getZ() + 1;
+        final int claimHeight = this.greaterBoundaryCorner.getY() - this.lesserBoundaryCorner.getY() + 1;
+
+        return claimWidth * claimLength * claimHeight;
+    }
+
+    @Override
+    public int getWidth() {
+        return this.greaterBoundaryCorner.getX() - this.lesserBoundaryCorner.getX() + 1;
+    }
+
+    @Override
+    public int getHeight() {
+        return this.greaterBoundaryCorner.getY() - this.lesserBoundaryCorner.getY() + 1;
+    }
+
+    @Override
+    public int getLength() {
+        return this.greaterBoundaryCorner.getZ() - this.lesserBoundaryCorner.getZ() + 1;
+    }
+
+    public Component allowEdit(Player player) {
+        final GDPermissionUser user = PermissionHolderCache.getInstance().getOrCreateUser(player);
+        if (user != null) {
+            return allowEdit(user);
+        }
+
+        return TextComponent.of("");
+    }
+
+    public Component allowEdit(GDPermissionUser holder) {
+        return allowEdit(holder, false);
+    }
+
+    public Component allowEdit(GDPermissionUser holder, boolean forced) {
+        if (this.isUserTrusted(holder, TrustTypes.MANAGER, null, forced)) {
+            return null;
+        }
+
+        if (PermissionUtil.getInstance().holderHasPermission(holder, GDPermissions.COMMAND_DELETE_CLAIMS)) {
+            return null;
+        }
+
+        if (this.parent != null && this.getData().doesInheritParent()) {
+            return this.parent.allowEdit(holder);
+        }
+
+        final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(this.getWorldUniqueId(), holder.getUniqueId());
+        if (playerData.canIgnoreClaim(this)) {
+            return null;
+        }
+
+        final Component message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.CLAIM_OWNER_ONLY, ImmutableMap.of(
+                "player", this.getOwnerName()));
+        return message;
+    }
+
+    public Component allowGrantPermission(Player player) {
+        if(this.allowEdit(player) == null) {
+            return null;
+        }
+
+        for(int i = 0; i < this.claimData.getManagers().size(); i++) {
+            UUID managerID = this.claimData.getManagers().get(i);
+            if(player.getUniqueId().equals(managerID)) {
+                return null;
+            }
+        }
+
+        if(this.parent != null && this.getData().doesInheritParent()) {
+            return this.parent.allowGrantPermission(player);
+        }
+
+        final Component reason = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.PERMISSION_TRUST, ImmutableMap.of(
+                "player", this.getOwnerName()));
+        return reason;
+    }
+
+    @Override
+    public Vector3i getLesserBoundaryCorner() {
+        return this.lesserBoundaryCorner.clone();
+    }
+
+    @Override
+    public Vector3i getGreaterBoundaryCorner() {
+        return this.greaterBoundaryCorner.clone();
+    }
+
+    @Override
+    public Component getOwnerName() {
+        if (this.isAdminClaim() || this.isWilderness()) {
+            return MessageCache.getInstance().OWNER_ADMIN;
+        }
+
+        if (this.getOwnerPlayerData() == null) {
+            return TextComponent.of("[unknown]");
+        }
+
+        return TextComponent.of(this.getOwnerPlayerData().getPlayerName());
+    }
+
+    @Override
+    public boolean contains(Vector3i pos, boolean excludeChildren) {
+        return this.contains(pos.getX(), pos.getY(), pos.getZ(), excludeChildren, null, false);
+    }
+
+    public boolean contains(Location<World> location, boolean useBorderBlockRadius) {
+        return this.contains(location.getBlockX(), location.getBlockY(), location.getBlockZ(), false, null, useBorderBlockRadius);
+    }
+
+    public boolean contains(Location<World> location, GDPlayerData playerData, boolean useBorderBlockRadius) {
+        return this.contains(location.getBlockX(), location.getBlockY(), location.getBlockZ(), false, playerData, useBorderBlockRadius);
+    }
+
+    public boolean contains(int x, int y, int z, boolean excludeChildren, GDPlayerData playerData, boolean useBorderBlockRadius) {
+        int borderBlockRadius = 0;
+        if (useBorderBlockRadius && (playerData != null && !playerData.bypassBorderCheck)) {
+            final int borderRadiusConfig = GriefDefenderPlugin.getActiveConfig(this.world.getUniqueId()).getConfig().claim.borderBlockRadius;
+            if (borderRadiusConfig > 0 && !this.isUserTrusted((User) playerData.getSubject(), TrustTypes.BUILDER)) {
+                borderBlockRadius = borderRadiusConfig;
+            }
+        }
+ 
+        boolean inClaim = (
+                y >= (this.lesserBoundaryCorner.getY() - borderBlockRadius)) &&
+                y < (this.greaterBoundaryCorner.getY() + 1 + borderBlockRadius) &&
+                x >= (this.lesserBoundaryCorner.getX() - borderBlockRadius) &&
+                x < (this.greaterBoundaryCorner.getX() + 1 + borderBlockRadius) &&
+                z >= (this.lesserBoundaryCorner.getZ() - borderBlockRadius) &&
+                z < (this.greaterBoundaryCorner.getZ() + 1 + borderBlockRadius);
+
+        if (!inClaim) {
+            return false;
+        }
+
+        if (!excludeChildren && this.parent != null && (this.getData() == null || (this.getData() != null && this.getData().doesInheritParent()))) {
+            return this.parent.contains(x, y, z, false, null, false);
+        }
+
+        return true;
+    }
+
+    public boolean isClaimOnBorder(GDClaim claim) {
+        if (claim.cuboid) {
+            return false;
+        }
+
+        boolean result = claim.lesserBoundaryCorner.getX() == this.lesserBoundaryCorner.getX() ||
+               claim.greaterBoundaryCorner.getX() == this.greaterBoundaryCorner.getX() ||
+               claim.lesserBoundaryCorner.getZ() == this.lesserBoundaryCorner.getZ() ||
+               claim.greaterBoundaryCorner.getZ() == this.greaterBoundaryCorner.getZ();
+        if (claim.cuboid) {
+            result = claim.lesserBoundaryCorner.getY() == this.lesserBoundaryCorner.getY() ||
+                    claim.greaterBoundaryCorner.getY() == this.greaterBoundaryCorner.getY();
+        }
+        return result;
+    }
+
+    @Override
+    public boolean overlaps(Claim other) {
+        GDClaim otherClaim = (GDClaim) other;
+        if (this.id == otherClaim.id) {
+            return false;
+        }
+
+        // Handle claims entirely within a town
+        if (this.isTown() && !otherClaim.isTown() && otherClaim.isInside(this)) {
+            return false;
+        }
+
+        //verify that no claim's lesser boundary point is inside this new claim, to cover the "existing claim is entirely inside new claim" case
+        if(this.contains(otherClaim.getLesserBoundaryCorner(), false)) {
+            return true;
+        }
+
+        return this.isBandingAcross(otherClaim);
+    }
+
+    //Checks if claim bands across another claim, either horizontally or vertically
+    public boolean isBandingAcross(GDClaim otherClaim) {
+        final boolean isClaimInside = otherClaim.isInside(this);
+        if (isClaimInside) {
+            return false;
+        }
+
+        final int smallX = otherClaim.getLesserBoundaryCorner().getX();
+        final int smallY = otherClaim.getLesserBoundaryCorner().getY();
+        final int smallZ = otherClaim.getLesserBoundaryCorner().getZ();
+        final int bigX = otherClaim.getGreaterBoundaryCorner().getX();
+        final int bigY = otherClaim.getGreaterBoundaryCorner().getY();
+        final int bigZ = otherClaim.getGreaterBoundaryCorner().getZ();
+
+        if(this.contains(otherClaim.lesserBoundaryCorner, false)) {
+            return true;
+        }
+        if(this.contains(otherClaim.greaterBoundaryCorner, false)) {
+            return true;
+        }
+        if(this.contains(new Vector3i(smallX, 0, bigZ), false)) {
+            return true;
+        }
+        if(this.contains(new Vector3i(bigX, 0, smallZ), false)) {
+            return true;
+        }
+
+        boolean inArea = false;
+        if(this.getLesserBoundaryCorner().getZ() <= bigZ &&
+           this.getLesserBoundaryCorner().getZ() >= smallZ &&
+           this.getLesserBoundaryCorner().getX() < smallX &&
+           this.getGreaterBoundaryCorner().getX() > bigX)
+           inArea = true;
+
+        if( this.getGreaterBoundaryCorner().getZ() <= bigZ && 
+            this.getGreaterBoundaryCorner().getZ() >= smallZ && 
+            this.getLesserBoundaryCorner().getX() < smallX &&
+            this.getGreaterBoundaryCorner().getX() > bigX )
+            inArea = true;
+        
+        if( this.getLesserBoundaryCorner().getX() <= bigX && 
+            this.getLesserBoundaryCorner().getX() >= smallX && 
+            this.getLesserBoundaryCorner().getZ() < smallZ &&
+            this.getGreaterBoundaryCorner().getZ() > bigZ )
+            inArea = true;
+            
+        if( this.getGreaterBoundaryCorner().getX() <= bigX && 
+            this.getGreaterBoundaryCorner().getX() >= smallX && 
+            this.getLesserBoundaryCorner().getZ() < smallZ &&
+            this.getGreaterBoundaryCorner().getZ() > bigZ )
+            inArea = true;
+
+        if (inArea) {
+            // check height
+            if ((this.lesserBoundaryCorner.getY() >= smallY &&
+                 this.lesserBoundaryCorner.getY() <= bigY) ||
+                (this.greaterBoundaryCorner.getY() <= smallY &&
+                 this.greaterBoundaryCorner.getY() >= smallY)) {
+                return true;
+            }
+
+            return false;
+        }
+
+        return false;
+    }
+
+    @Override
+    public boolean isInside(Claim claim) {
+        final GDClaim otherClaim = (GDClaim) claim;
+        if(!otherClaim.contains(this.lesserBoundaryCorner)) {
+            return false;
+        }
+        if(!otherClaim.contains(this.greaterBoundaryCorner)) {
+            return false;
+        }
+
+        if(!otherClaim.contains(new Vector3i(this.lesserBoundaryCorner.getX(), this.lesserBoundaryCorner.getY(), this.greaterBoundaryCorner.getZ()))) {
+            return false;
+        }
+        if(!otherClaim.contains(new Vector3i(this.greaterBoundaryCorner.getX(), this.greaterBoundaryCorner.getY(), this.lesserBoundaryCorner.getZ()))) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public ArrayList<Vector3i> getChunkPositions() {
+        ArrayList<Vector3i> chunkPositions = new ArrayList<Vector3i>();
+        final Set<Long> chunkHashes = this.getChunkHashes(true);
+        for (Long hash : chunkHashes) {
+            //chunkPositions.add(ChunkPos.)
+        }
+        return chunkPositions;
+    }
+
+    public ArrayList<Chunk> getChunks() {
+        ArrayList<Chunk> chunks = new ArrayList<Chunk>();
+
+        Chunk lesserChunk = this.world
+                .getChunk(this.getLesserBoundaryCorner().getX() >> 4, 0, this.getLesserBoundaryCorner().getZ() >> 4).orElse(null);
+        Chunk greaterChunk = this.world
+                .getChunk(this.getGreaterBoundaryCorner().getX() >> 4, 0, this.getGreaterBoundaryCorner().getZ() >> 4).orElse(null);
+
+        if (lesserChunk != null && greaterChunk != null) {
+            for (int x = lesserChunk.getPosition().getX(); x <= greaterChunk.getPosition().getX(); x++) {
+                for (int z = lesserChunk.getPosition().getZ(); z <= greaterChunk.getPosition().getZ(); z++) {
+                    Chunk chunk = world.loadChunk(x, 0, z, true).orElse(null);
+                    if (chunk != null) {
+                        chunks.add(chunk);
+                    }
+                }
+            }
+        }
+
+        return chunks;
+    }
+
+    public boolean canIgnoreHeight() {
+        if (this.isCuboid()) {
+            return false;
+        }
+
+        if (this.ownerPlayerData != null && (this.getOwnerMinClaimLevel() > 0 || this.getOwnerMaxClaimLevel() < 255)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    public double getOwnerEconomyBlockCost() {
+        return this.getOwnerEconomyBlockCost(this.ownerPlayerData);
+    }
+
+    public double getOwnerEconomyBlockCost(GDPlayerData playerData) {
+        final GDPermissionHolder subject = playerData == null ? GriefDefenderPlugin.DEFAULT_HOLDER : playerData.getSubject();
+        return GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Double.class), subject, Options.ECONOMY_BLOCK_COST).doubleValue();
+    }
+
+    public int getOwnerMinClaimLevel() {
+        return this.getOwnerMinClaimLevel(this.ownerPlayerData);
+    }
+
+    public int getOwnerMinClaimLevel(GDPlayerData playerData) {
+        final GDPermissionHolder subject = playerData == null ? GriefDefenderPlugin.DEFAULT_HOLDER : playerData.getSubject();
+        return GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), subject, Options.MIN_LEVEL).intValue();
+    }
+
+    public int getOwnerMaxClaimLevel() {
+        return this.getOwnerMaxClaimLevel(this.ownerPlayerData);
+    }
+
+    public int getOwnerMaxClaimLevel(GDPlayerData playerData) {
+        final GDPermissionHolder subject = playerData == null ? GriefDefenderPlugin.DEFAULT_HOLDER : playerData.getSubject();
+        return GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), subject, Options.MAX_LEVEL).intValue();
+    }
+
+    @Override
+    public Set<Long> getChunkHashes() {
+        return this.getChunkHashes(true);
+    }
+
+    public Set<Long> getChunkHashes(boolean refresh) {
+        if (this.chunkHashes == null || refresh) {
+            this.chunkHashes = new HashSet<Long>();
+            int smallX = this.lesserBoundaryCorner.getX() >> 4;
+            int smallZ = this.lesserBoundaryCorner.getZ() >> 4;
+            int largeX = this.greaterBoundaryCorner.getX() >> 4;
+            int largeZ = this.greaterBoundaryCorner.getZ() >> 4;
+    
+            for (int x = smallX; x <= largeX; x++) {
+                for (int z = smallZ; z <= largeZ; z++) {
+                    this.chunkHashes.add(BlockUtil.getInstance().asLong(x, z));
+                }
+            }
+        }
+
+        return this.chunkHashes;
+    }
+
+    @Override
+    public ClaimData getData() {
+        return (ClaimData) this.claimData;
+    }
+
+    public IClaimData getInternalClaimData() {
+        return this.claimData;
+    }
+
+    @Nullable
+    public TownDataConfig getTownData() {
+        if (!(this.claimData instanceof TownDataConfig)) {
+            return null;
+        }
+
+        return (TownDataConfig) this.claimData;
+    }
+
+    public ClaimStorageData getClaimStorage() {
+        return this.claimStorage;
+    }
+
+    public void setClaimData(IClaimData data) {
+        this.claimData = data;
+    }
+
+    public void setClaimStorage(ClaimStorageData storage) {
+        this.claimStorage = storage;
+    }
+
+    public void updateClaimStorageData() {
+        if (!this.isAdminClaim()) {
+            this.claimStorage.getConfig().setOwnerUniqueId(this.getOwnerUniqueId());
+        }
+        this.claimStorage.getConfig().setWorldUniqueId(this.world.getUniqueId());
+        this.claimData.setCuboid(this.cuboid);
+        this.claimData.setType(this.type);
+        this.claimData.setLesserBoundaryCorner(BlockUtil.getInstance().posToString(this.lesserBoundaryCorner));
+        this.claimData.setGreaterBoundaryCorner(BlockUtil.getInstance().posToString(this.greaterBoundaryCorner));
+        // Will save next world save
+        this.claimData.setRequiresSave(true);
+    }
+
+    public void save() {
+        for (Claim child : this.children) {
+            GDClaim childClaim = (GDClaim) child;
+            if (childClaim.getInternalClaimData().requiresSave()) {
+                childClaim.save();
+            }
+        }
+        GDSaveClaimEvent.Pre preEvent = new GDSaveClaimEvent.Pre(this);
+        GriefDefender.getEventManager().post(preEvent);
+        if (this.getInternalClaimData().requiresSave()) {
+            this.updateClaimStorageData();
+            this.getClaimStorage().save();
+            this.getInternalClaimData().setRequiresSave(false);
+        }
+        GDSaveClaimEvent.Post postEvent = new GDSaveClaimEvent.Post(this);
+        GriefDefender.getEventManager().post(postEvent);
+    }
+
+    public boolean isPvpEnabled() {
+        Tristate value = this.claimData.getPvpOverride();
+        if (value != Tristate.UNDEFINED) {
+            return value.asBoolean();
+        }
+
+        return this.world.getProperties().isPVPEnabled();
+    }
+
+    public void setPvpOverride(Tristate value) {
+        this.claimData.setPvpOverride(value);
+        this.getClaimStorage().save();
+    }
+
+    @Override
+    public ClaimResult transferOwner(UUID newOwnerID) {
+        return this.transferOwner(newOwnerID, false, false);
+    }
+
+    public ClaimResult transferOwner(UUID newOwnerID, boolean checkEconomy, boolean withdrawFunds) {
+        if (this.isWilderness()) {
+            return new GDClaimResult(ClaimResultType.WRONG_CLAIM_TYPE, TextComponent.builder("").append("The wilderness cannot be transferred.", TextColor.RED).build());
+        }
+
+        if (this.isAdminClaim()) {
+            return new GDClaimResult(ClaimResultType.WRONG_CLAIM_TYPE, TextComponent.builder("").append("Admin claims cannot be transferred.", TextColor.RED).build());
+        }
+
+        GDPlayerData ownerData = DATASTORE.getOrCreatePlayerData(this.world.getUniqueId(), this.getOwnerUniqueId());
+        // determine new owner
+        GDPlayerData newOwnerData = DATASTORE.getOrCreatePlayerData(this.world.getUniqueId(), newOwnerID);
+
+        if (this.isBasicClaim() && this.claimData.requiresClaimBlocks()) {
+            if (GriefDefenderPlugin.getInstance().isEconomyModeEnabled()) {
+                if (checkEconomy) {
+                    final GDClaimResult result = EconomyUtil.getInstance().checkEconomyFunds(this, newOwnerData, withdrawFunds);
+                    if (!result.successful()) {
+                        return result;
+                    }
+                }
+            } else {
+                int remainingClaimBlocks = newOwnerData.getRemainingClaimBlocks();
+                if (remainingClaimBlocks < 0 || (this.getClaimBlocks() > remainingClaimBlocks)) {
+                    return new GDClaimResult(ClaimResultType.INSUFFICIENT_CLAIM_BLOCKS);
+                }
+            }
+        }
+
+        // Check limits
+        final Player currentOwner = ownerData.getSubject() instanceof Player ? (Player) ownerData.getSubject() : null;
+        final int createClaimLimit = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), newOwnerData.getSubject(), Options.CREATE_LIMIT, this);
+        if (createClaimLimit > -1 && (newOwnerData.getInternalClaims().size() + 1) > createClaimLimit) {
+            if (currentOwner != null) {
+                GriefDefenderPlugin.sendMessage(currentOwner, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.CLAIM_TRANSFER_EXCEEDS_LIMIT));
+            }
+            return new GDClaimResult(this, ClaimResultType.EXCEEDS_MAX_CLAIM_LIMIT, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.CLAIM_TRANSFER_EXCEEDS_LIMIT));
+        }
+
+        // transfer
+        GDTransferClaimEvent event = new GDTransferClaimEvent(this, this.getOwnerUniqueId(), newOwnerID);
+        GriefDefender.getEventManager().post(event);
+        if (event.cancelled()) {
+            return new GDClaimResult(this, ClaimResultType.CLAIM_EVENT_CANCELLED, event.getMessage().orElse(null));
+        }
+
+        if (this.isAdminClaim()) {
+            // convert to basic
+            this.type = ClaimTypes.BASIC;
+            this.getVisualizer().setType(ClaimVisual.BASIC);
+            this.claimData.setType(ClaimTypes.BASIC);
+        }
+
+        this.ownerUniqueId = event.getNewOwner();
+        if (!this.getOwnerUniqueId().equals(newOwnerID)) {
+            newOwnerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(this.world, this.getOwnerUniqueId());
+        }
+
+        this.claimData.setOwnerUniqueId(newOwnerID);
+        if (this.isBasicClaim()) {
+            ownerData.getInternalClaims().remove(this);
+            newOwnerData.getInternalClaims().add(this);
+        }
+
+        this.ownerPlayerData = newOwnerData;
+        this.getClaimStorage().save();
+        return new GDClaimResult(this, ClaimResultType.SUCCESS);
+    }
+
+    public ClaimResult findParent(GDClaim claimToSearch) {
+        if (!this.isInside(claimToSearch)) {
+            return new GDClaimResult(ClaimResultType.CLAIM_NOT_FOUND);
+        }
+        Claim current = claimToSearch;
+        for (Claim child : current.getChildren(true)) {
+            if (this.isInside(child)) {
+                current = child;
+            }
+        }
+        return new GDClaimResult(current, ClaimResultType.SUCCESS);
+    }
+
+    public ClaimResult doesClaimOverlap() {
+        if (this.parent != null) {
+            final GDClaim parentClaim = (GDClaim) this.parent;
+            // 1 - Make sure new claim is inside parent
+            if (!this.isInside(parentClaim)) {
+                return new GDClaimResult(parentClaim, ClaimResultType.OVERLAPPING_CLAIM);
+            }
+
+            // 2 - Check parent children
+            for (Claim child : parentClaim.children) {
+                final GDClaim childClaim = (GDClaim) child;
+                if (this.isBandingAcross(childClaim) || childClaim.isBandingAcross(this)) {
+                    return new GDClaimResult(childClaim, ClaimResultType.OVERLAPPING_CLAIM);
+                }
+            }
+            return new GDClaimResult(this, ClaimResultType.SUCCESS);
+        }
+
+        final GDClaimManager claimWorldManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(this.world.getUniqueId());
+        final Set<Long> chunkHashes = this.getChunkHashes(true);
+
+        // Since there is no parent we need to check all claims stored in chunk hashes
+        for (Long chunkHash : chunkHashes) {
+            Set<Claim> claimsInChunk = claimWorldManager.getInternalChunksToClaimsMap().get(chunkHash);
+            if (claimsInChunk == null || claimsInChunk.size() == 0) {
+                continue;
+            }
+            for (Claim child : claimsInChunk) {
+                final GDClaim gpChild = (GDClaim) child;
+                // First check if newly resized claim is crossing another
+                if (this.isBandingAcross(gpChild) || gpChild.isBandingAcross(this)) {
+                    return new GDClaimResult(child, ClaimResultType.OVERLAPPING_CLAIM);
+                }
+            }
+        }
+
+        return new GDClaimResult(this, ClaimResultType.SUCCESS);
+    }
+
+    // Scans area for any overlaps and migrates children to a newly created or resized claim
+    public ClaimResult checkArea(boolean resize) {
+        final List<Claim> claimsInArea = new ArrayList<>();
+        claimsInArea.add(this);
+
+        if (this.parent != null) {
+            return checkAreaParent(claimsInArea, resize);
+        }
+
+        final List<Claim> claimsToMigrate = new ArrayList<>();
+        // First check children
+        for (Claim child : this.children) {
+            final GDClaim childClaim = (GDClaim) child;
+            if (this.isBandingAcross(childClaim) || childClaim.isBandingAcross(this)) {
+                return new GDClaimResult(childClaim, ClaimResultType.OVERLAPPING_CLAIM);
+            }
+            if (childClaim.isInside(this)) {
+                if (!this.isAdminClaim()) {
+                    if (this.type.equals(childClaim.type) || childClaim.isAdminClaim()) {
+                        return new GDClaimResult(childClaim, ClaimResultType.OVERLAPPING_CLAIM);
+                    }
+                }
+            } else {
+                // child is no longer within parent
+                // if resizing, migrate the child claim out
+                if (resize) {
+                    claimsToMigrate.add(childClaim);
+                } else {
+                    return new GDClaimResult(childClaim, ClaimResultType.OVERLAPPING_CLAIM);
+                }
+            }
+        }
+
+        if (!claimsToMigrate.isEmpty()) {
+            ((GDClaim) this.wildernessClaim).migrateClaims(claimsToMigrate);
+        }
+
+        final GDClaimManager claimWorldManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(this.world.getUniqueId());
+        final Set<Long> chunkHashes = this.getChunkHashes(true);
+
+        // Since there is no parent we need to check all claims stored in chunk hashes
+        for (Long chunkHash : chunkHashes) {
+            Set<Claim> claimsInChunk = claimWorldManager.getInternalChunksToClaimsMap().get(chunkHash);
+            if (claimsInChunk == null || claimsInChunk.size() == 0) {
+                continue;
+            }
+            for (Claim chunkClaim : claimsInChunk) {
+                final GDClaim gpChunkClaim = (GDClaim) chunkClaim;
+                if (gpChunkClaim.equals(this) || claimsInArea.contains(gpChunkClaim)) {
+                    continue;
+                }
+                if (this.isAdminClaim() && gpChunkClaim.isAdminClaim() && gpChunkClaim.parent != null && gpChunkClaim.parent.equals(this)) {
+                    continue;
+                }
+
+                // First check if new claim is crossing another
+                if (this.isBandingAcross(gpChunkClaim) || gpChunkClaim.isBandingAcross(this)) {
+                    return new GDClaimResult(gpChunkClaim, ClaimResultType.OVERLAPPING_CLAIM);
+                }
+                if (gpChunkClaim.isInside(this)) {
+                     if (!this.isAdminClaim()) {
+                        if (this.type.equals(gpChunkClaim.type) || gpChunkClaim.isAdminClaim()) {
+                            return new GDClaimResult(gpChunkClaim, ClaimResultType.OVERLAPPING_CLAIM);
+                        }
+                    }
+                    if (!this.canEnclose(gpChunkClaim)) {
+                        return new GDClaimResult(gpChunkClaim, ClaimResultType.OVERLAPPING_CLAIM);
+                    }
+                    if (!this.isSubdivision()) {
+                        claimsInArea.add(gpChunkClaim);
+                    }
+                } else if (this.isInside(gpChunkClaim)) {
+                    // Fix WorldEdit issue
+                    // Make sure to check if chunk claim can enclose newly created claim
+                    if (!gpChunkClaim.canEnclose(this)) {
+                        return new GDClaimResult(gpChunkClaim, ClaimResultType.OVERLAPPING_CLAIM);
+                    }
+                }
+            }
+        }
+
+        return new GDClaimResult(claimsInArea, ClaimResultType.SUCCESS);
+    }
+
+    public ClaimResult checkAreaParent(List<Claim> claimsInArea, boolean resize) {
+        if (this.isClaimOnBorder(this.parent)) {
+            return new GDClaimResult(this.parent, ClaimResultType.OVERLAPPING_CLAIM);
+        }
+        final GDClaim parentClaim = (GDClaim) this.parent;
+        // 1 - Make sure new claim is inside parent
+        if (!this.isInside(parentClaim)) {
+            return new GDClaimResult(parentClaim, ClaimResultType.OVERLAPPING_CLAIM);
+        }
+
+        // 2 - Check parent children
+        for (Claim child : parentClaim.children) {
+            final GDClaim childClaim = (GDClaim) child;
+            if (this.equals(child)) {
+                continue;
+            }
+            if (this.isBandingAcross(childClaim) || childClaim.isBandingAcross(this)) {
+                return new GDClaimResult(childClaim, ClaimResultType.OVERLAPPING_CLAIM);
+            }
+
+            if (childClaim.isInside(this)) {
+                if (!this.isAdminClaim()) {
+                    if (this.type.equals(childClaim.type) || childClaim.isAdminClaim()) {
+                        return new GDClaimResult(childClaim, ClaimResultType.OVERLAPPING_CLAIM);
+                    }
+                }
+                if (!this.isSubdivision()) {
+                    claimsInArea.add(childClaim);
+                }
+            }
+            // ignore claims not inside
+        }
+
+        if (resize) {
+            // Make sure children are still within their parent
+            final List<Claim> claimsToMigrate = new ArrayList<>();
+            for (Claim child : this.children) {
+                GDClaim childClaim = (GDClaim) child;
+                if (this.isBandingAcross(childClaim) || childClaim.isBandingAcross(this)) {
+                    return new GDClaimResult(childClaim, ClaimResultType.OVERLAPPING_CLAIM);
+                }
+                if (!childClaim.isInside(this)) {
+                    if (this.parent != null) {
+                        claimsToMigrate.add(childClaim);
+                    } else {
+                        childClaim.parent = null;
+                        this.children.remove(childClaim);
+                        final GDClaimManager claimWorldManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(this.world.getUniqueId());
+                        claimWorldManager.addClaim(childClaim, true);
+                    }
+                }
+            }
+            if (!claimsToMigrate.isEmpty()) {
+                this.parent.migrateClaims(claimsToMigrate);
+            }
+        }
+        return new GDClaimResult(claimsInArea, ClaimResultType.SUCCESS);
+    }
+
+    public boolean canEnclose(Claim claim) {
+        if (claim.isWilderness()) {
+            return false;
+        }
+        if (this.isAdminClaim()) {
+            // admin claims can enclose any type
+            return true;
+        }
+        if (this.isSubdivision()) {
+            return false;
+        }
+        if (this.isBasicClaim()) {
+            if (!claim.isSubdivision()) {
+                return false;
+            }
+            return true;
+        }
+        if (this.isTown()) {
+            if (claim.isAdminClaim()) {
+                return false;
+            }
+            return true;
+        }
+        return true;
+    }
+
+    // Checks to see if the passed in claim is a parent of this claim
+    @Override
+    public boolean isParent(Claim claim) {
+        if (this.parent == null) {
+            return false;
+        }
+
+        GDClaim parent = this.parent;
+        while (parent != null) {
+            if (parent.getUniqueId().equals(claim.getUniqueId())) {
+                return true;
+            }
+            parent = parent.parent;
+        }
+
+        return false;
+    }
+
+    @Override
+    public ClaimResult resize(int x1, int x2, int y1, int y2, int z1, int z2) {
+        int minx = Math.min(x1, x2);
+        int miny = Math.min(y1, y2);
+        int minz = Math.min(z1, z2);
+        int maxx = Math.max(x1, x2);
+        int maxy = Math.max(y1, y2);
+        int maxz = Math.max(z1, z2);
+
+        final Object root = GDCauseStackManager.getInstance().getCurrentCause().root();
+        final GDPermissionUser user = root instanceof GDPermissionUser ? (GDPermissionUser) root : null;
+        final Player player = user != null ? user.getOnlinePlayer() : null;
+        if (this.cuboid) {
+            return resizeCuboid(player, minx, miny, minz, maxx, maxy, maxz);
+        }
+
+        Location<World> startCorner = null;
+        Location<World> endCorner = null;
+        GDPlayerData playerData = null;
+        if (player != null) {
+            playerData = GriefDefenderPlugin.getInstance().dataStore.getPlayerData(this.world, player.getUniqueId());
+        } else if (!this.isAdminClaim() && this.ownerUniqueId != null) {
+            playerData = GriefDefenderPlugin.getInstance().dataStore.getPlayerData(this.world, this.ownerUniqueId);
+        }
+
+        if (playerData == null) {
+            if (GriefDefenderPlugin.getActiveConfig(this.world.getUniqueId()).getConfig().claim.claimAutoSchematicRestore) {
+                return new GDClaimResult(this, ClaimResultType.FAILURE);
+            }
+            startCorner = new Location<World>(this.world, minx, miny, minz);
+            endCorner = new Location<World>(this.world, maxx, maxy, maxz);
+        } else {
+            if (!playerData.canIgnoreClaim(this) && GriefDefenderPlugin.getActiveConfig(this.world.getUniqueId()).getConfig().claim.claimAutoSchematicRestore) {
+                return new GDClaimResult(this, ClaimResultType.FAILURE);
+            }
+            startCorner = playerData.lastShovelLocation;
+            endCorner = playerData.endShovelLocation;
+        }
+
+        // Auto-adjust Y levels for 2D claims
+        if (playerData != null) {
+            miny = this.getOwnerMinClaimLevel();
+        }
+        if (playerData != null) {
+            maxy = this.getOwnerMaxClaimLevel();
+        }
+        Vector3i currentLesserCorner = this.getLesserBoundaryCorner();
+        Vector3i currentGreaterCorner = this.getGreaterBoundaryCorner();
+        Vector3i newLesserCorner = new Vector3i(minx, miny, minz);
+        Vector3i newGreaterCorner = new Vector3i(maxx, maxy, maxz);
+
+        GDChangeClaimEvent.Resize event = new GDChangeClaimEvent.Resize(this, startCorner, endCorner);
+        GriefDefender.getEventManager().post(event);
+        if (event.cancelled()) {
+            return new GDClaimResult(this, ClaimResultType.CLAIM_EVENT_CANCELLED);
+        }
+
+        // check player has enough claim blocks
+        if ((this.isBasicClaim() || this.isTown()) && this.claimData.requiresClaimBlocks()) {
+            final int newCost = BlockUtil.getInstance().getClaimBlockCost(this.world, newLesserCorner, newGreaterCorner, this.cuboid);
+            final int currentCost = BlockUtil.getInstance().getClaimBlockCost(this.world, currentLesserCorner, currentGreaterCorner, this.cuboid);
+            if (GriefDefenderPlugin.getGlobalConfig().getConfig().economy.economyMode) {
+                final Account playerAccount = GriefDefenderPlugin.getInstance().economyService.get().getOrCreateAccount(playerData.playerID).orElse(null);
+                if (playerAccount == null) {
+                    return new GDClaimResult(ClaimResultType.ECONOMY_ACCOUNT_NOT_FOUND);
+                }
+
+                final Currency defaultCurrency = GriefDefenderPlugin.getInstance().economyService.get().getDefaultCurrency();
+                final double requiredFunds = Math.abs((newCost - currentCost) * this.getOwnerEconomyBlockCost());
+                if (newCost > currentCost) {
+                    final BigDecimal currentFunds = playerAccount.getBalance(defaultCurrency);
+                    final TransactionResult result = playerAccount.withdraw(defaultCurrency, BigDecimal.valueOf(requiredFunds), Sponge.getCauseStackManager().getCurrentCause());
+                    if (result.getResult() != ResultType.SUCCESS) {
+                        Component message = null;
+                        if (player != null) {
+                            message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.ECONOMY_NOT_ENOUGH_FUNDS, ImmutableMap.of(
+                                    "balance", currentFunds.doubleValue(),
+                                    "amount", requiredFunds));
+                            GriefDefenderPlugin.sendMessage(player, message);
+                        }
+    
+                        playerData.lastShovelLocation = null;
+                        playerData.claimResizing = null;
+                        return new GDClaimResult(ClaimResultType.ECONOMY_NOT_ENOUGH_FUNDS, message);
+                    }
+                } else {
+                    final TransactionResult result = playerAccount.deposit(defaultCurrency, BigDecimal.valueOf(requiredFunds), Sponge.getCauseStackManager().getCurrentCause());
+                }
+            } else if (newCost > currentCost) {
+                final int remainingClaimBlocks = this.ownerPlayerData.getRemainingClaimBlocks() - (newCost - currentCost);
+                if (remainingClaimBlocks < 0) {
+                    if (player != null) {
+                        if (GriefDefenderPlugin.CLAIM_BLOCK_SYSTEM == ClaimBlockSystem.VOLUME) {
+                            final double claimableChunks = Math.abs(remainingClaimBlocks / 65536.0);
+                            GriefDefenderPlugin.sendMessage(player, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.CLAIM_SIZE_NEED_BLOCKS_3D, ImmutableMap.of(
+                                    "chunk-amount", Math.round(claimableChunks * 100.0)/100.0,
+                                    "block-amount", Math.abs(remainingClaimBlocks))));
+                        } else {
+                            GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.CLAIM_SIZE_NEED_BLOCKS_2D, ImmutableMap.of(
+                                    "block-amount", Math.abs(remainingClaimBlocks))));
+                        }
+                    }
+                    playerData.lastShovelLocation = null;
+                    playerData.claimResizing = null;
+                    this.lesserBoundaryCorner = currentLesserCorner;
+                    this.greaterBoundaryCorner = currentGreaterCorner;
+                    return new GDClaimResult(ClaimResultType.INSUFFICIENT_CLAIM_BLOCKS);
+                }
+            }
+        }
+
+        this.lesserBoundaryCorner = newLesserCorner;
+        this.greaterBoundaryCorner = newGreaterCorner;
+
+        // checkArea refreshes the current chunk hashes so it is important
+        // to make a copy before making the call
+        final Set<Long> currentChunkHashes = new HashSet<>(this.chunkHashes);
+
+        final ClaimResult result = this.checkArea(true);
+        if (!result.successful()) {
+            this.lesserBoundaryCorner = currentLesserCorner;
+            this.greaterBoundaryCorner = currentGreaterCorner;
+            return result;
+        }
+
+        if (this.type != ClaimTypes.ADMIN && this.type != ClaimTypes.WILDERNESS) {
+            ClaimResult claimResult = checkSizeLimits(player, playerData, newLesserCorner, newGreaterCorner);
+            if (!claimResult.successful()) {
+                this.lesserBoundaryCorner = currentLesserCorner;
+                this.greaterBoundaryCorner = currentGreaterCorner;
+                return claimResult;
+            }
+        }
+
+        // This needs to be adjusted before we check for overlaps
+        this.lesserBoundaryCorner = newLesserCorner;
+        this.greaterBoundaryCorner = newGreaterCorner;
+        GDClaimManager claimWorldManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(this.world.getUniqueId());
+
+        // resize validated, remove invalid chunkHashes
+        if (this.parent == null) {
+            for (Long chunkHash : currentChunkHashes) {
+                Set<Claim> claimsInChunk = claimWorldManager.getInternalChunksToClaimsMap().get(chunkHash);
+                if (claimsInChunk != null && claimsInChunk.size() > 0) {
+                    claimsInChunk.remove(this);
+                }
+            }
+
+            final Set<Long> newChunkHashes = this.getChunkHashes(true);
+            // add new chunk hashes
+            for (Long chunkHash : newChunkHashes) {
+                Set<Claim> claimsInChunk = claimWorldManager.getInternalChunksToClaimsMap().get(chunkHash);
+                if (claimsInChunk == null) {
+                    claimsInChunk = new HashSet<>();
+                    claimWorldManager.getInternalChunksToClaimsMap().put(chunkHash, claimsInChunk);
+                }
+
+                claimsInChunk.add(this);
+            }
+        }
+
+        this.claimData.setLesserBoundaryCorner(BlockUtil.getInstance().posToString(this.lesserBoundaryCorner));
+        this.claimData.setGreaterBoundaryCorner(BlockUtil.getInstance().posToString(this.greaterBoundaryCorner));
+        this.claimData.setRequiresSave(true);
+        this.getClaimStorage().save();
+
+        if (result.getClaims().size() > 1) {
+            this.migrateClaims(new ArrayList<>(result.getClaims()));
+        }
+        this.resetVisuals();
+        return new GDClaimResult(this, ClaimResultType.SUCCESS);
+    }
+
+    public ClaimResult resizeCuboid(Player player, int smallX, int smallY, int smallZ, int bigX, int bigY, int bigZ) {
+        // make sure resize doesn't cross paths
+        if (smallX >= bigX || smallY >= bigY || smallZ >= bigZ) {
+            return new GDClaimResult(this, ClaimResultType.OVERLAPPING_CLAIM);
+        }
+
+        Location<World> startCorner = null;
+        Location<World> endCorner = null;
+        GDPlayerData playerData = null;
+
+        if (player != null) {
+            playerData = GriefDefenderPlugin.getInstance().dataStore.getPlayerData(this.world, player.getUniqueId());
+        } else if (!this.isAdminClaim() && this.ownerUniqueId != null) {
+            playerData = GriefDefenderPlugin.getInstance().dataStore.getPlayerData(this.world, this.ownerUniqueId);
+        }
+
+        if (playerData == null) {
+            if (GriefDefenderPlugin.getActiveConfig(this.world.getUniqueId()).getConfig().claim.claimAutoSchematicRestore) {
+                return new GDClaimResult(this, ClaimResultType.FAILURE);
+            }
+            startCorner = new Location<World>(this.world, smallX, smallY, smallZ);
+            endCorner = new Location<World>(this.world, bigX, bigY, bigZ);
+        } else {
+            if (!playerData.canIgnoreClaim(this) && GriefDefenderPlugin.getActiveConfig(this.world.getUniqueId()).getConfig().claim.claimAutoSchematicRestore) {
+                return new GDClaimResult(this, ClaimResultType.FAILURE);
+            }
+            startCorner = playerData.lastShovelLocation;
+            endCorner = playerData.endShovelLocation;
+        }
+
+        GDChangeClaimEvent.Resize event = new GDChangeClaimEvent.Resize(this, startCorner, endCorner);
+        GriefDefender.getEventManager().post(event);
+        if (event.cancelled()) {
+            return new GDClaimResult(this, ClaimResultType.CLAIM_EVENT_CANCELLED);
+        }
+
+        final int minClaimLevel = this.getOwnerMinClaimLevel();
+        if (playerData != null && playerData.shovelMode != ShovelTypes.ADMIN && smallY < minClaimLevel) {
+            final Component message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.CLAIM_BELOW_LEVEL, ImmutableMap.of(
+                    "limit", minClaimLevel));
+            GriefDefenderPlugin.sendMessage(player, message);
+            return new GDClaimResult(ClaimResultType.BELOW_MIN_LEVEL);
+        }
+        final int maxClaimLevel = this.getOwnerMaxClaimLevel();
+        if (playerData != null && playerData.shovelMode != ShovelTypes.ADMIN && bigY > maxClaimLevel) {
+            final Component message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.CLAIM_ABOVE_LEVEL, ImmutableMap.of(
+                    "limit", maxClaimLevel));
+            GriefDefenderPlugin.sendMessage(player, message);
+            return new GDClaimResult(ClaimResultType.ABOVE_MAX_LEVEL);
+        }
+        // check if child extends past parent limits
+        if (this.parent != null) {
+            if (smallX < this.parent.getLesserBoundaryCorner().getX() ||
+                smallY < this.parent.getLesserBoundaryCorner().getY() ||
+                smallZ < this.parent.getLesserBoundaryCorner().getZ()) {
+                return new GDClaimResult(this.parent, ClaimResultType.OVERLAPPING_CLAIM);
+            }
+            if (bigX > this.parent.getGreaterBoundaryCorner().getX() ||
+                (this.parent.isCuboid() && bigY > this.parent.getGreaterBoundaryCorner().getY()) ||
+                bigZ > this.parent.getGreaterBoundaryCorner().getZ()) {
+                return new GDClaimResult(this.parent, ClaimResultType.OVERLAPPING_CLAIM);
+            }
+        }
+
+        Vector3i currentLesserCorner = this.lesserBoundaryCorner;
+        Vector3i currentGreaterCorner = this.greaterBoundaryCorner;
+        Vector3i newLesserCorner = new Vector3i(smallX, smallY, smallZ);
+        Vector3i newGreaterCorner = new Vector3i(bigX, bigY, bigZ);
+        this.lesserBoundaryCorner = newLesserCorner;
+        this.greaterBoundaryCorner = newGreaterCorner;
+
+        // checkArea refreshes the current chunk hashes so it is important
+        // to make a copy before making the call
+        final Set<Long> currentChunkHashes = new HashSet<>(this.chunkHashes);
+
+        final ClaimResult result = this.checkArea(true);
+        if (!result.successful()) {
+            this.lesserBoundaryCorner = currentLesserCorner;
+            this.greaterBoundaryCorner = currentGreaterCorner;
+            return result;
+        }
+
+        if (this.type != ClaimTypes.ADMIN && this.type != ClaimTypes.WILDERNESS) {
+            ClaimResult claimResult = checkSizeLimits(player, playerData, newLesserCorner, newGreaterCorner);
+            if (!claimResult.successful()) {
+                this.lesserBoundaryCorner = currentLesserCorner;
+                this.greaterBoundaryCorner = currentGreaterCorner;
+                return claimResult;
+            }
+        }
+
+        this.lesserBoundaryCorner = newLesserCorner;
+        this.greaterBoundaryCorner = newGreaterCorner;
+        // resize validated, remove invalid chunkHashes
+        final GDClaimManager claimWorldManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(this.world.getUniqueId());
+        if (this.parent == null) {
+            for (Long chunkHash : currentChunkHashes) {
+                Set<Claim> claimsInChunk = claimWorldManager.getInternalChunksToClaimsMap().get(chunkHash);
+                if (claimsInChunk != null && claimsInChunk.size() > 0) {
+                    claimsInChunk.remove(this);
+                }
+            }
+
+            final Set<Long> newChunkHashes = this.getChunkHashes(true);
+            // add new chunk hashes
+            for (Long chunkHash : newChunkHashes) {
+                Set<Claim> claimsInChunk = claimWorldManager.getInternalChunksToClaimsMap().get(chunkHash);
+                if (claimsInChunk == null) {
+                    claimsInChunk = new HashSet<>();
+                    claimWorldManager.getInternalChunksToClaimsMap().put(chunkHash, claimsInChunk);
+                }
+
+                claimsInChunk.add(this);
+            }
+        }
+
+        this.claimData.setLesserBoundaryCorner(BlockUtil.getInstance().posToString(this.lesserBoundaryCorner));
+        this.claimData.setGreaterBoundaryCorner(BlockUtil.getInstance().posToString(this.greaterBoundaryCorner));
+        this.claimData.setRequiresSave(true);
+        this.getClaimStorage().save();
+        if (result.getClaims().size() > 1) {
+            this.migrateClaims(new ArrayList<>(result.getClaims()));
+        }
+        this.resetVisuals();
+        return new GDClaimResult(this, ClaimResultType.SUCCESS);
+    }
+
+    private ClaimResult checkSizeLimits(Player player, GDPlayerData playerData, Vector3i lesserCorner, Vector3i greaterCorner) {
+        if (playerData == null) {
+            return new GDClaimResult(ClaimResultType.SUCCESS);
+        }
+
+        final GDPermissionHolder holder = PermissionHolderCache.getInstance().getOrCreateUser(playerData.playerID);
+        final int minClaimX = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), holder, Options.MIN_SIZE_X, this);
+        final int minClaimY = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), holder, Options.MIN_SIZE_Y, this);
+        final int minClaimZ = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), holder, Options.MIN_SIZE_Z, this);
+        final int maxClaimX = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), holder, Options.MAX_SIZE_X, this);
+        final int maxClaimY = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), holder, Options.MAX_SIZE_Y, this);
+        final int maxClaimZ = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), holder, Options.MAX_SIZE_Z, this);
+
+        // Handle single block selection
+        if ((this.isCuboid() && greaterCorner.equals(lesserCorner)) || (!this.isCuboid() && greaterCorner.getX() == lesserCorner.getX() && greaterCorner.getZ() == lesserCorner.getZ())) {
+            if (playerData.claimResizing != null) {
+                final Component message = MessageCache.getInstance().RESIZE_SAME_LOCATION;
+                GriefDefenderPlugin.sendMessage(player, message);
+                playerData.lastShovelLocation = null;
+                playerData.claimResizing = null;
+                // TODO: Add new result type for this
+                return new GDClaimResult(ClaimResultType.BELOW_MIN_SIZE_X, message);
+            }
+            if (playerData.claimSubdividing == null) {
+                final Component message = MessageCache.getInstance().CREATE_SUBDIVISION_ONLY;
+                GriefDefenderPlugin.sendMessage(player, message);
+                playerData.lastShovelLocation = null;
+                // TODO: Add new result type for this
+                return new GDClaimResult(ClaimResultType.BELOW_MIN_SIZE_X, message);
+            }
+        }
+        Component message = null;
+        String maxCuboidArea = maxClaimX + "x" + maxClaimY + "x" + maxClaimZ;
+        if (maxClaimX == 0 && maxClaimY == 0 && maxClaimZ == 0) {
+            maxCuboidArea = "∞";
+        }
+        String maxArea = maxClaimX + "x" + maxClaimZ;
+        if (maxClaimX == 0 && maxClaimZ == 0) {
+            maxArea = "∞";
+        }
+
+        if (maxClaimX > 0) {
+            int size = Math.abs(greaterCorner.getX() - lesserCorner.getX()) + 1;
+            if (size > maxClaimX) {
+                if (player != null) {
+                    if (this.isCuboid()) {
+                        message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.CLAIM_SIZE_MAX, ImmutableMap.of(
+                                "axis", "x",
+                                "size", size,
+                                "max-size", maxClaimX == 0 ? "∞" : maxClaimX,
+                                "min-area", minClaimX + "x" + minClaimY + "x" + minClaimZ,
+                                "max-area", maxCuboidArea));
+                    } else {
+                        message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.CLAIM_SIZE_MAX, ImmutableMap.of(
+                                "axis", "x",
+                                "size", size,
+                                "max-size", maxClaimX == 0 ? "∞" : maxClaimX,
+                                "min-area", minClaimX + "x" + minClaimZ,
+                                "max-area", maxArea));
+                    }
+                    GriefDefenderPlugin.sendMessage(player, message);
+                }
+                return new GDClaimResult(ClaimResultType.EXCEEDS_MAX_SIZE_X, message);
+            }
+        }
+        if (this.cuboid && maxClaimY > 0) {
+            int size = Math.abs(greaterCorner.getY() - lesserCorner.getY()) + 1;
+            if (size > maxClaimY) {
+                if (player != null) {
+                    if (this.isCuboid()) {
+                        message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.CLAIM_SIZE_MAX, ImmutableMap.of(
+                                "axis", "y",
+                                "size", size,
+                                "max-size", maxClaimY == 0 ? "∞" : maxClaimY,
+                                "min-area", minClaimX + "x" + minClaimY + "x" + minClaimZ,
+                                "max-area", maxCuboidArea));
+                    } else {
+                        message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.CLAIM_SIZE_MAX, ImmutableMap.of(
+                                "axis", "y",
+                                "size", size,
+                                "max-size", maxClaimY == 0 ? "∞" : maxClaimY,
+                                "min-area", minClaimX + "x" + minClaimZ,
+                                "max-area", maxArea));
+                    }
+                    GriefDefenderPlugin.sendMessage(player, message);
+                }
+                return new GDClaimResult(ClaimResultType.EXCEEDS_MAX_SIZE_Y, message);
+            }
+        }
+        if (maxClaimZ > 0) {
+            int size = Math.abs(greaterCorner.getZ() - lesserCorner.getZ()) + 1;
+            if (size > maxClaimZ) {
+                if (player != null) {
+                    if (this.isCuboid()) {
+                        message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.CLAIM_SIZE_MAX, ImmutableMap.of(
+                                "axis", "z",
+                                "size", size,
+                                "max-size", maxClaimZ == 0 ? "∞" : maxClaimZ,
+                                "min-area", minClaimX + "x" + minClaimY + "x" + minClaimZ,
+                                "max-area", maxCuboidArea));
+                    } else {
+                        message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.CLAIM_SIZE_MAX, ImmutableMap.of(
+                                "axis", "z",
+                                "size", size,
+                                "max-size", maxClaimZ == 0 ? "∞" : maxClaimZ,
+                                "min-area", minClaimX + "x" + minClaimZ,
+                                "max-area", maxArea));
+                    }
+                    GriefDefenderPlugin.sendMessage(player, message);
+                }
+                return new GDClaimResult(ClaimResultType.EXCEEDS_MAX_SIZE_Z, message);
+            }
+        }
+        if (minClaimX > 0) {
+            int size = Math.abs(greaterCorner.getX() - lesserCorner.getX()) + 1;
+            if (size < minClaimX) {
+                if (player != null) {
+                    if (this.isCuboid()) {
+                        message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.CLAIM_SIZE_MIN, ImmutableMap.of(
+                                "axis", "x",
+                                "size", size,
+                                "min-size", minClaimX == 0 ? "∞" : minClaimX,
+                                "min-area", minClaimX + "x" + minClaimY + "x" + minClaimZ,
+                                "max-area", maxCuboidArea));
+                    } else {
+                        message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.CLAIM_SIZE_MIN, ImmutableMap.of(
+                                "axis", "x",
+                                "size", size,
+                                "min-size", minClaimX,
+                                "min-area", minClaimX + "x" + minClaimZ,
+                                "max-area", maxArea));
+                    }
+                    GriefDefenderPlugin.sendMessage(player, message);
+                }
+                return new GDClaimResult(ClaimResultType.BELOW_MIN_SIZE_X, message);
+            }
+        }
+        if (this.cuboid && minClaimY > 0) {
+            int size = Math.abs(greaterCorner.getY() - lesserCorner.getY()) + 1;
+            if (size < minClaimY) {
+                if (player != null) {
+                    if (this.isCuboid()) {
+                        message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.CLAIM_SIZE_MIN, ImmutableMap.of(
+                                "axis", "y",
+                                "size", size,
+                                "min-size", minClaimY == 0 ? "∞" : minClaimY,
+                                "min-area", minClaimX + "x" + minClaimY + "x" + minClaimZ,
+                                "max-area", maxCuboidArea));
+                    } else {
+                        message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.CLAIM_SIZE_MIN, ImmutableMap.of(
+                                "axis", "y",
+                                "size", size,
+                                "min-size", minClaimY == 0 ? "∞" : minClaimY,
+                                "min-area", minClaimX + "x" + minClaimZ,
+                                "max-area", maxArea));
+                    }
+                    GriefDefenderPlugin.sendMessage(player, message);
+                }
+                return new GDClaimResult(ClaimResultType.BELOW_MIN_SIZE_Y, message);
+            }
+        }
+        if (minClaimZ > 0) {
+            int size = Math.abs(greaterCorner.getZ() - lesserCorner.getZ()) + 1;
+            if (size < minClaimZ) {
+                if (player != null) {
+                    if (this.isCuboid()) {
+                        message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.CLAIM_SIZE_MIN, ImmutableMap.of(
+                                "axis", "z",
+                                "size", size,
+                                "min-size", minClaimZ == 0 ? "∞" : minClaimZ,
+                                "min-area", minClaimX + "x" + minClaimY + "x" + minClaimZ,
+                                "max-area", maxCuboidArea));
+                    } else {
+                        message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.CLAIM_SIZE_MIN, ImmutableMap.of(
+                                "axis", "z",
+                                "size", size,
+                                "min-size", minClaimZ == 0 ? "∞" : minClaimZ,
+                                "min-area", minClaimX + "x" + minClaimZ,
+                                "max-area", maxArea));
+                    }
+                    GriefDefenderPlugin.sendMessage(player, message);
+                }
+                return new GDClaimResult(ClaimResultType.BELOW_MIN_SIZE_Z, message);
+            }
+        }
+
+        return new GDClaimResult(ClaimResultType.SUCCESS);
+    }
+
+    public void unload() {
+        // clear any references
+        this.world = null;
+        if (this.ownerPlayerData != null) {
+            this.ownerPlayerData.getInternalClaims().remove(this);
+        }
+    }
+
+    @Override
+    public Claim getWilderness() {
+        return this.wildernessClaim;
+    }
+
+    @Override
+    public ClaimManager getClaimManager() {
+        return (ClaimManager) this.worldClaimManager;
+    }
+
+    @Override
+    public Context getContext() {
+        return this.context;
+    }
+
+    public org.spongepowered.api.service.context.Context getSpongeContext() {
+        return this.spongeContext;
+    }
+
+    public Context getInheritContext() {
+        if (this.parent == null || !this.getData().doesInheritParent()) {
+            return this.context;
+        }
+
+        return this.parent.getInheritContext();
+    }
+
+    public boolean hasAdminParent() {
+        if (this.parent == null || this.isAdminClaim()) {
+            return false;
+        }
+
+        if (this.parent.isAdminClaim()) {
+            return true;
+        }
+
+        return this.parent.hasAdminParent();
+    }
+
+    @Override
+    public boolean extend(int newDepth) {
+        return false;
+    }
+
+    @Override
+    public Optional<Claim> getParent() {
+        return Optional.ofNullable(this.parent);
+    }
+
+    @Override
+    public UUID getWorldUniqueId() {
+        return this.world.getUniqueId();
+    }
+
+    public World getWorld() {
+        return this.world;
+    }
+
+    public void setWorld(World world) {
+        this.world = world;
+    }
+    /*@Override
+    public List<Entity> getEntities() {
+        Collection<Entity> worldEntityList = Sponge.getServer().getWorld(this.world.getUniqueId()).get().getEntities();
+        List<Entity> entityList = new ArrayList<>();
+        for (Entity entity : worldEntityList) {
+            if (!(entity.isRemoved() && this.contains(VecHelper.toVector3i(entity.getLocation())))) {
+                entityList.add(entity);
+            }
+        }
+
+        return entityList;
+    }
+
+    @Override
+    public List<Player> getPlayers() {
+        Collection<Player> worldPlayerList = Sponge.getServer().getWorld(this.world.getUniqueId()).get().getPlayers();
+        List<Player> playerList = new ArrayList<>();
+        for (Player player : worldPlayerList) {
+            if (!(player.isRemoved() && this.contains(VecHelper.toVector3i(player.getLocation())))) {
+                playerList.add(player);
+            }
+        }
+
+        return playerList;
+    }*/
+    @Override
+    public List<UUID> getPlayers() {
+        Collection<Player> worldPlayerList = Sponge.getServer().getWorld(this.world.getUniqueId()).get().getPlayers();
+        List<UUID> playerList = new ArrayList<>();
+        for (Player player : worldPlayerList) {
+            if (!(player.isRemoved() && this.contains(player.getLocation().getBlockPosition()))) {
+                playerList.add(player.getUniqueId());
+            }
+        }
+
+        return playerList;
+    }
+
+    @Override
+    public Set<Claim> getChildren(boolean recursive) {
+        if (recursive) {
+            Set<Claim> claimList = new HashSet<>(this.children);
+            List<Claim> subChildren = new ArrayList<>();
+            for (Claim child : claimList) {
+                GDClaim childClaim = (GDClaim) child;
+                if (!childClaim.children.isEmpty()) {
+                    subChildren.addAll(childClaim.getChildren(true));
+                }
+            }
+            claimList.addAll(subChildren);
+            return claimList;
+        }
+        return ImmutableSet.copyOf(this.children);
+    }
+
+    @Override
+    public List<Claim> getParents(boolean recursive) {
+        List<Claim> parents = new ArrayList<>();
+        GDClaim currentClaim = this;
+        while (currentClaim.parent != null) {
+            parents.add(currentClaim.parent);
+            currentClaim = currentClaim.parent;
+        }
+
+        // Index 0 is highest parent while last index represents direct
+        Collections.reverse(parents);
+        return ImmutableList.copyOf(parents);
+    }
+
+    public List<Claim> getInheritedParents() {
+        List<Claim> parents = new ArrayList<>();
+        GDClaim currentClaim = this;
+        while (currentClaim.parent != null && (currentClaim.getData() == null || currentClaim.getData().doesInheritParent())) {
+            if (currentClaim.isAdminClaim()) {
+                if (currentClaim.parent.isAdminClaim()) {
+                    parents.add(currentClaim.parent);
+                }
+            } else {
+                parents.add(currentClaim.parent);
+            }
+            currentClaim = currentClaim.parent;
+        }
+
+        // Index 0 is highest parent while last index represents direct
+        Collections.reverse(parents);
+        return ImmutableList.copyOf(parents);
+    }
+
+    @Override
+    public ClaimResult deleteChild(Claim child) {
+        boolean found = false;
+        for (Claim childClaim : this.children) {
+            if (childClaim.getUniqueId().equals(child.getUniqueId())) {
+                found = true;
+            }
+        }
+
+        if (!found) {
+            return new GDClaimResult(ClaimResultType.CLAIM_NOT_FOUND);
+        }
+
+        final GDClaimManager claimManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(this.world.getUniqueId());
+        return claimManager.deleteClaim(child, true);
+    }
+
+    @Override
+    public ClaimResult deleteChildren() {
+        return this.deleteChildren(null);
+    }
+
+    @Override
+    public ClaimResult deleteChildren(ClaimType claimType) {
+        List<Claim> claimList = new ArrayList<>();
+        for (Claim child : this.children) {
+            if (claimType == null || child.getType() == claimType) {
+                claimList.add(child);
+            }
+        }
+
+        if (claimList.isEmpty()) {
+            return new GDClaimResult(ClaimResultType.CLAIM_NOT_FOUND);
+        }
+
+        GDRemoveClaimEvent event = new GDRemoveClaimEvent(claimList);
+        GriefDefender.getEventManager().post(event);
+        if (event.cancelled()) {
+            return new GDClaimResult(claimList, ClaimResultType.CLAIM_EVENT_CANCELLED);
+        }
+
+        final GDClaimManager claimManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(this.world.getUniqueId());
+        for (Claim child : claimList) {
+            claimManager.deleteClaimInternal(child, true);
+        }
+
+        return new GDClaimResult(event.getClaims(), ClaimResultType.SUCCESS);
+    }
+
+    @Override
+    public ClaimResult changeType(ClaimType type, Optional<UUID> ownerUniqueId) {
+        return changeType(type, ownerUniqueId, null);
+    }
+
+    public ClaimResult changeType(ClaimType type, Optional<UUID> ownerUniqueId, CommandSource src) {
+        if (type == this.type) {
+            return new GDClaimResult(ClaimResultType.SUCCESS);
+        }
+
+        final Player sourcePlayer = src instanceof Player ? (Player) src : null;
+        GDChangeClaimEvent.Type event = new GDChangeClaimEvent.Type(this, type);
+        GriefDefender.getEventManager().post(event);
+        if (event.cancelled()) {
+            return new GDClaimResult(ClaimResultType.CLAIM_EVENT_CANCELLED, event.getMessage().orElse(null));
+        }
+
+        final GDClaimManager claimWorldManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(this.world.getUniqueId());
+        final GDPlayerData sourcePlayerData = src != null && src instanceof Player ? claimWorldManager.getOrCreatePlayerData(((Player) src).getUniqueId()) : null;
+        UUID newOwnerUUID = ownerUniqueId.orElse(this.ownerUniqueId);
+        final ClaimResult result = this.validateClaimType(type, newOwnerUUID, sourcePlayerData);
+        if (!result.successful()) {
+            return result;
+        }
+
+        if (type == ClaimTypes.ADMIN) {
+            newOwnerUUID = GriefDefenderPlugin.ADMIN_USER_UUID;
+        }
+
+        final String fileName = this.getClaimStorage().filePath.getFileName().toString();
+        final Path newPath = this.getClaimStorage().folderPath.getParent().resolve(type.getName().toLowerCase()).resolve(fileName);
+        try {
+            if (Files.notExists(newPath.getParent())) {
+                Files.createDirectories(newPath.getParent());
+            }
+            Files.move(this.getClaimStorage().filePath, newPath);
+            if (type == ClaimTypes.TOWN) {
+                this.setClaimStorage(new TownStorageData(newPath, this.getWorldUniqueId(), newOwnerUUID, this.cuboid));
+            } else {
+                this.setClaimStorage(new ClaimStorageData(newPath, this.getWorldUniqueId(), (ClaimDataConfig) this.getInternalClaimData()));
+            }
+            this.claimData = this.claimStorage.getConfig();
+            this.getClaimStorage().save();
+        } catch (IOException e) {
+            e.printStackTrace();
+            return new GDClaimResult(ClaimResultType.CLAIM_NOT_FOUND, TextComponent.of(e.getMessage()));
+        }
+
+        // If switched to admin or new owner, remove from player claim list
+        if (type == ClaimTypes.ADMIN || !this.ownerUniqueId.equals(newOwnerUUID)) {
+            final Set<Claim> currentPlayerClaims = claimWorldManager.getInternalPlayerClaims(this.ownerUniqueId);
+            if (currentPlayerClaims != null) {
+                currentPlayerClaims.remove(this);
+            }
+        }
+        if (type != ClaimTypes.ADMIN) {
+            final Set<Claim> newPlayerClaims = claimWorldManager.getInternalPlayerClaims(newOwnerUUID);
+            if (newPlayerClaims != null && !newPlayerClaims.contains(this)) {
+                newPlayerClaims.add(this);
+            }
+        }
+
+        if (!this.isAdminClaim() && this.ownerPlayerData != null) {
+            final Player player = Sponge.getServer().getPlayer(this.ownerUniqueId).orElse(null);
+            if (player != null) {
+                this.ownerPlayerData.revertActiveVisual(player);
+            }
+        }
+
+        // revert visuals for all players watching this claim
+        List<UUID> playersWatching = new ArrayList<>(this.playersWatching);
+        for (UUID playerUniqueId : playersWatching) {
+            final Player spongePlayer = Sponge.getServer().getPlayer(playerUniqueId).orElse(null);
+            final GDPlayerData playerData = claimWorldManager.getOrCreatePlayerData(playerUniqueId);
+            if (spongePlayer != null) {
+                playerData.revertActiveVisual(spongePlayer);
+            }
+        }
+
+        if (!newOwnerUUID.equals(GriefDefenderPlugin.ADMIN_USER_UUID)) {
+            this.setOwnerUniqueId(newOwnerUUID);
+        }
+        this.setType(type);
+        this.claimVisual = null;
+        this.getInternalClaimData().setRequiresSave(true);
+        this.getClaimStorage().save();
+        return new GDClaimResult(ClaimResultType.SUCCESS);
+    }
+
+    public ClaimResult validateClaimType(ClaimType type, UUID newOwnerUUID, GDPlayerData playerData) {
+        boolean isAdmin = false;
+        if (playerData != null && (playerData.canManageAdminClaims || playerData.canIgnoreClaim(this))) {
+            isAdmin = true;
+        }
+
+        if (type == ClaimTypes.ADMIN) {
+            if (!isAdmin) {
+                final Component message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.RESULT_TYPE_CHANGE_NOT_ADMIN,
+                        ImmutableMap.of("type", TextComponent.of("ADMIN").color(TextColor.RED)));
+                return new GDClaimResult(ClaimResultType.WRONG_CLAIM_TYPE, message);
+            }
+        } else if (type == ClaimTypes.BASIC) {
+            if (this.isAdminClaim() && newOwnerUUID == null) {
+                return new GDClaimResult(ClaimResultType.REQUIRES_OWNER, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.RESULT_TYPE_REQUIRES_OWNER,
+                        ImmutableMap.of(
+                            "type", TextComponent.of("ADMIN", TextColor.RED),
+                            "target_type", TextComponent.of("BASIC", TextColor.GREEN))));
+            }
+            if (this.parent != null && this.parent.isBasicClaim()) {
+                final Component message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.RESULT_TYPE_CHILD_SAME,
+                        ImmutableMap.of("type", TextComponent.of("BASIC").color(TextColor.GREEN)));
+                return new GDClaimResult(ClaimResultType.WRONG_CLAIM_TYPE, message);
+            }
+            for (Claim child : this.children) {
+                if (!child.isSubdivision()) {
+                    final Component message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.RESULT_TYPE_ONLY_SUBDIVISION,
+                            ImmutableMap.of("type", TextComponent.of("BASIC").color(TextColor.GREEN)));
+                    return new GDClaimResult(ClaimResultType.WRONG_CLAIM_TYPE, message);
+                }
+            }
+        } else if (type == ClaimTypes.SUBDIVISION) {
+            if (!this.children.isEmpty()) {
+                final Component message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.RESULT_TYPE_NO_CHILDREN,
+                        ImmutableMap.of("type", TextComponent.of("SUBDIVISION").color(TextColor.AQUA)));
+                return new GDClaimResult(ClaimResultType.WRONG_CLAIM_TYPE, message);
+            }
+            if (this.parent == null) {
+                final Component message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.RESULT_TYPE_CREATE_DENY,
+                        ImmutableMap.of(
+                                "type", TextComponent.of("SUBDIVISION", TextColor.AQUA),
+                                "target_type", TextComponent.of("WILDERNESS", TextColor.GREEN)));
+                return new GDClaimResult(ClaimResultType.WRONG_CLAIM_TYPE, message);
+            }
+            if (this.isAdminClaim() && newOwnerUUID == null) {
+                return new GDClaimResult(ClaimResultType.REQUIRES_OWNER, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.RESULT_TYPE_REQUIRES_OWNER,
+                        ImmutableMap.of(
+                                "type", TextComponent.of("ADMIN", TextColor.RED),
+                                "target_type", TextComponent.of("SUBDIVISION", TextColor.AQUA))));
+            }
+        } else if (type == ClaimTypes.TOWN) {
+            if (this.parent != null && this.parent.isTown()) {
+                final Component message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.RESULT_TYPE_NO_CHILDREN,
+                        ImmutableMap.of("type", TextComponent.of("TOWN").color(TextColor.GREEN)));
+                return new GDClaimResult(ClaimResultType.WRONG_CLAIM_TYPE, message);
+            }
+        } else if (type == ClaimTypes.WILDERNESS) {
+            final Component message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.RESULT_TYPE_CHANGE_DENY,
+                    ImmutableMap.of("type", TextComponent.of("WILDERNESS").color(TextColor.GREEN)));
+            return new GDClaimResult(ClaimResultType.WRONG_CLAIM_TYPE, message);
+        }
+
+        return new GDClaimResult(ClaimResultType.SUCCESS);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || !(o instanceof GDClaim)) {
+            return false;
+        }
+        GDClaim that = (GDClaim) o;
+        return this.type == that.type &&
+               Objects.equal(this.id, that.id);
+    }
+
+    @Override
+    public int hashCode() {
+        return this.hashCode;
+    }
+
+    @Override
+    public List<UUID> getUserTrusts() {
+        List<UUID> trustList = new ArrayList<>();
+        trustList.addAll(this.claimData.getAccessors());
+        trustList.addAll(this.claimData.getContainers());
+        trustList.addAll(this.claimData.getBuilders());
+        trustList.addAll(this.claimData.getManagers());
+        return ImmutableList.copyOf(trustList);
+    }
+
+    @Override
+    public List<UUID> getUserTrusts(TrustType type) {
+        return ImmutableList.copyOf(this.getUserTrustList(type));
+    }
+
+    public boolean isUserTrusted(User user, TrustType type) {
+        final GDPermissionUser gdUser = PermissionHolderCache.getInstance().getOrCreateUser(user);
+        return isUserTrusted(gdUser, type, null);
+    }
+
+    public boolean isUserTrusted(GDPermissionUser user, TrustType type) {
+        return isUserTrusted(user, type, null);
+    }
+
+    @Override
+    public boolean isUserTrusted(UUID uuid, TrustType type) {
+        final GDPermissionUser user = PermissionHolderCache.getInstance().getOrCreateUser(uuid);
+        return isUserTrusted(user, type, null);
+    }
+
+    public boolean isUserTrusted(GDPermissionUser user, TrustType type, Set<Context> contexts) {
+        return isUserTrusted(user, type, contexts, false);
+    }
+
+    public boolean isUserTrusted(GDPermissionUser user, TrustType type, Set<Context> contexts, boolean forced) {
+        if (user == null) {
+            return false;
+        }
+
+        final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(this.world.getUniqueId(), user.getUniqueId());
+        if (!playerData.canIgnoreClaim(this) && this.getInternalClaimData().isExpired()) {
+            return false;
+        }
+        if (forced || !playerData.debugClaimPermissions) {
+            if (user.getUniqueId().equals(this.getOwnerUniqueId())) {
+                return true;
+            }
+            if (this.isAdminClaim() && playerData.canManageAdminClaims) {
+                return true;
+            }
+            if (this.isWilderness() && playerData.canManageWilderness) {
+                return true;
+            }
+            if (playerData.canIgnoreClaim(this)) {
+                return true;
+            }
+        }
+
+        if (type == null) {
+            return true;
+        }
+        if (this.isPublicTrusted(type)) {
+            return true;
+        }
+
+        if (type == TrustTypes.ACCESSOR) {
+            if (this.claimData.getAccessors().contains(user.getUniqueId())) {
+                return true;
+            }
+            if (this.claimData.getBuilders().contains(user.getUniqueId())) {
+                return true;
+            }
+            if (this.claimData.getContainers().contains(user.getUniqueId())) {
+                return true;
+            }
+            if (this.claimData.getManagers().contains(user.getUniqueId())) {
+                return true;
+            }
+        } else if (type == TrustTypes.BUILDER) {
+            if (this.claimData.getBuilders().contains(user.getUniqueId())) {
+                return true;
+            }
+            if (this.claimData.getManagers().contains(user.getUniqueId())) {
+                return true;
+            }
+        } else if (type == TrustTypes.CONTAINER) {
+            if (this.claimData.getContainers().contains(user.getUniqueId())) {
+                return true;
+            }
+            if (this.claimData.getBuilders().contains(user.getUniqueId())) {
+                return true;
+            }
+            if (this.claimData.getManagers().contains(user.getUniqueId())) {
+                return true;
+            }
+        } else if (type == TrustTypes.MANAGER) {
+            if (this.claimData.getManagers().contains(user.getUniqueId())) {
+                return true;
+            }
+        }
+
+        if (contexts == null) {
+            contexts = new HashSet<>();
+            contexts.add(this.getContext());
+        }
+
+        if (PermissionUtil.getInstance().getPermissionValue(this, user, GDPermissions.getTrustPermission(type), contexts) == Tristate.TRUE) {
+            return true;
+        }
+
+        // Only check parent if this claim inherits
+        if (this.parent != null && this.getData().doesInheritParent()) {
+            return this.parent.isUserTrusted(user, type, contexts);
+        }
+
+        return false;
+    }
+
+    private boolean isPublicTrusted(TrustType type) {
+        if (type == TrustTypes.ACCESSOR) {
+            if (this.claimData.getAccessors().contains(GriefDefenderPlugin.PUBLIC_UUID)) {
+                return true;
+            }
+            if (this.claimData.getBuilders().contains(GriefDefenderPlugin.PUBLIC_UUID)) {
+                return true;
+            }
+            if (this.claimData.getContainers().contains(GriefDefenderPlugin.PUBLIC_UUID)) {
+                return true;
+            }
+            if (this.claimData.getManagers().contains(GriefDefenderPlugin.PUBLIC_UUID)) {
+                return true;
+            }
+        } else if (type == TrustTypes.BUILDER) {
+            if (this.claimData.getBuilders().contains(GriefDefenderPlugin.PUBLIC_UUID)) {
+                return true;
+            }
+            if (this.claimData.getManagers().contains(GriefDefenderPlugin.PUBLIC_UUID)) {
+                return true;
+            }
+        } else if (type == TrustTypes.CONTAINER) {
+            if (this.claimData.getContainers().contains(GriefDefenderPlugin.PUBLIC_UUID)) {
+                return true;
+            }
+            if (this.claimData.getBuilders().contains(GriefDefenderPlugin.PUBLIC_UUID)) {
+                return true;
+            }
+            if (this.claimData.getManagers().contains(GriefDefenderPlugin.PUBLIC_UUID)) {
+                return true;
+            }
+        } else if (type == TrustTypes.MANAGER) {
+            if (this.claimData.getManagers().contains(GriefDefenderPlugin.PUBLIC_UUID)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    @Override
+    public boolean isGroupTrusted(String name, TrustType type) {
+        if (name == null) {
+            return false;
+        }
+
+        if (!PermissionUtil.getInstance().hasGroupSubject(name)) {
+            return false;
+        }
+
+        final GDPermissionHolder holder = PermissionHolderCache.getInstance().getOrCreateHolder(name);
+        Set<Context> contexts = new HashSet<>();
+        contexts.add(this.getContext());
+
+        return PermissionUtil.getInstance().getPermissionValue(this, holder, GDPermissions.getTrustPermission(type), contexts) == Tristate.TRUE;
+    }
+
+    @Override
+    public ClaimResult addUserTrust(UUID uuid, TrustType type) {
+        GDUserTrustClaimEvent.Add event = new GDUserTrustClaimEvent.Add(this, ImmutableList.of(uuid), type);
+        GriefDefender.getEventManager().post(event);
+        if (event.cancelled()) {
+            return new GDClaimResult(ClaimResultType.CLAIM_EVENT_CANCELLED, event.getMessage().orElse(null));
+        }
+
+        List<UUID> userList = this.getUserTrustList(type);
+        if (!userList.contains(uuid)) {
+            userList.add(uuid);
+        }
+
+        this.claimData.setRequiresSave(true);
+        this.claimData.save();
+        return new GDClaimResult(this, ClaimResultType.SUCCESS);
+    }
+
+    @Override
+    public ClaimResult addUserTrusts(List<UUID> uuids, TrustType type) {
+        GDUserTrustClaimEvent.Add event = new GDUserTrustClaimEvent.Add(this, uuids, type);
+        GriefDefender.getEventManager().post(event);
+        if (event.cancelled()) {
+            return new GDClaimResult(ClaimResultType.CLAIM_EVENT_CANCELLED, event.getMessage().orElse(null));
+        }
+
+        for (UUID uuid : uuids) {
+            List<UUID> userList = this.getUserTrustList(type);
+            if (!userList.contains(uuid)) {
+                userList.add(uuid);
+            }
+        }
+
+        this.claimData.setRequiresSave(true);
+        this.claimData.save();
+        return new GDClaimResult(this, ClaimResultType.SUCCESS);
+    }
+
+    @Override
+    public ClaimResult removeUserTrust(UUID uuid, TrustType type) {
+        GDUserTrustClaimEvent.Remove event = new GDUserTrustClaimEvent.Remove(this, ImmutableList.of(uuid), type);
+        GriefDefender.getEventManager().post(event);
+        if (event.cancelled()) {
+            return new GDClaimResult(ClaimResultType.CLAIM_EVENT_CANCELLED, event.getMessage().orElse(null));
+        }
+
+        if (type == TrustTypes.NONE) {
+            final ClaimResult result = this.removeAllTrustsFromUser(uuid);
+            this.claimData.setRequiresSave(true);
+            this.claimData.save();
+            return result;
+        }
+
+        this.getUserTrustList(type).remove(uuid);
+        this.claimData.setRequiresSave(true);
+        this.claimData.save();
+        return new GDClaimResult(this, ClaimResultType.SUCCESS);
+    }
+
+    @Override
+    public ClaimResult removeUserTrusts(List<UUID> uuids, TrustType type) {
+        GDUserTrustClaimEvent.Remove event = new GDUserTrustClaimEvent.Remove(this, uuids, type);
+        GriefDefender.getEventManager().post(event);
+        if (event.cancelled()) {
+            return new GDClaimResult(ClaimResultType.CLAIM_EVENT_CANCELLED, event.getMessage().orElse(null));
+        }
+
+        if (type == TrustTypes.NONE) {
+            for (UUID uuid : uuids) {
+                this.removeAllTrustsFromUser(uuid);
+            }
+
+            this.claimData.setRequiresSave(true);
+            this.claimData.save();
+            return new GDClaimResult(this, ClaimResultType.SUCCESS);
+        }
+
+        List<UUID> userList = this.getUserTrustList(type);
+        for (UUID uuid : uuids) {
+            if (userList.contains(uuid)) {
+                userList.remove(uuid);
+            }
+        }
+
+        this.claimData.setRequiresSave(true);
+        this.claimData.save();
+        return new GDClaimResult(this, ClaimResultType.SUCCESS);
+    }
+
+    @Override
+    public ClaimResult addGroupTrust(String group, TrustType type) {
+        GDGroupTrustClaimEvent.Add event = new GDGroupTrustClaimEvent.Add(this, ImmutableList.of(group), type);
+        GriefDefender.getEventManager().post(event);
+        if (event.cancelled()) {
+            return new GDClaimResult(ClaimResultType.CLAIM_EVENT_CANCELLED, event.getMessage().orElse(null));
+        }
+
+        List<String> groupList = this.getGroupTrustList(type);
+        if (!groupList.contains(group)) {
+            groupList.add(group);
+        }
+
+        this.claimData.setRequiresSave(true);
+        this.claimData.save();
+        return new GDClaimResult(this, ClaimResultType.SUCCESS);
+    }
+
+    @Override
+    public ClaimResult addGroupTrusts(List<String> groups, TrustType type) {
+        GDGroupTrustClaimEvent.Add event = new GDGroupTrustClaimEvent.Add(this, groups, type);
+        GriefDefender.getEventManager().post(event);
+        if (event.cancelled()) {
+            return new GDClaimResult(ClaimResultType.CLAIM_EVENT_CANCELLED, event.getMessage().orElse(null));
+        }
+
+        for (String group : groups) {
+            List<String> groupList = this.getGroupTrustList(type);
+            if (!groupList.contains(group)) {
+                groupList.add(group);
+            }
+        }
+
+        this.claimData.setRequiresSave(true);
+        this.claimData.save();
+        return new GDClaimResult(this, ClaimResultType.SUCCESS);
+    }
+
+    @Override
+    public ClaimResult removeGroupTrust(String group, TrustType type) {
+        GDGroupTrustClaimEvent.Remove event = new GDGroupTrustClaimEvent.Remove(this, ImmutableList.of(group), type);
+        GriefDefender.getEventManager().post(event);
+        if (event.cancelled()) {
+            return new GDClaimResult(ClaimResultType.CLAIM_EVENT_CANCELLED, event.getMessage().orElse(null));
+        }
+
+        if (type == TrustTypes.NONE) {
+            final ClaimResult result = this.removeAllTrustsFromGroup(group);
+            this.claimData.setRequiresSave(true);
+            this.claimData.save();
+            return result;
+        }
+
+        this.getGroupTrustList(type).remove(group);
+        this.claimData.setRequiresSave(true);
+        this.claimData.save();
+        return new GDClaimResult(this, ClaimResultType.SUCCESS);
+    }
+
+    @Override
+    public ClaimResult removeGroupTrusts(List<String> groups, TrustType type) {
+        GDGroupTrustClaimEvent.Remove event = new GDGroupTrustClaimEvent.Remove(this, groups, type);
+        GriefDefender.getEventManager().post(event);
+        if (event.cancelled()) {
+            return new GDClaimResult(ClaimResultType.CLAIM_EVENT_CANCELLED, event.getMessage().orElse(null));
+        }
+
+        if (type == TrustTypes.NONE) {
+            for (String group : groups) {
+                this.removeAllTrustsFromGroup(group);
+            }
+
+            this.claimData.setRequiresSave(true);
+            this.claimData.save();
+            return new GDClaimResult(this, ClaimResultType.SUCCESS);
+        }
+
+        List<String> groupList = this.getGroupTrustList(type);
+        for (String group : groups) {
+            if (groupList.contains(group)) {
+                groupList.remove(group);
+            }
+        }
+
+        this.claimData.setRequiresSave(true);
+        this.claimData.save();
+        return new GDClaimResult(this, ClaimResultType.SUCCESS);
+    }
+
+    @Override
+    public ClaimResult removeAllTrusts() {
+        List<UUID> userTrustList = this.getUserTrusts();
+        GDUserTrustClaimEvent.Remove userEvent = new GDUserTrustClaimEvent.Remove(this, userTrustList, TrustTypes.NONE);
+        GriefDefender.getEventManager().post(userEvent);
+        if (userEvent.cancelled()) {
+            return new GDClaimResult(ClaimResultType.CLAIM_EVENT_CANCELLED, userEvent.getMessage().orElse(null));
+        }
+
+        List<String> groupTrustList = this.getGroupTrusts();
+        GDGroupTrustClaimEvent.Remove event = new GDGroupTrustClaimEvent.Remove(this, groupTrustList, TrustTypes.NONE);
+        GriefDefender.getEventManager().post(event);
+        if (event.cancelled()) {
+            return new GDClaimResult(ClaimResultType.CLAIM_EVENT_CANCELLED, event.getMessage().orElse(null));
+        }
+
+        for (TrustType type : TrustTypeRegistryModule.getInstance().getAll()) {
+            this.getUserTrustList(type).clear();
+        }
+
+        for (TrustType type : TrustTypeRegistryModule.getInstance().getAll()) {
+            this.getGroupTrustList(type).clear();
+        }
+
+        this.claimData.setRequiresSave(true);
+        this.claimData.save();
+        return new GDClaimResult(this, ClaimResultType.SUCCESS);
+    }
+
+    @Override
+    public ClaimResult removeAllUserTrusts() {
+        List<UUID> trustList = this.getUserTrusts();
+        GDUserTrustClaimEvent.Remove event = new GDUserTrustClaimEvent.Remove(this, trustList, TrustTypes.NONE);
+        GriefDefender.getEventManager().post(event);
+        if (event.cancelled()) {
+            return new GDClaimResult(ClaimResultType.CLAIM_EVENT_CANCELLED, event.getMessage().orElse(null));
+        }
+
+        for (TrustType type : TrustTypeRegistryModule.getInstance().getAll()) {
+            this.getUserTrustList(type).clear();
+        }
+
+        this.claimData.setRequiresSave(true);
+        this.claimData.save();
+        return new GDClaimResult(this, ClaimResultType.SUCCESS);
+    }
+
+    @Override
+    public ClaimResult removeAllGroupTrusts() {
+        List<String> trustList = this.getGroupTrusts();
+        GDGroupTrustClaimEvent.Remove event = new GDGroupTrustClaimEvent.Remove(this, trustList, TrustTypes.NONE);
+        GriefDefender.getEventManager().post(event);
+        if (event.cancelled()) {
+            return new GDClaimResult(ClaimResultType.CLAIM_EVENT_CANCELLED, event.getMessage().orElse(null));
+        }
+
+        for (TrustType type : TrustTypeRegistryModule.getInstance().getAll()) {
+            this.getGroupTrustList(type).clear();
+        }
+
+        this.claimData.setRequiresSave(true);
+        this.claimData.save();
+        return new GDClaimResult(this, ClaimResultType.SUCCESS);
+    }
+
+    public ClaimResult removeAllTrustsFromUser(UUID userUniqueId) {
+        for (TrustType type : TrustTypeRegistryModule.getInstance().getAll()) {
+            this.getUserTrustList(type).remove(userUniqueId);
+        }
+
+        return new GDClaimResult(this, ClaimResultType.SUCCESS);
+    }
+
+    public ClaimResult removeAllTrustsFromGroup(String group) {
+        for (TrustType type : TrustTypeRegistryModule.getInstance().getAll()) {
+            this.getGroupTrustList(type).remove(group);
+        }
+
+        return new GDClaimResult(this, ClaimResultType.SUCCESS);
+    }
+
+    public List<UUID> getUserTrustList(TrustType type) {
+        if (type == TrustTypes.NONE) {
+            return new ArrayList<>();
+        }
+        if (type == TrustTypes.ACCESSOR) {
+            return this.claimData.getAccessors();
+        }
+        if (type == TrustTypes.CONTAINER) {
+            return this.claimData.getContainers();
+        }
+        if (type == TrustTypes.BUILDER) {
+            return this.claimData.getBuilders();
+        }
+        return this.claimData.getManagers();
+    }
+
+    public List<UUID> getParentUserTrustList(TrustType type) {
+        List<UUID> userList = new ArrayList<>();
+        for (Claim claim : this.getInheritedParents()) {
+            GDClaim parentClaim = (GDClaim) claim;
+            userList.addAll(parentClaim.getUserTrusts(type));
+        }
+        return userList;
+    }
+
+    public List<String> getParentGroupTrustList(TrustType type) {
+        List<String> trustList = new ArrayList<>();
+        for (Claim claim : this.getInheritedParents()) {
+            GDClaim parentClaim = (GDClaim) claim;
+            trustList.addAll(parentClaim.getGroupTrusts(type));
+        }
+        return trustList;
+    }
+
+    public List<UUID> getUserTrustList(TrustType type, boolean includeParents) {
+        List<UUID> trustList = new ArrayList<>();
+        if (type == TrustTypes.ACCESSOR) {
+            trustList.addAll(this.claimData.getAccessors());
+        } else if (type == TrustTypes.CONTAINER) {
+            trustList.addAll(this.claimData.getContainers());
+        } else if (type == TrustTypes.BUILDER) {
+            trustList.addAll(this.claimData.getBuilders());
+        } else {
+            trustList.addAll(this.claimData.getManagers());
+        }
+
+        if (includeParents) {
+            List<UUID> parentList = getParentUserTrustList(type);
+            for (UUID uuid : parentList) {
+                if (!trustList.contains(uuid)) {
+                    trustList.add(uuid);
+                }
+            }
+        }
+
+        return trustList;
+    }
+
+    public List<String> getGroupTrustList(TrustType type) {
+        if (type == TrustTypes.NONE) {
+            return new ArrayList<>();
+        }
+        if (type == TrustTypes.ACCESSOR) {
+            return this.claimData.getAccessorGroups();
+        }
+        if (type == TrustTypes.CONTAINER) {
+            return this.claimData.getContainerGroups();
+        }
+        if (type == TrustTypes.BUILDER) {
+            return this.claimData.getBuilderGroups();
+        }
+        return this.claimData.getManagerGroups();
+    }
+
+    public List<String> getGroupTrustList(TrustType type, boolean includeParents) {
+        List<String> trustList = new ArrayList<>();
+        if (type == TrustTypes.ACCESSOR) {
+            trustList.addAll(this.claimData.getAccessorGroups());
+        } else if (type == TrustTypes.CONTAINER) {
+            trustList.addAll(this.claimData.getContainerGroups());
+        } else if (type == TrustTypes.BUILDER) {
+            trustList.addAll(this.claimData.getBuilderGroups());
+        } else {
+            trustList.addAll(this.claimData.getManagerGroups());
+        }
+
+        if (includeParents) {
+            List<String> parentList = getParentGroupTrustList(type);
+            for (String groupId : parentList) {
+                if (!trustList.contains(groupId)) {
+                    trustList.add(groupId);
+                }
+            }
+        }
+
+        return trustList;
+    }
+
+    @Override
+    public List<String> getGroupTrusts() {
+        List<String> groups = new ArrayList<>();
+        groups.addAll(this.getInternalClaimData().getAccessorGroups());
+        groups.addAll(this.getInternalClaimData().getBuilderGroups());
+        groups.addAll(this.getInternalClaimData().getContainerGroups());
+        groups.addAll(this.getInternalClaimData().getManagerGroups());
+        return ImmutableList.copyOf(groups);
+    }
+
+    @Override
+    public List<String> getGroupTrusts(TrustType type) {
+        return ImmutableList.copyOf(this.getGroupTrustList(type));
+    }
+
+    @Override
+    public Optional<UUID> getEconomyAccountId() {
+        if (this.isAdminClaim() || this.isSubdivision() || !GriefDefenderPlugin.getGlobalConfig().getConfig().claim.bankTaxSystem) {
+            return Optional.empty();
+        }
+
+        if (this.economyAccount != null) {
+            return Optional.of(this.id);
+        }
+        EconomyService economyService = GriefDefenderPlugin.getInstance().economyService.orElse(null);
+        if (economyService != null) {
+            this.economyAccount = economyService.getOrCreateAccount(this.id.toString()).orElse(null);
+            return Optional.ofNullable(this.id);
+        }
+        return Optional.empty();
+    }
+
+    public Account getEconomyAccount() {
+        if (this.isAdminClaim() || this.isSubdivision() || !GriefDefenderPlugin.getGlobalConfig().getConfig().claim.bankTaxSystem) {
+            return null;
+        }
+
+        if (this.economyAccount != null) {
+            return this.economyAccount;
+        }
+        EconomyService economyService = GriefDefenderPlugin.getInstance().economyService.orElse(null);
+        if (economyService != null) {
+            this.economyAccount = economyService.getOrCreateAccount(this.id.toString()).orElse(null);
+            return this.economyAccount;
+        }
+
+        return null;
+    }
+
+    public static class ClaimBuilder implements Builder {
+
+        private UUID ownerUniqueId;
+        private ClaimType type = ClaimTypes.BASIC;
+        private boolean cuboid = false;
+        private boolean requiresClaimBlocks = true;
+        private boolean denyMessages = true;
+        private boolean expire = true;
+        private boolean resizable = true;
+        private boolean inherit = true;
+        private boolean overrides = true;
+        private boolean createLimitRestrictions = true;
+        private boolean levelRestrictions = true;
+        private boolean sizeRestrictions = true;
+        private UUID worldUniqueId;
+        private Vector3i point1;
+        private Vector3i point2;
+        private Vector3i spawnPos;
+        private Component greeting;
+        private Component farewell;
+        private Claim parent;
+
+        public ClaimBuilder() {
+            
+        }
+
+        @Override
+        public Builder bounds(Vector3i point1, Vector3i point2) {
+            this.point1 = point1;
+            this.point2 = point2;
+            return this;
+        }
+
+        @Override
+        public Builder cuboid(boolean cuboid) {
+            this.cuboid = cuboid;
+            return this;
+        }
+
+        @Override
+        public Builder owner(UUID ownerUniqueId) {
+            this.ownerUniqueId = ownerUniqueId;
+            return this;
+        }
+
+        @Override
+        public Builder parent(Claim parentClaim) {
+            this.parent = parentClaim;
+            return this;
+        }
+
+        @Override
+        public Builder type(ClaimType type) {
+            this.type = type;
+            return this;
+        }
+
+        @Override
+        public Builder world(UUID worldUniqueId) {
+            this.worldUniqueId = worldUniqueId;
+            return this;
+        }
+
+        @Override
+        public Builder createLimitRestrictions(boolean checkCreateLimit) {
+            this.createLimitRestrictions = checkCreateLimit;
+            return this;
+        }
+
+        @Override
+        public Builder levelRestrictions(boolean checkLevel) {
+            this.levelRestrictions = checkLevel;
+            return this;
+        }
+
+        @Override
+        public Builder sizeRestrictions(boolean checkSize) {
+            this.sizeRestrictions = checkSize;
+            return this;
+        }
+
+        @Override
+        public Builder requireClaimBlocks(boolean requiresClaimBlocks) {
+            this.requiresClaimBlocks = requiresClaimBlocks;
+            return this;
+        }
+
+        @Override
+        public Builder denyMessages(boolean allowDenyMessages) {
+            this.denyMessages = allowDenyMessages;
+            return this;
+        }
+
+        @Override
+        public Builder expire(boolean allowExpire) {
+            this.expire = allowExpire;
+            return this;
+        }
+
+        @Override
+        public Builder inherit(boolean inherit) {
+            this.inherit = inherit;
+            return this;
+        }
+
+        @Override
+        public Builder resizable(boolean allowResize) {
+            this.resizable = allowResize;
+            return this;
+        }
+
+        @Override
+        public Builder overrides(boolean allowOverrides) {
+            this.overrides = allowOverrides;
+            return this;
+        }
+
+        @Override
+        public Builder farewell(Component farewell) {
+            this.farewell = farewell;
+            return this;
+        }
+
+        @Override
+        public Builder greeting(Component greeting) {
+            this.greeting = greeting;
+            return this;
+        }
+
+        @Override
+        public Builder spawnPos(Vector3i spawnPos) {
+            this.spawnPos = spawnPos;
+            return this;
+        }
+
+        @Override
+        public Builder reset() {
+            this.ownerUniqueId = null;
+            this.type = ClaimTypes.BASIC;
+            this.cuboid = false;
+            this.worldUniqueId = null;
+            this.point1 = null;
+            this.point2 = null;
+            this.parent = null;
+            return this;
+        }
+
+        @Override
+        public ClaimResult build() {
+            checkNotNull(this.type);
+            checkNotNull(this.worldUniqueId);
+            checkNotNull(this.point1);
+            checkNotNull(this.point2);
+
+            final World world = Sponge.getServer().getWorld(this.worldUniqueId).orElse(null);
+            if (world == null) {
+                return new GDClaimResult(ClaimResultType.WORLD_NOT_FOUND);
+            }
+
+            if (this.type == ClaimTypes.SUBDIVISION) {
+                checkNotNull(this.parent);
+            }
+
+            if (this.type == ClaimTypes.ADMIN || this.type == ClaimTypes.WILDERNESS) {
+                this.sizeRestrictions = false;
+                this.levelRestrictions = false;
+            }
+
+            GDClaim claim = null;
+            if (this.type == ClaimTypes.TOWN) {
+                claim = new GDTown(world, this.point1, this.point2, this.type, this.ownerUniqueId, this.cuboid);
+            } else {
+                claim = new GDClaim(world, this.point1, this.point2, this.type, this.ownerUniqueId, this.cuboid);
+            }
+            claim.parent = (GDClaim) this.parent;
+            Player player = null;
+            final Object source = GDCauseStackManager.getInstance().getCurrentCause().root();
+            if (source instanceof GDPermissionUser) {
+                final GDPermissionUser user = (GDPermissionUser) source;
+                player = (Player) user.getOnlinePlayer();
+            }
+            GDPlayerData playerData = null;
+            double requiredFunds = 0;
+
+            if (this.ownerUniqueId != null) {
+                if (playerData == null) {
+                    playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(this.worldUniqueId, this.ownerUniqueId);
+                }
+
+                if (this.levelRestrictions) {
+                    final int minClaimLevel = claim.getOwnerMinClaimLevel();
+                    if (claim.getLesserBoundaryCorner().getY() < minClaimLevel) {
+                        Component message = null;
+                        if (player != null) {
+                            message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.CLAIM_BELOW_LEVEL, ImmutableMap.of(
+                                    "limit", minClaimLevel));
+                            GriefDefenderPlugin.sendMessage(player, message);
+                        }
+                        return new GDClaimResult(claim, ClaimResultType.BELOW_MIN_LEVEL, message);
+                    }
+                    final int maxClaimLevel = claim.getOwnerMaxClaimLevel();
+                    if (claim.getGreaterBoundaryCorner().getY() > maxClaimLevel) {
+                        Component message = null;
+                        if (player != null) {
+                            message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.CLAIM_ABOVE_LEVEL, ImmutableMap.of(
+                                    "limit", maxClaimLevel));
+                            GriefDefenderPlugin.sendMessage(player, message);
+                        }
+                        return new GDClaimResult(claim, ClaimResultType.ABOVE_MAX_LEVEL, message);
+                    }
+                }
+
+                if (this.sizeRestrictions) {
+                    ClaimResult claimResult = claim.checkSizeLimits(player, playerData, this.point1, this.point2);
+                    if (!claimResult.successful()) {
+                        return claimResult;
+                    }
+                }
+
+                final GDPermissionUser user = PermissionHolderCache.getInstance().getOrCreateUser(this.ownerUniqueId);
+                if (this.createLimitRestrictions && !PermissionUtil.getInstance().holderHasPermission(user, GDPermissions.BYPASS_CLAIM_LIMIT)) {
+                    final int createClaimLimit = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), user, Options.CREATE_LIMIT, claim);
+                    if (createClaimLimit > -1 && (playerData.getClaimTypeCount(claim.getType()) + 1) > createClaimLimit) {
+                        final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.CREATE_FAILED_CLAIM_LIMIT, ImmutableMap.of(
+                                "limit", createClaimLimit,
+                                "type", claim.getType().getName()));
+                        if (player != null) {
+                            GriefDefenderPlugin.sendMessage(player, message);
+                        }
+                        return new GDClaimResult(claim, ClaimResultType.EXCEEDS_MAX_CLAIM_LIMIT, message);
+                    }
+                }
+
+                // check player has enough claim blocks
+                if ((claim.isBasicClaim() || claim.isTown()) && this.requiresClaimBlocks) {
+                    final int claimCost = BlockUtil.getInstance().getClaimBlockCost(world, claim.lesserBoundaryCorner, claim.greaterBoundaryCorner, claim.cuboid);
+                    if (GriefDefenderPlugin.getInstance().isEconomyModeEnabled()) {
+                        final GDClaimResult result = EconomyUtil.getInstance().checkEconomyFunds(claim, playerData, true);
+                        if (!result.successful()) {
+                            return result;
+                        }
+                        requiredFunds = claimCost * claim.getOwnerEconomyBlockCost();
+                    } else {
+                        final int remainingClaimBlocks = playerData.getRemainingClaimBlocks() - claimCost;
+                        if (remainingClaimBlocks < 0) {
+                            Component message = null;
+                            if (player != null) {
+                                if (GriefDefenderPlugin.CLAIM_BLOCK_SYSTEM == ClaimBlockSystem.VOLUME) {
+                                    final double claimableChunks = Math.abs(remainingClaimBlocks / 65536.0);
+                                    message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.CLAIM_SIZE_NEED_BLOCKS_3D, ImmutableMap.of(
+                                            "chunk-amount", Math.round(claimableChunks * 100.0)/100.0,
+                                            "block-amount", Math.abs(remainingClaimBlocks)));
+                                } else {
+                                    message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.CLAIM_SIZE_NEED_BLOCKS_2D, ImmutableMap.of(
+                                            "block-amount", Math.abs(remainingClaimBlocks)));
+                                }
+                                GriefDefenderPlugin.sendMessage(player, message);
+                            }
+                            //playerData.lastShovelLocation = null;
+                            playerData.claimResizing = null;
+                            return new GDClaimResult(ClaimResultType.INSUFFICIENT_CLAIM_BLOCKS, message);
+                        }
+                    }
+                }
+
+                if (!GriefDefenderPlugin.getInstance().isEconomyModeEnabled() && claim.isTown() && player != null) {
+                    final double townCost = GriefDefenderPlugin.getGlobalConfig().getConfig().town.cost;
+                    if (townCost > 0) {
+                        Account playerAccount = GriefDefenderPlugin.getInstance().economyService.get().getOrCreateAccount(player.getUniqueId()).orElse(null);
+                        if (playerAccount == null) {
+                            final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.ECONOMY_PLAYER_NOT_FOUND, ImmutableMap.of(
+                                    "player", player.getName()));
+                            GriefDefenderPlugin.sendMessage(player, message);
+                            return new GDClaimResult(claim, ClaimResultType.NOT_ENOUGH_FUNDS, message);
+                        }
+                        final double balance = playerAccount.getBalance(GriefDefenderPlugin.getInstance().economyService.get().getDefaultCurrency()).doubleValue();
+                        if (balance < townCost) {
+                            final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.TOWN_CREATE_NOT_ENOUGH_FUNDS, ImmutableMap.of(
+                                    "amount", String.valueOf("$" +townCost),
+                                    "balance", String.valueOf("$" + balance),
+                                    "amount-needed", String.valueOf("$" + (townCost - balance))));
+                            GriefDefenderPlugin.sendMessage(player, message);
+                            return new GDClaimResult(claim, ClaimResultType.NOT_ENOUGH_FUNDS, message);
+                        }
+                        final Currency defaultCurrency = GriefDefenderPlugin.getInstance().economyService.get().getDefaultCurrency();
+                        playerAccount.withdraw(defaultCurrency, BigDecimal.valueOf(townCost), Cause.of(EventContext.empty(), GDCauseStackManager.getInstance().getCurrentCause().all()));
+                    }
+                }
+            }
+
+            final ClaimResult claimResult = claim.checkArea(false);
+            if (!claimResult.successful()) {
+                if (player != null && (claim.isBasicClaim() || claim.isTown()) && this.requiresClaimBlocks && GriefDefenderPlugin.getGlobalConfig().getConfig().economy.economyMode) {
+                    EconomyUtil.depositFunds(player.getUniqueId(), requiredFunds);
+                }
+                return claimResult;
+            }
+
+            GDCreateClaimEvent.Pre event = new GDCreateClaimEvent.Pre(claim);
+            GriefDefender.getEventManager().post(event);
+            if (event.cancelled()) {
+                final Component message = event.getMessage().orElse(null);
+                if (message != null && player != null) {
+                    GriefDefenderPlugin.sendMessage(player, message);
+                }
+                if (player != null && (claim.isBasicClaim() || claim.isTown()) && this.requiresClaimBlocks && GriefDefenderPlugin.getGlobalConfig().getConfig().economy.economyMode) {
+                    EconomyUtil.depositFunds(player.getUniqueId(), requiredFunds);
+                }
+                return new GDClaimResult(claim, ClaimResultType.CLAIM_EVENT_CANCELLED, message);
+            }
+
+            claim.initializeClaimData((GDClaim) this.parent);
+            if (this.parent != null) {
+                if (this.parent.isTown()) {
+                    claim.getData().setInheritParent(true);
+                }
+                claim.getData().setParent(this.parent.getUniqueId());
+            }
+
+            claim.getData().setExpiration(this.expire);
+            claim.getData().setDenyMessages(this.denyMessages);
+            claim.getData().setFlagOverrides(this.overrides);
+            claim.getData().setInheritParent(this.inherit);
+            claim.getData().setResizable(this.resizable);
+            claim.getData().setRequiresClaimBlocks(this.requiresClaimBlocks);
+            claim.getData().setFarewell(this.farewell);
+            claim.getData().setGreeting(this.greeting);
+            claim.getData().setSpawnPos(this.spawnPos);
+            claim.getData().setSizeRestrictions(this.sizeRestrictions);
+            final GDClaimManager claimManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(this.worldUniqueId);
+            claimManager.addClaim(claim, true);
+
+            if (claimResult.getClaims().size() > 1) {
+                claim.migrateClaims(new ArrayList<>(claimResult.getClaims()));
+            }
+
+            GDCreateClaimEvent.Post postEvent = new GDCreateClaimEvent.Post(claim);
+            GriefDefender.getEventManager().post(postEvent);
+            if (postEvent.cancelled()) {
+                final Component message = postEvent.getMessage().orElse(null);
+                if (message != null && player != null) {
+                    GriefDefenderPlugin.sendMessage(player, message);
+                }
+                final GDClaimManager claimWorldManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(world.getUniqueId());
+                claimWorldManager.deleteClaimInternal(claim, true);
+                return new GDClaimResult(claim, ClaimResultType.CLAIM_EVENT_CANCELLED, message);
+            }
+
+            if (GriefDefenderPlugin.getInstance().getWorldEditProvider() != null) {
+                if (GriefDefenderPlugin.getActiveConfig(this.worldUniqueId).getConfig().claim.claimAutoSchematicRestore) {
+                    final ClaimSchematic schematic = ClaimSchematic.builder().claim(claim).name("__restore__").build().orElse(null);
+                }
+            }
+            return new GDClaimResult(claim, ClaimResultType.SUCCESS);
+        }
+    }
+
+    public boolean migrateClaims(List<Claim> claims) {
+        GDClaim parentClaim = this;
+        for (Claim child : claims) {
+            if (child.equals(this)) {
+                continue;
+            }
+
+            moveChildToParent(parentClaim, (GDClaim) child);
+        }
+
+        return true;
+    }
+
+    public void moveChildToParent(GDClaim parentClaim, GDClaim childClaim) {
+        // Remove child from current parent if available
+        if (childClaim.parent != null && childClaim.parent != parentClaim) {
+            childClaim.parent.children.remove(childClaim);
+        }
+        childClaim.parent = parentClaim;
+        String fileName = childClaim.getClaimStorage().filePath.getFileName().toString();
+        Path newPath = parentClaim.getClaimStorage().folderPath.resolve(childClaim.getType().getName().toLowerCase()).resolve(fileName);
+        try {
+            if (Files.notExists(newPath.getParent())) {
+                Files.createDirectories(newPath.getParent());
+            }
+            Files.move(childClaim.getClaimStorage().filePath, newPath);
+            if (childClaim.getClaimStorage().folderPath.toFile().listFiles().length == 0) {
+                Files.delete(childClaim.getClaimStorage().folderPath);
+            }
+            childClaim.setClaimStorage(new ClaimStorageData(newPath, this.getWorldUniqueId(), (ClaimDataConfig) childClaim.getInternalClaimData()));
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        // Make sure to update new parent in storage
+        childClaim.getInternalClaimData().setParent(parentClaim.getUniqueId());
+        this.worldClaimManager.addClaim(childClaim, true);
+        for (Claim child : childClaim.children) {
+            moveChildToParent(childClaim, (GDClaim) child);
+        }
+    }
+
+    @Override
+    public Context getDefaultTypeContext() {
+        if (this.isAdminClaim()) {
+            return ClaimContexts.ADMIN_DEFAULT_CONTEXT;
+        }
+        if (this.isBasicClaim() || this.isSubdivision()) {
+            return ClaimContexts.BASIC_DEFAULT_CONTEXT;
+        }
+        if (this.isTown()) {
+            return ClaimContexts.TOWN_DEFAULT_CONTEXT;
+        }
+
+        return ClaimContexts.WILDERNESS_DEFAULT_CONTEXT;
+    }
+
+    public org.spongepowered.api.service.context.Context getSpongeDefaultTypeContext() {
+        if (this.isAdminClaim()) {
+            return SpongeContexts.ADMIN_DEFAULT_CONTEXT;
+        }
+        if (this.isBasicClaim() || this.isSubdivision()) {
+            return SpongeContexts.BASIC_DEFAULT_CONTEXT;
+        }
+        if (this.isTown()) {
+            return SpongeContexts.TOWN_DEFAULT_CONTEXT;
+        }
+
+        return SpongeContexts.WILDERNESS_DEFAULT_CONTEXT;
+    }
+
+    @Override
+    public Context getOverrideTypeContext() {
+        if (this.isAdminClaim()) {
+            return ClaimContexts.ADMIN_OVERRIDE_CONTEXT;
+        }
+        if (this.isBasicClaim() || this.isSubdivision()) {
+            return ClaimContexts.BASIC_OVERRIDE_CONTEXT;
+        }
+        if (this.isTown()) {
+            return ClaimContexts.TOWN_OVERRIDE_CONTEXT;
+        }
+
+        return ClaimContexts.WILDERNESS_OVERRIDE_CONTEXT;
+    }
+
+    @Override
+    public Context getOverrideClaimContext() {
+        return this.overrideClaimContext;
+    }
+
+    public org.spongepowered.api.service.context.Context getSpongeOverrideTypeContext() {
+        if (this.isAdminClaim()) {
+            return SpongeContexts.ADMIN_OVERRIDE_CONTEXT;
+        }
+        if (this.isBasicClaim() || this.isSubdivision()) {
+            return SpongeContexts.BASIC_OVERRIDE_CONTEXT;
+        }
+        if (this.isTown()) {
+            return SpongeContexts.TOWN_OVERRIDE_CONTEXT;
+        }
+
+        return SpongeContexts.WILDERNESS_OVERRIDE_CONTEXT;
+    }
+
+    public org.spongepowered.api.service.context.Context getSpongeOverrideClaimContext() {
+        return this.spongeOverrideClaimContext;
+    }
+
+    @Override
+    public Map<String, ClaimSchematic> getSchematics() {
+        return this.schematics;
+    }
+
+    @Override
+    public boolean deleteSchematic(String name) {
+        final Path schematicPath = GriefDefenderPlugin.getInstance().getWorldEditProvider().getSchematicWorldMap().get(this.world.getUniqueId()).resolve(this.id.toString());
+        try {
+            Files.createDirectories(schematicPath);
+        } catch (IOException e1) {
+            e1.printStackTrace();
+        }
+        File outputFile = schematicPath.resolve(name + ".schematic").toFile();
+        if (outputFile.delete()) {
+            this.schematics.remove(name);
+            return true;
+        }
+
+        return false;
+    }
+}
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/claim/GDClaimManager.java b/sponge/src/main/java/com/griefdefender/claim/GDClaimManager.java
new file mode 100644
index 0000000..2eb21b6
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/claim/GDClaimManager.java
@@ -0,0 +1,668 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.claim;
+
+import com.flowpowered.math.vector.Vector3i;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.google.common.reflect.TypeToken;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.GriefDefender;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.ClaimBlockSystem;
+import com.griefdefender.api.claim.ClaimManager;
+import com.griefdefender.api.claim.ClaimResult;
+import com.griefdefender.api.claim.ClaimResultType;
+import com.griefdefender.api.claim.ClaimTypes;
+import com.griefdefender.api.permission.option.Options;
+import com.griefdefender.configuration.ClaimDataConfig;
+import com.griefdefender.configuration.ClaimStorageData;
+import com.griefdefender.configuration.GriefDefenderConfig;
+import com.griefdefender.configuration.PlayerStorageData;
+import com.griefdefender.event.GDRemoveClaimEvent;
+import com.griefdefender.internal.util.BlockUtil;
+import com.griefdefender.internal.util.VecHelper;
+import com.griefdefender.permission.GDPermissionManager;
+import com.griefdefender.storage.BaseStorage;
+import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.serializer.plain.PlainComponentSerializer;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.event.CauseStackManager;
+import org.spongepowered.api.service.economy.EconomyService;
+import org.spongepowered.api.service.economy.account.Account;
+import org.spongepowered.api.service.economy.account.UniqueAccount;
+import org.spongepowered.api.util.Direction;
+import org.spongepowered.api.world.Location;
+import org.spongepowered.api.world.World;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.UUID;
+
+public class GDClaimManager implements ClaimManager {
+
+    private static final BaseStorage DATASTORE = GriefDefenderPlugin.getInstance().dataStore;
+    private UUID worldUniqueId;
+    private GriefDefenderConfig<?> activeConfig;
+
+    // Player UUID -> player data
+    private Map<UUID, GDPlayerData> playerDataList = Maps.newHashMap();
+    // World claim list
+    private Set<Claim> worldClaims = new HashSet<>();
+    // Claim UUID -> Claim
+    private Map<UUID, Claim> claimUniqueIdMap = Maps.newHashMap();
+    // String -> Claim
+    private Map<Long, Set<Claim>> chunksToClaimsMap = new Long2ObjectOpenHashMap<>(4096);
+    private GDClaim theWildernessClaim;
+
+    public GDClaimManager(World world) {
+        this.worldUniqueId = world.getUniqueId();
+        this.activeConfig = GriefDefenderPlugin.getActiveConfig(this.worldUniqueId);
+    }
+
+    public GDPlayerData getOrCreatePlayerData(UUID playerUniqueId) {
+        GDPlayerData playerData = this.getPlayerDataMap().get(playerUniqueId);
+        if (playerData == null) {
+            return createPlayerData(playerUniqueId);
+        } else {
+            return playerData;
+        }
+    }
+
+    private GDPlayerData createPlayerData(UUID playerUniqueId) {
+        Path playerFilePath = null;
+        if (BaseStorage.USE_GLOBAL_PLAYER_STORAGE) {
+            playerFilePath = BaseStorage.globalPlayerDataPath.resolve(playerUniqueId.toString());
+        } else {
+            playerFilePath = BaseStorage.worldConfigMap.get(this.worldUniqueId).getPath().getParent().resolve("PlayerData").resolve(playerUniqueId.toString());
+        }
+
+        PlayerStorageData playerStorage = new PlayerStorageData(playerFilePath);
+        Set<Claim> claimList = this.createPlayerClaimList(playerUniqueId);
+        GDPlayerData playerData = new GDPlayerData(this.worldUniqueId, playerUniqueId, playerStorage, this.activeConfig, claimList);
+        this.getPlayerDataMap().put(playerUniqueId, playerData);
+        return playerData;
+    }
+
+    private Set<Claim> createPlayerClaimList(UUID playerUniqueId) {
+        Set<Claim> claimList = new HashSet<>();
+        if (BaseStorage.USE_GLOBAL_PLAYER_STORAGE) {
+            for (World world : Sponge.getServer().getWorlds()) {
+                GDClaimManager claimmanager = DATASTORE.getClaimWorldManager(world.getUniqueId());
+                for (Claim claim : claimmanager.worldClaims) {
+                    GDClaim gpClaim = (GDClaim) claim;
+                    if (gpClaim.isAdminClaim()) {
+                        continue;
+                    }
+                    if (gpClaim.parent != null) {
+                       if (gpClaim.parent.getOwnerUniqueId().equals(playerUniqueId)) {
+                           claimList.add(claim);
+                       }
+                    } else {
+                        if (gpClaim.getOwnerUniqueId().equals(playerUniqueId)) {
+                            claimList.add(claim);
+                        }
+                    }
+                }
+            }
+        } else {
+            for (Claim claim : this.worldClaims) {
+                GDClaim gpClaim = (GDClaim) claim;
+                if (gpClaim.isAdminClaim()) {
+                    continue;
+                }
+                if (gpClaim.parent != null) {
+                   if (gpClaim.parent.getOwnerUniqueId().equals(playerUniqueId)) {
+                       claimList.add(claim);
+                   }
+                } else {
+                    if (gpClaim.getOwnerUniqueId().equals(playerUniqueId)) {
+                        claimList.add(claim);
+                    }
+                }
+            }
+        }
+
+        return claimList;
+    }
+
+    public void removePlayer(UUID playerUniqueId) {
+        this.getPlayerDataMap().remove(playerUniqueId);
+    }
+
+    public ClaimResult addClaim(Claim claim) {
+        GDClaim newClaim = (GDClaim) claim;
+        // ensure this new claim won't overlap any existing claims
+        ClaimResult result = newClaim.checkArea(false);
+        if (!result.successful()) {
+            return result;
+        }
+
+        // validate world
+        if (!this.worldUniqueId.equals(newClaim.getWorld().getUniqueId())) {
+            World world = Sponge.getServer().getWorld(this.worldUniqueId).get();
+            newClaim.setWorld(world);
+        }
+
+        // otherwise add this new claim to the data store to make it effective
+        this.addClaim(newClaim, true);
+        if (result.getClaims().size() > 1) {
+            newClaim.migrateClaims(new ArrayList<>(result.getClaims()));
+        }
+        return result;
+    }
+
+    public void addClaim(Claim claimToAdd, boolean writeToStorage) {
+        GDClaim claim = (GDClaim) claimToAdd;
+        if (claim.parent == null && this.worldClaims.contains(claimToAdd)) {
+            return;
+        }
+
+        if (writeToStorage) {
+            DATASTORE.writeClaimToStorage(claim);
+        }
+
+        // We need to keep track of all claims so they can be referenced by children during server startup
+        this.claimUniqueIdMap.put(claim.getUniqueId(), claim);
+
+        if (claim.isWilderness()) {
+            this.theWildernessClaim = claim;
+            return;
+        }
+
+        if (claim.parent != null) {
+            claim.parent.children.add(claim);
+            this.worldClaims.remove(claim);
+            this.deleteChunkHashes((GDClaim) claim);
+            if (!claim.isAdminClaim() && (!claim.isInTown() || !claim.getTownClaim().getOwnerUniqueId().equals(claim.getOwnerUniqueId()))) {
+                final GDPlayerData playerData = this.getPlayerDataMap().get(claim.getOwnerUniqueId());
+                Set<Claim> playerClaims = playerData.getInternalClaims();
+                if (!playerClaims.contains(claim)) {
+                    playerClaims.add(claim);
+                }
+            }
+            return;
+        }
+
+        if (!this.worldClaims.contains(claim)) {
+            this.worldClaims.add(claim);
+        }
+        final UUID ownerId = claim.getOwnerUniqueId();
+        final GDPlayerData playerData = this.getPlayerDataMap().get(ownerId);
+        if (playerData != null) {
+            Set<Claim> playerClaims = playerData.getInternalClaims();
+            if (!playerClaims.contains(claim)) {
+                playerClaims.add(claim);
+            }
+        } else if (!claim.isAdminClaim()) {
+            this.createPlayerData(ownerId);
+        }
+
+        this.updateChunkHashes(claim);
+        return;
+    }
+
+    public void updateChunkHashes(GDClaim claim) {
+        this.deleteChunkHashes(claim);
+        Set<Long> chunkHashes = claim.getChunkHashes(true);
+        for (Long chunkHash : chunkHashes) {
+            Set<Claim> claimsInChunk = this.getInternalChunksToClaimsMap().get(chunkHash);
+            if (claimsInChunk == null) {
+                claimsInChunk = new HashSet<Claim>();
+                this.getInternalChunksToClaimsMap().put(chunkHash, claimsInChunk);
+            }
+
+            claimsInChunk.add(claim);
+        }
+    }
+
+    // Used when parent claims becomes children
+    public void removeClaimData(Claim claim) {
+        this.worldClaims.remove(claim);
+        this.deleteChunkHashes((GDClaim) claim);
+    }
+
+    @Override
+    public ClaimResult deleteClaim(Claim claim, boolean deleteChildren) {
+        GDRemoveClaimEvent event = new GDRemoveClaimEvent(claim);
+        GriefDefender.getEventManager().post(event);
+        if (event.cancelled()) {
+            return new GDClaimResult(claim, ClaimResultType.CLAIM_EVENT_CANCELLED, event.getMessage().orElse(null));
+        }
+
+        return this.deleteClaimInternal(claim, deleteChildren);
+    }
+
+    public ClaimResult deleteClaimInternal(Claim claim, boolean deleteChildren) {
+        final GDClaim gpClaim = (GDClaim) claim;
+        Set<Claim> subClaims = claim.getChildren(false);
+        for (Claim child : subClaims) {
+            if (deleteChildren || (gpClaim.parent == null && child.isSubdivision())) {
+                this.deleteClaimInternal(child, true);
+                continue;
+            }
+
+            final GDClaim parentClaim = (GDClaim) claim;
+            final GDClaim childClaim = (GDClaim) child;
+            if (parentClaim.parent != null) {
+                migrateChildToNewParent(parentClaim.parent, childClaim);
+            } else {
+                // move child to parent folder
+                migrateChildToNewParent(null, childClaim);
+            }
+        }
+
+        resetPlayerClaimVisuals(claim);
+        // transfer bank balance to owner
+        final Account bankAccount = ((GDClaim) claim).getEconomyAccount();
+        if (bankAccount != null) {
+            try (final CauseStackManager.StackFrame frame = Sponge.getCauseStackManager().pushCauseFrame()) {
+                Sponge.getCauseStackManager().pushCause(GriefDefenderPlugin.getInstance());
+                final EconomyService economyService = GriefDefenderPlugin.getInstance().economyService.get();
+                final UniqueAccount ownerAccount = economyService.getOrCreateAccount(claim.getOwnerUniqueId()).orElse(null);
+                if (ownerAccount != null) {
+                    ownerAccount.deposit(economyService.getDefaultCurrency(), bankAccount.getBalance(economyService.getDefaultCurrency()),
+                        Sponge.getCauseStackManager().getCurrentCause());
+                }
+                bankAccount.resetBalance(economyService.getDefaultCurrency(), Sponge.getCauseStackManager().getCurrentCause());
+            }
+        }
+        this.worldClaims.remove(claim);
+        this.claimUniqueIdMap.remove(claim.getUniqueId());
+        this.deleteChunkHashes((GDClaim) claim);
+        if (gpClaim.parent != null) {
+            gpClaim.parent.children.remove(claim);
+        }
+
+        return DATASTORE.deleteClaimFromStorage((GDClaim) claim);
+    }
+
+    // Migrates children to new parent
+    private void migrateChildToNewParent(GDClaim parentClaim, GDClaim childClaim) {
+        childClaim.parent = parentClaim;
+        String fileName = childClaim.getClaimStorage().filePath.getFileName().toString();
+        Path newPath = null;
+        if (parentClaim == null) {
+            newPath = childClaim.getClaimStorage().folderPath.getParent().getParent().resolve(childClaim.getType().getName().toLowerCase()).resolve(fileName);
+        } else {
+            // Only store in same claim type folder if not admin.
+            // Admin claims are currently the only type that can hold children of same type within
+            if (childClaim.getType().equals(parentClaim.getType()) && (!parentClaim.isAdminClaim())) {
+                newPath = parentClaim.getClaimStorage().folderPath.resolve(fileName);
+            } else {
+                newPath = parentClaim.getClaimStorage().folderPath.resolve(childClaim.getType().getName().toLowerCase()).resolve(fileName);
+            }
+        }
+
+        try {
+            if (Files.notExists(newPath.getParent())) {
+                Files.createDirectories(newPath.getParent());
+            }
+            Files.move(childClaim.getClaimStorage().filePath, newPath);
+            if (childClaim.getClaimStorage().folderPath.toFile().listFiles().length == 0) {
+                Files.delete(childClaim.getClaimStorage().folderPath);
+            }
+            childClaim.setClaimStorage(new ClaimStorageData(newPath, this.worldUniqueId, (ClaimDataConfig) childClaim.getInternalClaimData()));
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+
+        // Make sure to update new parent in storage
+        final UUID parentUniqueId = parentClaim == null ? null : parentClaim.getUniqueId();
+        childClaim.getInternalClaimData().setParent(parentUniqueId);
+        this.addClaim(childClaim, true);
+        for (Claim child : childClaim.children) {
+            migrateChildToNewParent(childClaim, (GDClaim) child);
+        }
+    }
+
+    private void resetPlayerClaimVisuals(Claim claim) {
+        // player may be offline so check is needed
+        GDPlayerData playerData = this.getPlayerDataMap().get(claim.getOwnerUniqueId());
+        if (playerData != null) {
+            playerData.getInternalClaims().remove(claim);
+            if (playerData.lastClaim != null) {
+                playerData.lastClaim.clear();
+            }
+        }
+
+        // revert visuals for all players watching this claim
+        List<UUID> playersWatching = new ArrayList<>(((GDClaim) claim).playersWatching);
+        for (UUID playerUniqueId : playersWatching) {
+            Player player = Sponge.getServer().getPlayer(playerUniqueId).orElse(null);
+            if (player != null) {
+                playerData = this.getOrCreatePlayerData(playerUniqueId);
+                playerData.revertActiveVisual(player);
+                if (playerData.lastClaim != null) {
+                    playerData.lastClaim.clear();
+                }
+                if (GriefDefenderPlugin.getInstance().worldEditProvider != null) {
+                    GriefDefenderPlugin.getInstance().worldEditProvider.revertVisuals(player, playerData, claim.getUniqueId());
+                }
+            }
+        }
+    }
+
+    private void deleteChunkHashes(GDClaim claim) {
+        Set<Long> chunkHashes = claim.getChunkHashes(false);
+        if (chunkHashes == null) {
+            return;
+        }
+
+        for (Long chunkHash : chunkHashes) {
+            Set<Claim> claimsInChunk = this.getInternalChunksToClaimsMap().get(chunkHash);
+            if (claimsInChunk != null) {
+                claimsInChunk.remove(claim);
+            }
+        }
+    }
+
+    @Nullable
+    public Optional<Claim> getClaimByUUID(UUID claimUniqueId) {
+        return Optional.ofNullable(this.claimUniqueIdMap.get(claimUniqueId));
+    }
+
+    public Set<Claim> getInternalPlayerClaims(UUID playerUniqueId) {
+        final GDPlayerData playerData = this.getPlayerDataMap().get(playerUniqueId);
+        if (playerData == null) {
+            return new HashSet<>();
+        }
+        return playerData.getInternalClaims();
+    }
+
+    @Nullable
+    public Set<Claim> getPlayerClaims(UUID playerUniqueId) {
+        final GDPlayerData playerData = this.getPlayerDataMap().get(playerUniqueId);
+        if (playerData == null) {
+            return ImmutableSet.of();
+        }
+        return ImmutableSet.copyOf(this.getPlayerDataMap().get(playerUniqueId).getInternalClaims());
+    }
+
+    public void createWildernessClaim(World world) {
+        if (this.theWildernessClaim != null) {
+            return;
+        }
+
+        final Vector3i lesserCorner = new Vector3i(-30000000, 0, -30000000);
+        final Vector3i greaterCorner = new Vector3i(29999999, 255, 29999999);
+        // Use world UUID as wilderness claim ID
+        GDClaim wilderness = new GDClaim(world, lesserCorner, greaterCorner, world.getUniqueId(), ClaimTypes.WILDERNESS, null, false);
+        wilderness.setOwnerUniqueId(GriefDefenderPlugin.WORLD_USER_UUID);
+        wilderness.initializeClaimData(null);
+        wilderness.claimData.save();
+        wilderness.claimStorage.save();
+        this.theWildernessClaim = wilderness;
+        this.claimUniqueIdMap.put(wilderness.getUniqueId(), wilderness);
+    }
+
+    @Override
+    public GDClaim getWildernessClaim() {
+        if (this.theWildernessClaim == null) {
+            World world = Sponge.getServer().getWorld(this.worldUniqueId).get();
+            this.createWildernessClaim(world);
+        }
+        return this.theWildernessClaim;
+    }
+
+    @Override
+    public Set<Claim> getWorldClaims() {
+        return this.worldClaims;
+    }
+
+    public Map<UUID, GDPlayerData> getPlayerDataMap() {
+        if (BaseStorage.USE_GLOBAL_PLAYER_STORAGE) {
+            return BaseStorage.GLOBAL_PLAYER_DATA;
+        }
+        return this.playerDataList;
+    }
+
+    public Set<Claim> findOverlappingClaims(Claim claim) {
+        Set<Claim> claimSet = new HashSet<>();
+        for (Long chunkHash : claim.getChunkHashes()) {
+            final Set<Claim> chunkClaims = this.chunksToClaimsMap.get(chunkHash);
+            if (chunkClaims == null) {
+                continue;
+            }
+            for (Claim chunkClaim : chunkClaims) {
+                if (!chunkClaim.equals(claim) && (claim.overlaps(chunkClaim) || chunkClaim.overlaps(claim))) {
+                    claimSet.add(chunkClaim);
+                }
+            }
+        }
+        return claimSet;
+    }
+
+    @Override
+    public Map<Long, Set<Claim>> getChunksToClaimsMap() {
+        return ImmutableMap.copyOf(this.chunksToClaimsMap);
+    }
+
+    public Map<Long, Set<Claim>> getInternalChunksToClaimsMap() {
+        return this.chunksToClaimsMap;
+    }
+
+    public void save() {
+        for (Claim claim : this.worldClaims) {
+            GDClaim gpClaim = (GDClaim) claim;
+            gpClaim.save();
+        }
+        this.getWildernessClaim().save();
+
+        for (GDPlayerData playerData : this.getPlayerDataMap().values()) {
+            playerData.getStorageData().save();
+        }
+    }
+
+    public void unload() {
+        this.playerDataList.clear();
+        this.worldClaims.clear();
+        this.claimUniqueIdMap.clear();
+        this.chunksToClaimsMap.clear();
+        if (this.theWildernessClaim != null) {
+            this.theWildernessClaim.unload();
+            this.theWildernessClaim = null;
+        }
+        this.worldUniqueId = null;
+    }
+
+    @Override
+    public Claim getClaimAt(Vector3i pos) {
+        final World world = Sponge.getServer().getWorld(this.worldUniqueId).orElse(null);
+        return this.getClaimAt(VecHelper.toLocation(world, pos), null, null, false);
+    }
+
+    public Claim getClaimAt(Location<World> location, boolean useBorderBlockRadius) {
+        return this.getClaimAt(location, null, null, useBorderBlockRadius);
+    }
+
+    public Claim getClaimAtPlayer(Location<World> location, GDPlayerData playerData) {
+        return this.getClaimAt(location, (GDClaim) playerData.lastClaim.get(), playerData, false);
+    }
+
+    public Claim getClaimAtPlayer(Location<World> location, GDPlayerData playerData, boolean useBorderBlockRadius) {
+        return this.getClaimAt(location, (GDClaim) playerData.lastClaim.get(), playerData, useBorderBlockRadius);
+    }
+
+    public Claim getClaimAt(Location<World> location) {
+        return this.getClaimAt(location, false);
+    }
+
+    public Claim getClaimAt(Location<World> location, GDClaim cachedClaim, GDPlayerData playerData, boolean useBorderBlockRadius) {
+        //GPTimings.CLAIM_GETCLAIM.startTimingIfSync();
+        // check cachedClaim guess first. if the location is inside it, we're done
+        if (cachedClaim != null && !cachedClaim.isWilderness() && cachedClaim.contains(location, true)) {
+           // GPTimings.CLAIM_GETCLAIM.stopTimingIfSync();
+            return cachedClaim;
+        }
+
+        Set<Claim> claimsInChunk = this.getInternalChunksToClaimsMap().get(BlockUtil.getInstance().asLong(location.getBlockX() >> 4, location.getBlockZ() >> 4));
+        if (useBorderBlockRadius && (playerData != null && !playerData.bypassBorderCheck)) {
+            final int borderBlockRadius = GriefDefenderPlugin.getActiveConfig(location.getExtent().getUniqueId()).getConfig().claim.borderBlockRadius;
+            // if borderBlockRadius > 0, check surrounding chunks
+            if (borderBlockRadius > 0) {
+                for (Direction direction : BlockUtil.ORDINAL_SET) {
+                    Location<World> currentLocation = location;
+                    for (int i = 0; i < borderBlockRadius; i++) { // Handle depth
+                        currentLocation = currentLocation.getBlockRelative(direction); 
+                        Set<Claim> relativeClaims = this.getInternalChunksToClaimsMap().get(BlockUtil.getInstance().asLong(currentLocation.getBlockX() >> 4, currentLocation.getBlockZ() >> 4));
+                        if (relativeClaims != null) {
+                            if (claimsInChunk == null) {
+                                claimsInChunk = new HashSet<>();
+                            }
+                            claimsInChunk.addAll(relativeClaims);
+                        }
+                    }
+                }
+            }
+        }
+        if (claimsInChunk == null) {
+            //GPTimings.CLAIM_GETCLAIM.stopTimingIfSync();
+            return this.getWildernessClaim();
+        }
+
+        for (Claim claim : claimsInChunk) {
+            GDClaim foundClaim = findClaim((GDClaim) claim, location, playerData, useBorderBlockRadius);
+            if (foundClaim != null) {
+                return foundClaim;
+            }
+        }
+
+        //GPTimings.CLAIM_GETCLAIM.stopTimingIfSync();
+        // if no claim found, return the world claim
+        return this.getWildernessClaim();
+    }
+
+    private GDClaim findClaim(GDClaim claim, Location<World> location, GDPlayerData playerData, boolean useBorderBlockRadius) {
+        if (claim.contains(location, playerData, useBorderBlockRadius)) {
+            // when we find a top level claim, if the location is in one of its children,
+            // return the child claim, not the top level claim
+            for (Claim childClaim : claim.children) {
+                GDClaim child = (GDClaim) childClaim;
+                if (!child.children.isEmpty()) {
+                    GDClaim innerChild = findClaim(child, location, playerData, useBorderBlockRadius);
+                    if (innerChild != null) {
+                        return innerChild;
+                    }
+                }
+                // check if child has children (Town -> Basic -> Subdivision)
+                if (child.contains(location, playerData, useBorderBlockRadius)) {
+                    return child;
+                }
+            }
+            return claim;
+        }
+        return null;
+    }
+
+    @Override
+    public List<Claim> getClaimsByName(String name) {
+        List<Claim> claimList = new ArrayList<>();
+        for (Claim worldClaim : this.getWorldClaims()) {
+            Component claimName = worldClaim.getName().orElse(null);
+            if (claimName != null && claimName != TextComponent.empty()) {
+                if (PlainComponentSerializer.INSTANCE.serialize(claimName).equalsIgnoreCase(name)) {
+                    claimList.add(worldClaim);
+                }
+            }
+            // check children
+            for (Claim child : ((GDClaim) worldClaim).getChildren(true)) {
+                if (child.getUniqueId().toString().equals(name)) {
+                    claimList.add(child);
+                }
+            }
+        }
+        return claimList;
+    }
+
+    public void resetPlayerData() {
+        // check migration reset
+        if (GriefDefenderPlugin.getGlobalConfig().getConfig().playerdata.resetMigrations) {
+            for (GDPlayerData playerData : this.getPlayerDataMap().values()) {
+                final PlayerStorageData playerStorage = playerData.getStorageData();
+                playerStorage.getConfig().setMigratedBlocks(false);
+                playerStorage.save();
+            }
+        }
+        // migrate playerdata to new claim block system
+        final int migration3dRate = GriefDefenderPlugin.getGlobalConfig().getConfig().playerdata.migrateVolumeRate;
+        final int migration2dRate = GriefDefenderPlugin.getGlobalConfig().getConfig().playerdata.migrateAreaRate;
+        final boolean resetClaimBlockData = GriefDefenderPlugin.getGlobalConfig().getConfig().playerdata.resetAccruedClaimBlocks;
+
+        if (migration3dRate <= -1 && migration2dRate <= -1 && !resetClaimBlockData) {
+            return;
+        }
+        if (GriefDefenderPlugin.CLAIM_BLOCK_SYSTEM == ClaimBlockSystem.VOLUME && migration2dRate >= 0) {
+            return;
+        }
+        if (GriefDefenderPlugin.CLAIM_BLOCK_SYSTEM == ClaimBlockSystem.AREA && migration3dRate >= 0) {
+            return;
+        }
+
+        for (GDPlayerData playerData : this.getPlayerDataMap().values()) {
+            final PlayerStorageData playerStorage = playerData.getStorageData();
+            final int accruedBlocks = playerStorage.getConfig().getAccruedClaimBlocks();
+            int newAccruedBlocks = accruedBlocks;
+            // first check reset
+            if (resetClaimBlockData) {
+                newAccruedBlocks = playerData.getTotalClaimsCost();
+                playerStorage.getConfig().setBonusClaimBlocks(0);
+            } else if (migration3dRate > -1 && !playerStorage.getConfig().hasMigratedBlocks()) {
+                newAccruedBlocks = accruedBlocks * migration3dRate;
+                playerStorage.getConfig().setMigratedBlocks(true);
+            } else if (migration2dRate > -1 && !playerStorage.getConfig().hasMigratedBlocks()) {
+                newAccruedBlocks = accruedBlocks / migration2dRate;
+                playerStorage.getConfig().setMigratedBlocks(true);
+            }
+            if (newAccruedBlocks < 0) {
+                newAccruedBlocks = 0;
+            }
+            final int maxAccruedBlocks = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), playerData.getSubject(), Options.MAX_ACCRUED_BLOCKS);
+            if (newAccruedBlocks > maxAccruedBlocks) {
+                newAccruedBlocks = maxAccruedBlocks;
+            }
+            playerStorage.getConfig().setAccruedClaimBlocks(newAccruedBlocks);
+            playerStorage.save();
+        }
+    }
+
+    @Override
+    public UUID getWorldId() {
+        return this.worldUniqueId;
+    }
+}
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/claim/GDClaimResult.java b/sponge/src/main/java/com/griefdefender/claim/GDClaimResult.java
new file mode 100644
index 0000000..e1817ce
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/claim/GDClaimResult.java
@@ -0,0 +1,89 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.claim;
+
+import com.google.common.collect.ImmutableList;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.ClaimResult;
+import com.griefdefender.api.claim.ClaimResultType;
+import net.kyori.text.Component;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+public class GDClaimResult implements ClaimResult {
+
+    private final Component eventMessage;
+    private final List<Claim> claims;
+    private final ClaimResultType resultType;
+
+    public GDClaimResult(ClaimResultType type) {
+        this(type, null);
+    }
+
+    public GDClaimResult(ClaimResultType type, Component message) {
+        this.claims = ImmutableList.of();
+        this.resultType = type;
+        this.eventMessage = message;
+    }
+
+    public GDClaimResult(Claim claim, ClaimResultType type) {
+        this(claim, type, null);
+    }
+
+    public GDClaimResult(Claim claim, ClaimResultType type, Component message) {
+        List<Claim> claimList = new ArrayList<>();
+        claimList.add(claim);
+        this.claims = ImmutableList.copyOf(claimList);
+        this.resultType = type;
+        this.eventMessage = message;
+    }
+
+    public GDClaimResult(List<Claim> claims, ClaimResultType type) {
+        this(claims, type, null);
+    }
+
+    public GDClaimResult(List<Claim> claims, ClaimResultType type, Component message) {
+        this.claims = ImmutableList.copyOf(claims);
+        this.resultType = type;
+        this.eventMessage = message;
+    }
+
+    @Override
+    public ClaimResultType getResultType() {
+        return this.resultType;
+    }
+
+    @Override
+    public Optional<Component> getMessage() {
+        return Optional.ofNullable(this.eventMessage);
+    }
+
+    @Override
+    public List<Claim> getClaims() {
+        return this.claims;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/claim/GDClaimSchematic.java b/sponge/src/main/java/com/griefdefender/claim/GDClaimSchematic.java
new file mode 100644
index 0000000..aa514a2
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/claim/GDClaimSchematic.java
@@ -0,0 +1,187 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.claim;
+
+import com.flowpowered.math.vector.Vector3i;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.ClaimSchematic;
+import com.griefdefender.internal.util.VecHelper;
+import com.griefdefender.storage.FileStorage;
+import org.spongepowered.api.data.DataContainer;
+import org.spongepowered.api.data.persistence.DataFormats;
+import org.spongepowered.api.data.persistence.DataTranslators;
+import org.spongepowered.api.world.BlockChangeFlags;
+import org.spongepowered.api.world.World;
+import org.spongepowered.api.world.extent.ArchetypeVolume;
+import org.spongepowered.api.world.schematic.Schematic;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.time.Instant;
+import java.util.Optional;
+import java.util.zip.GZIPOutputStream;
+
+public class GDClaimSchematic implements ClaimSchematic {
+
+    private Claim claim;
+    private Schematic schematic;
+    private String name;
+    private Vector3i origin;
+    private final Instant dateCreated;
+
+    // Used during server startup to load schematic
+    public GDClaimSchematic(Claim claim, Schematic schematic, String name) {
+        this.claim = claim;
+        this.schematic = schematic;
+        this.name = name;
+        this.origin = claim.getLesserBoundaryCorner();
+        this.dateCreated = Instant.now();
+    }
+
+    private GDClaimSchematic(Claim claim, Schematic schematic, String name, Vector3i origin) {
+        this.claim = claim;
+        this.schematic = schematic;
+        this.name = name;
+        this.origin = origin;
+        this.dateCreated = Instant.now();
+    }
+
+    @Override
+    public Claim getClaim() {
+        return this.claim;
+    }
+
+    @Override
+    public String getName() {
+        return this.name;
+    }
+
+    @Override
+    public Instant getDateCreated() {
+        return this.dateCreated;
+    }
+
+    @Override
+    public Vector3i getOrigin() {
+        return this.origin;
+    }
+
+    @Override
+    public void setOrigin(Vector3i pos) {
+        this.origin = pos;
+    }
+
+    public Schematic getSchematic() {
+        return this.schematic;
+    }
+
+    /**
+     * Applies schematic to claim.
+     * 
+     * @return If schematic apply was successful, false if not
+     */
+    public boolean apply() {
+        if (!this.schematic.containsBlock(this.claim.getLesserBoundaryCorner()) && !schematic.containsBlock(this.claim.getGreaterBoundaryCorner())) {
+            return false;
+        }
+
+        this.schematic.apply(VecHelper.toLocation(((GDClaim)(this.claim)).getWorld(), this.claim.getLesserBoundaryCorner()), BlockChangeFlags.ALL);
+        return true;
+    }
+
+    public static class ClaimSchematicBuilder implements Builder {
+
+        private Claim claim;
+        private Schematic schematic;
+        private String name;
+        private Vector3i origin;
+
+        @Override
+        public Builder claim(Claim claim) {
+            final World world = ((GDClaim) claim).getWorld();
+            final ArchetypeVolume volume = world.createArchetypeVolume(this.claim.getLesserBoundaryCorner(), this.claim.getGreaterBoundaryCorner(), new Vector3i(0, 0, 0));
+            final Schematic schematic = Schematic.builder()
+                    .metaValue(Schematic.METADATA_NAME, name)
+                    .metaValue(Schematic.METADATA_DATE, Instant.now().toString())
+                    .metaValue("UUID", this.claim.getUniqueId().toString())
+                    .volume(volume)
+                    .build();
+            this.claim = claim;
+            this.schematic = schematic;
+            return this;
+        }
+
+        @Override
+        public Builder name(String name) {
+            this.name = name;
+            return this;
+        }
+
+        @Override
+        public Builder origin(Vector3i origin) {
+            this.origin = origin;
+            return this;
+        }
+
+        @Override
+        public Builder reset() {
+            this.name = null;
+            this.origin = null;
+            this.schematic = null;
+            return this;
+        }
+
+        @Override
+        public Optional<ClaimSchematic> build() {
+            if (this.origin == null) {
+                //this.origin = new Vector3i(0, 0, 0);
+                this.origin = this.claim.getLesserBoundaryCorner();
+            }
+            final World world = ((GDClaim) this.claim).getWorld();
+            DataContainer schematicData = DataTranslators.SCHEMATIC.translate(schematic);
+            final Path schematicPath = GriefDefenderPlugin.getInstance().getWorldEditProvider().getSchematicWorldMap().get(world.getUniqueId()).resolve(this.claim.getUniqueId().toString());
+            try {
+                Files.createDirectories(schematicPath);
+            } catch (IOException e1) {
+                e1.printStackTrace();
+            }
+            File outputFile = schematicPath.resolve(name + ".schematic").toFile();
+            try {
+                DataFormats.NBT.writeTo(new GZIPOutputStream(new FileOutputStream(outputFile)), schematicData);
+            } catch (Exception e) {
+                e.printStackTrace();
+                return Optional.empty();
+            }
+
+            final GDClaimSchematic schematic = new GDClaimSchematic(this.claim, this.schematic, this.name, this.origin);
+            ((GDClaim) this.claim).schematics.put(this.name, schematic);
+            return Optional.of(schematic);
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/claim/GDClaimType.java b/sponge/src/main/java/com/griefdefender/claim/GDClaimType.java
new file mode 100644
index 0000000..a1c9db1
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/claim/GDClaimType.java
@@ -0,0 +1,119 @@
+/*
+ * This file is part of Sponge, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) SpongePowered <https://www.spongepowered.org>
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.claim;
+
+import com.griefdefender.api.claim.ClaimContexts;
+import com.griefdefender.api.claim.ClaimType;
+import com.griefdefender.api.permission.Context;
+import com.griefdefender.util.SpongeContexts;
+
+public class GDClaimType implements ClaimType {
+
+    private final String id;
+    private final String name;
+    private final Context defaultContext;
+    private final Context overrideContext;
+    private final org.spongepowered.api.service.context.Context spongeDefaultContext;
+    private final org.spongepowered.api.service.context.Context spongeOverrideContext;
+
+    public GDClaimType(String id, String name) {
+        this.id = id;
+        this.name = name;
+
+        if (name.equalsIgnoreCase("any") || name.equalsIgnoreCase("global")) {
+            this.defaultContext = ClaimContexts.GLOBAL_DEFAULT_CONTEXT;
+            this.overrideContext = ClaimContexts.GLOBAL_OVERRIDE_CONTEXT;
+            this.spongeDefaultContext = SpongeContexts.GLOBAL_DEFAULT_CONTEXT;
+            this.spongeOverrideContext = SpongeContexts.GLOBAL_OVERRIDE_CONTEXT;
+        } else if (name.equalsIgnoreCase("admin")) {
+            this.defaultContext = ClaimContexts.ADMIN_DEFAULT_CONTEXT;
+            this.overrideContext = ClaimContexts.ADMIN_OVERRIDE_CONTEXT;
+            this.spongeDefaultContext = SpongeContexts.GLOBAL_DEFAULT_CONTEXT;
+            this.spongeOverrideContext = SpongeContexts.GLOBAL_OVERRIDE_CONTEXT;
+        } else if (name.equalsIgnoreCase("basic")) {
+            this.defaultContext = ClaimContexts.BASIC_DEFAULT_CONTEXT;
+            this.overrideContext = ClaimContexts.BASIC_OVERRIDE_CONTEXT;
+            this.spongeDefaultContext = SpongeContexts.GLOBAL_DEFAULT_CONTEXT;
+            this.spongeOverrideContext = SpongeContexts.GLOBAL_OVERRIDE_CONTEXT;
+        } else if (name.equalsIgnoreCase("subdivision")) {
+            this.defaultContext = ClaimContexts.SUBDIVISION_DEFAULT_CONTEXT;
+            this.overrideContext = ClaimContexts.SUBDIVISION_OVERRIDE_CONTEXT;
+            this.spongeDefaultContext = SpongeContexts.SUBDIVISION_DEFAULT_CONTEXT;
+            this.spongeOverrideContext = SpongeContexts.SUBDIVISION_OVERRIDE_CONTEXT;
+        } else if (name.equalsIgnoreCase("town")) {
+            this.defaultContext = ClaimContexts.TOWN_DEFAULT_CONTEXT;
+            this.overrideContext = ClaimContexts.TOWN_OVERRIDE_CONTEXT;
+            this.spongeDefaultContext = SpongeContexts.TOWN_DEFAULT_CONTEXT;
+            this.spongeOverrideContext = SpongeContexts.TOWN_OVERRIDE_CONTEXT;
+        } else if (name.equalsIgnoreCase("wilderness")) {
+            this.defaultContext = ClaimContexts.WILDERNESS_DEFAULT_CONTEXT;
+            this.overrideContext = ClaimContexts.WILDERNESS_OVERRIDE_CONTEXT;
+            this.spongeDefaultContext = SpongeContexts.WILDERNESS_DEFAULT_CONTEXT;
+            this.spongeOverrideContext = SpongeContexts.WILDERNESS_OVERRIDE_CONTEXT;
+        } else {
+            this.defaultContext = new Context("gd_claim_default", name.toLowerCase());
+            this.overrideContext = new Context("gd_claim_override", name.toLowerCase());
+            this.spongeDefaultContext = new org.spongepowered.api.service.context.Context("gd_claim_default", name.toLowerCase());
+            this.spongeOverrideContext = new org.spongepowered.api.service.context.Context("gd_claim_override", name.toLowerCase());
+        }
+    }
+
+    @Override
+    public String getId() {
+        return this.id;
+    }
+
+    @Override
+    public String getName() {
+        return this.name;
+    }
+
+    public String toString() {
+        return this.id;
+    }
+
+    @Override
+    public Context getContext() {
+        return this.defaultContext;
+    }
+
+    @Override
+    public Context getDefaultContext() {
+        return this.defaultContext;
+    }
+
+    @Override
+    public Context getOverrideContext() {
+        return this.overrideContext;
+    }
+
+    public org.spongepowered.api.service.context.Context getSpongeDefaultContext() {
+        return this.spongeDefaultContext;
+    }
+
+    public org.spongepowered.api.service.context.Context getSpongeOverrideContext() {
+        return this.spongeOverrideContext;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/claim/GDShovelType.java b/sponge/src/main/java/com/griefdefender/claim/GDShovelType.java
new file mode 100644
index 0000000..521a1d2
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/claim/GDShovelType.java
@@ -0,0 +1,49 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.claim;
+
+import com.griefdefender.api.claim.ShovelType;
+
+public class GDShovelType implements ShovelType {
+
+    private final String id;
+    private final String name;
+
+    public GDShovelType(String id, String name) {
+        this.id = id;
+        this.name = name;
+    }
+
+    @Override
+    public String getId() {
+        return this.id;
+    }
+
+    @Override
+    public String getName() {
+        return this.name;
+    }
+
+}
diff --git a/sponge/src/main/java/com/griefdefender/claim/GDTown.java b/sponge/src/main/java/com/griefdefender/claim/GDTown.java
new file mode 100644
index 0000000..1b5599d
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/claim/GDTown.java
@@ -0,0 +1,21 @@
+package com.griefdefender.claim;
+
+import com.flowpowered.math.vector.Vector3i;
+import com.griefdefender.api.claim.ClaimType;
+import com.griefdefender.api.claim.Town;
+import com.griefdefender.api.data.TownData;
+import org.spongepowered.api.world.World;
+
+import java.util.UUID;
+
+public class GDTown extends GDClaim implements Town {
+
+    public GDTown(World world, Vector3i point1, Vector3i point2, ClaimType type, UUID ownerUniqueId, boolean cuboid) {
+        super(world, point1, point2, type, ownerUniqueId, cuboid);
+    }
+
+    @Override
+    public TownData getData() {
+        return (TownData) this.claimData;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/claim/GDTrustType.java b/sponge/src/main/java/com/griefdefender/claim/GDTrustType.java
new file mode 100644
index 0000000..3a299b5
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/claim/GDTrustType.java
@@ -0,0 +1,53 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.claim;
+
+import com.griefdefender.api.claim.TrustType;
+
+public class GDTrustType implements TrustType {
+
+    private final String id;
+    private final String name;
+
+    public GDTrustType(String id, String name) {
+        this.id = id.toLowerCase();
+        this.name = name.toLowerCase();
+    }
+
+    @Override
+    public String getId() {
+        return this.id;
+    }
+
+    @Override
+    public String getName() {
+        return this.name;
+    }
+
+    @Override
+    public String toString() {
+        return this.id;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/ClaimFlagBase.java b/sponge/src/main/java/com/griefdefender/command/ClaimFlagBase.java
new file mode 100644
index 0000000..affaefa
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/ClaimFlagBase.java
@@ -0,0 +1,973 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.InvalidCommandArgument;
+import com.github.benmanes.caffeine.cache.Cache;
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.GriefDefender;
+import com.griefdefender.api.Tristate;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.ClaimContexts;
+import com.griefdefender.api.permission.Context;
+import com.griefdefender.api.permission.ContextKeys;
+import com.griefdefender.api.permission.PermissionResult;
+import com.griefdefender.api.permission.ResultTypes;
+import com.griefdefender.api.permission.flag.Flag;
+import com.griefdefender.api.permission.flag.Flags;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.configuration.category.CustomFlagGroupCategory;
+import com.griefdefender.event.GDCauseStackManager;
+import com.griefdefender.event.GDFlagPermissionEvent;
+import com.griefdefender.internal.pagination.PaginationList;
+import com.griefdefender.internal.util.NMSUtil;
+import com.griefdefender.permission.GDPermissionHolder;
+import com.griefdefender.permission.GDPermissionManager;
+import com.griefdefender.permission.GDPermissionUser;
+import com.griefdefender.permission.GDPermissions;
+import com.griefdefender.permission.flag.CustomFlagData;
+import com.griefdefender.permission.flag.GDActiveFlagData;
+import com.griefdefender.permission.flag.GDCustomFlagDefinition;
+import com.griefdefender.permission.ui.ClaimClickData;
+import com.griefdefender.permission.ui.FlagData;
+import com.griefdefender.permission.ui.MenuType;
+import com.griefdefender.permission.ui.UIHelper;
+import com.griefdefender.permission.ui.FlagData.FlagContextHolder;
+import com.griefdefender.registry.FlagRegistryModule;
+import com.griefdefender.text.action.GDCallbackHolder;
+import com.griefdefender.util.CauseContextHelper;
+import com.griefdefender.util.PaginationUtil;
+import com.griefdefender.util.PermissionUtil;
+
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import net.kyori.text.event.ClickEvent;
+import net.kyori.text.event.HoverEvent;
+import net.kyori.text.format.TextColor;
+import net.kyori.text.format.TextDecoration;
+import org.spongepowered.api.command.CommandSource;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.item.inventory.ItemStack;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+public abstract class ClaimFlagBase extends BaseCommand {
+
+    private static final Component whiteOpenBracket = TextComponent.of("[", TextColor.AQUA);
+    private static final Component whiteCloseBracket = TextComponent.of("]", TextColor.AQUA);
+    protected GDPermissionHolder subject;
+    protected ClaimSubjectType subjectType;
+    protected String friendlySubjectName;
+    private final Cache<UUID, String> lastActivePresetMenuMap = Caffeine.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES)
+            .build();
+    private final Cache<UUID, String> lastActiveMenuTypeMap = Caffeine.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES)
+            .build();
+
+    protected ClaimFlagBase(ClaimSubjectType type) {
+        this.subjectType = type;
+    }
+
+    public void execute(Player player, String[] args) throws InvalidCommandArgument {
+        final GDPermissionUser src = PermissionHolderCache.getInstance().getOrCreateUser(player);
+        final GDPermissionHolder commandSubject = subject;
+        String commandFlag = null;
+        String target = null;
+        String value = null;
+        String contexts = null;
+        final String arguments = String.join(" ", args);
+        int index = arguments.indexOf("context[");
+        if (index != -1) {
+            contexts = arguments.substring(index, arguments.length());
+        }
+        if (args.length > 0) {
+            if (args.length < 3) {
+                throw new InvalidCommandArgument();
+            }
+            commandFlag = args[0];
+            target = args[1];
+            value = args[2];
+        }
+        final Flag flag = FlagRegistryModule.getInstance().getById(commandFlag).orElse(null);
+        if (commandFlag != null && flag == null) {
+            TextAdapter.sendComponent(player, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.FLAG_NOT_FOUND, ImmutableMap.of(
+                    "flag", commandFlag)));
+            return;
+        }
+
+        if (flag != null && !player.hasPermission(GDPermissions.USER_CLAIM_FLAGS + "." + flag.getName().toLowerCase())) {
+            TextAdapter.sendComponent(player, MessageCache.getInstance().PERMISSION_FLAG_USE);
+            return;
+        }
+
+        if (target != null && target.equalsIgnoreCase("hand")) {
+            ItemStack stack = NMSUtil.getInstance().getActiveItem(player);
+            if (stack != null) {
+                target = GDPermissionManager.getInstance().getPermissionIdentifier(stack);
+            }
+        }
+
+        final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(playerData, player.getLocation());
+        final Set<Context> contextSet = CauseContextHelper.generateContexts(player, claim, contexts);
+        if (contextSet == null) {
+            return;
+        }
+
+        if (claim != null) {
+            if (flag == null && value == null && player.hasPermission(GDPermissions.COMMAND_LIST_CLAIM_FLAGS)) {
+                String defaultGroup = "";
+                for (Entry<String, CustomFlagGroupCategory> groupEntry : GriefDefenderPlugin.getGlobalConfig().getConfig().customFlags.getGroups().entrySet()) {
+                    final String permission = groupEntry.getValue().isAdminGroup() ? GDPermissions.FLAG_CUSTOM_ADMIN_BASE : GDPermissions.FLAG_CUSTOM_USER_BASE;
+                    if (!player.hasPermission(permission + "." + groupEntry.getKey()) && !src.getInternalPlayerData().canIgnoreClaim(claim)) {
+                        continue;
+                    }
+                    defaultGroup = groupEntry.getKey();
+                    break;
+                }
+                if (!defaultGroup.isEmpty()) {
+                    showCustomFlags(src, claim, defaultGroup);
+                } else {
+                    TextAdapter.sendComponent(player, MessageCache.getInstance().PERMISSION_FLAG_USE);
+                }
+                return;
+            } else if (flag != null && value != null) {
+                GDCauseStackManager.getInstance().pushCause(player);
+                PermissionResult result = CommandHelper.addFlagPermission(player, this.subject, claim, flag, target, PermissionUtil.getInstance().getTristateFromString(value.toUpperCase()), contextSet);
+                final String flagTarget = target;
+                if (result.getResultType() == ResultTypes.TARGET_NOT_VALID) {
+                    GriefDefenderPlugin.sendMessage(player, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.FLAG_INVALID_TARGET,
+                            ImmutableMap.of("target", flagTarget,
+                                    "flag", flag)));
+                } else if (result.getResultType() == ResultTypes.NO_PERMISSION) {
+                    GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().PERMISSION_FLAG_USE);
+                }
+                GDCauseStackManager.getInstance().popCause();
+                return;
+            }
+        } else {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().CLAIM_NOT_FOUND);
+        }
+    }
+
+    protected void showCustomFlags(GDPermissionUser src, GDClaim claim, String displayGroup) {
+        final Player player = src.getOnlinePlayer();
+        final String lastPermissionMenuType = this.lastActivePresetMenuMap.getIfPresent(player.getUniqueId());
+        if (lastPermissionMenuType != null && !lastPermissionMenuType.equalsIgnoreCase(displayGroup.toLowerCase())) {
+            PaginationUtil.getInstance().resetActivePage(player.getUniqueId());
+        }
+
+        TextComponent.Builder flagHeadBuilder = TextComponent.builder()
+                .append(" Displaying :", TextColor.AQUA);
+        final Map<String, CustomFlagGroupCategory> flagGroups = GriefDefenderPlugin.getGlobalConfig().getConfig().customFlags.getGroups();
+        List<String> groups = new ArrayList<>();
+        for (Map.Entry<String, CustomFlagGroupCategory> flagGroupEntry : flagGroups.entrySet()) {
+            final CustomFlagGroupCategory flagGroupCat = flagGroupEntry.getValue();
+            if (!flagGroupCat.isEnabled()) {
+                continue;
+            }
+            final String groupName = flagGroupEntry.getKey();
+            final boolean isAdminGroup = GriefDefenderPlugin.getGlobalConfig().getConfig().customFlags.getGroups().get(groupName).isAdminGroup();
+            final String permission = isAdminGroup ? GDPermissions.FLAG_CUSTOM_ADMIN_BASE : GDPermissions.FLAG_CUSTOM_USER_BASE;
+            if (!player.hasPermission(permission + "." + groupName) && !src.getInternalPlayerData().canIgnoreClaim(claim)) {
+                continue;
+            }
+
+            groups.add(groupName);
+        }
+
+        final CustomFlagGroupCategory flagGroupCat = flagGroups.get(displayGroup);
+        if (flagGroupCat == null || flagGroupCat.getFlagDefinitions().isEmpty()) {
+            TextAdapter.sendComponent(player, TextComponent.of("No custom flag definitions were found for group '" + displayGroup + "'."));
+            return;
+        }
+
+        Collections.sort(groups);
+        for (String group : groups) {
+            flagHeadBuilder.append(" ").append(displayGroup.equalsIgnoreCase(group) ? TextComponent.builder()
+                    .append(whiteOpenBracket)
+                    .append(group.toUpperCase(), flagGroups.get(group).isAdminGroup() ? TextColor.RED : TextColor.GOLD)
+                    .append(whiteCloseBracket).build() : 
+                        TextComponent.builder().append(group.toUpperCase(), TextColor.GRAY)
+                            .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createCustomFlagConsumer(src, claim, group))))
+                            .build());
+        }
+
+        List<Component> textComponents = new ArrayList<>();
+        for (GDCustomFlagDefinition customFlag : flagGroupCat.getFlagDefinitions().values()) {
+            Component flagText = TextComponent.builder()
+                .append(getCustomFlagText(customFlag))
+                .append(" ")
+                .append(this.getCustomClickableText(src, claim, customFlag, displayGroup))
+                .build();
+            textComponents.add(flagText);
+        }
+
+
+        Collections.sort(textComponents, UIHelper.PLAIN_COMPARATOR);
+        int fillSize = 20 - (textComponents.size() + 2);
+        for (int i = 0; i < fillSize; i++) {
+            textComponents.add(TextComponent.of(" "));
+        }
+
+        String lastMenu = this.lastActiveMenuTypeMap.getIfPresent(src.getUniqueId());
+        MenuType lastActiveMenu = MenuType.CLAIM;
+        if (lastMenu != null) {
+            lastActiveMenu = MenuType.valueOf(lastMenu.toUpperCase());
+        }
+        Component footer = null;
+        if (player.hasPermission(GDPermissions.ADVANCED_FLAGS)) {
+            footer = TextComponent.builder().append(whiteOpenBracket)
+            .append(TextComponent.of("PRESET").color(TextColor.GOLD)).append(whiteCloseBracket)
+            .append(" ")
+            .append(TextComponent.builder()
+                    .append(TextComponent.of("ADVANCED").color(TextColor.GRAY)
+                    .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createClaimFlagConsumer(src, claim, lastActiveMenu)))))
+                    .build())
+            .build();
+        }
+        PaginationList.Builder paginationBuilder = PaginationList.builder()
+                .title(flagHeadBuilder.build()).padding(TextComponent.builder(" ").decoration(TextDecoration.STRIKETHROUGH, true).build()).contents(textComponents).footer(footer);
+        final PaginationList paginationList = paginationBuilder.build();
+        Integer activePage = 1;
+        activePage = PaginationUtil.getInstance().getActivePage(player.getUniqueId());
+        if (activePage == null) {
+            activePage = 1;
+        }
+        this.lastActivePresetMenuMap.put(player.getUniqueId(), displayGroup.toLowerCase());
+        paginationList.sendTo(player, activePage);
+    }
+
+    protected void showFlagPermissions(GDPermissionUser src, GDClaim claim, MenuType displayType) {
+        final Player player = src.getOnlinePlayer();
+        boolean isAdmin = false;
+        if (player.hasPermission(GDPermissions.DELETE_CLAIM_ADMIN)) {
+            isAdmin = true;
+        }
+
+        final String lastPermissionMenuType = this.lastActiveMenuTypeMap.getIfPresent(player.getUniqueId());
+        if (lastPermissionMenuType != null && !lastPermissionMenuType.equalsIgnoreCase(displayType.name())) {
+            PaginationUtil.getInstance().resetActivePage(player.getUniqueId());
+        }
+
+        final Component showOverrideText = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.UI_CLICK_FILTER_TYPE, 
+                ImmutableMap.of("type", TextComponent.of("OVERRIDE", TextColor.RED)));
+        final Component showDefaultText = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.UI_CLICK_FILTER_TYPE, 
+                ImmutableMap.of("type", TextComponent.of("DEFAULT", TextColor.LIGHT_PURPLE)));
+        final Component showClaimText = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.UI_CLICK_FILTER_TYPE, 
+                ImmutableMap.of("type", TextComponent.of("CLAIM", TextColor.GOLD)));
+        final Component showInheritText = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.UI_CLICK_FILTER_TYPE, 
+                ImmutableMap.of("type", TextComponent.of("INHERIT", TextColor.AQUA)));
+        Component defaultFlagText = TextComponent.empty();
+        if (isAdmin) {
+            defaultFlagText = TextComponent.builder("")
+                    .append(displayType == MenuType.DEFAULT ? TextComponent.builder("")
+                            .append(whiteOpenBracket)
+                            .append("DEFAULT", TextColor.LIGHT_PURPLE)
+                            .append(whiteCloseBracket).build() : TextComponent.of("DEFAULT", TextColor.GRAY))
+                    .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createClaimFlagConsumer(src, claim, MenuType.DEFAULT))))
+                    .hoverEvent(HoverEvent.showText(showDefaultText)).build();
+        }
+        final Component overrideFlagText = TextComponent.builder("")
+                .append(displayType == MenuType.OVERRIDE ? TextComponent.builder("")
+                        .append(whiteOpenBracket)
+                        .append("OVERRIDE", TextColor.RED)
+                        .append(whiteCloseBracket).build() : TextComponent.of("OVERRIDE", TextColor.GRAY))
+                .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createClaimFlagConsumer(src, claim, MenuType.OVERRIDE))))
+                .hoverEvent(HoverEvent.showText(showOverrideText)).build();
+        final Component claimFlagText = TextComponent.builder("")
+                .append(displayType == MenuType.CLAIM ? TextComponent.builder("")
+                        .append(whiteOpenBracket)
+                        .append("CLAIM", TextColor.YELLOW)
+                        .append(whiteCloseBracket).build() : TextComponent.of("CLAIM", TextColor.GRAY))
+                .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createClaimFlagConsumer(src, claim, MenuType.CLAIM))))
+                .hoverEvent(HoverEvent.showText(showClaimText)).build();
+        final Component inheritFlagText = TextComponent.builder("")
+                .append(displayType == MenuType.INHERIT ? TextComponent.builder("")
+                        .append(whiteOpenBracket)
+                        .append("INHERIT", TextColor.AQUA)
+                        .append(whiteCloseBracket).build() : TextComponent.of("INHERIT", TextColor.GRAY))
+                .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createClaimFlagConsumer(src, claim, MenuType.INHERIT))))
+                .hoverEvent(HoverEvent.showText(showInheritText)).build();
+        Component claimFlagHead = TextComponent.empty();
+        if (this.subjectType == ClaimSubjectType.GLOBAL) {
+            if (isAdmin) {
+                claimFlagHead = TextComponent.builder("")
+                        .append(" Displaying : ", TextColor.AQUA)
+                        .append(defaultFlagText)
+                        .append("  ")
+                        .append(claimFlagText)
+                        .append("  ")
+                        .append(inheritFlagText)
+                        .append("  ")
+                        .append(overrideFlagText).build();
+            } else {
+                claimFlagHead = TextComponent.builder("")
+                        .append(" Displaying : ", TextColor.AQUA)
+                        .append(claimFlagText)
+                        .append("  ")
+                        .append(inheritFlagText)
+                        .append("  ")
+                        .append(overrideFlagText).build();
+            }
+        } else {
+            claimFlagHead = TextComponent.builder("")
+                    .append(" " + this.subjectType.getFriendlyName() + " ", TextColor.AQUA)
+                    .append(this.friendlySubjectName, TextColor.YELLOW)
+                    .append(" : ", TextColor.AQUA)
+                    .append(claimFlagText)
+                    .append("  ")
+                    .append(inheritFlagText)
+                    .append("  ")
+                    .append(overrideFlagText).build();
+        }
+
+        Set<Context> defaultContexts = new HashSet<>();
+        Set<Context> overrideContexts = new HashSet<>();
+        if (claim.isAdminClaim()) {
+            defaultContexts.add(ClaimContexts.ADMIN_DEFAULT_CONTEXT);
+            overrideContexts.add(ClaimContexts.ADMIN_OVERRIDE_CONTEXT);
+        } else if (claim.isBasicClaim() || claim.isSubdivision()) {
+            defaultContexts.add(ClaimContexts.BASIC_DEFAULT_CONTEXT);
+            overrideContexts.add(ClaimContexts.BASIC_OVERRIDE_CONTEXT);
+        } else if (claim.isTown()) {
+            defaultContexts.add(ClaimContexts.TOWN_DEFAULT_CONTEXT);
+            overrideContexts.add(ClaimContexts.TOWN_OVERRIDE_CONTEXT);
+        } else {
+            defaultContexts.add(ClaimContexts.WILDERNESS_DEFAULT_CONTEXT);
+            overrideContexts.add(ClaimContexts.WILDERNESS_OVERRIDE_CONTEXT);
+        }
+        defaultContexts.add(ClaimContexts.GLOBAL_DEFAULT_CONTEXT);
+        overrideContexts.add(ClaimContexts.GLOBAL_OVERRIDE_CONTEXT);
+        overrideContexts.add(claim.getOverrideClaimContext());
+
+        Map<String, FlagData> filteredContextMap = new HashMap<>();
+        for (Map.Entry<Set<Context>, Map<String, Boolean>> mapEntry : PermissionUtil.getInstance().getTransientPermissions(this.subject).entrySet()) {
+            final Set<Context> contextSet = mapEntry.getKey();
+            if (contextSet.contains(claim.getDefaultTypeContext())) {
+                this.addFilteredContexts(filteredContextMap, contextSet, MenuType.DEFAULT, mapEntry.getValue());
+            } else if (contextSet.contains(ClaimContexts.GLOBAL_DEFAULT_CONTEXT)) {
+                this.addFilteredContexts(filteredContextMap, contextSet, MenuType.DEFAULT, mapEntry.getValue());
+            }
+        }
+
+        final List<Claim> inheritParents = claim.getInheritedParents();
+        for (Map.Entry<Set<Context>, Map<String, Boolean>> mapEntry : PermissionUtil.getInstance().getPermanentPermissions(this.subject).entrySet()) {
+            final Set<Context> contextSet = mapEntry.getKey();
+            if (contextSet.contains(claim.getDefaultTypeContext())) {
+                this.addFilteredContexts(filteredContextMap, contextSet, MenuType.DEFAULT, mapEntry.getValue());
+            } else if (contextSet.contains(ClaimContexts.GLOBAL_DEFAULT_CONTEXT)) {
+                this.addFilteredContexts(filteredContextMap, contextSet, MenuType.DEFAULT, mapEntry.getValue());
+            }
+            if (displayType != MenuType.DEFAULT) {
+                if (contextSet.contains(claim.getContext())) {
+                    this.addFilteredContexts(filteredContextMap, contextSet, MenuType.CLAIM, mapEntry.getValue());
+                }
+                for (Claim parentClaim : inheritParents) {
+                    GDClaim parent = (GDClaim) parentClaim;
+                    // check parent context
+                    if (contextSet.contains(parent.getContext())) {
+                        this.addFilteredContexts(filteredContextMap, contextSet, MenuType.INHERIT, mapEntry.getValue());
+                    }
+                }
+                if (contextSet.contains(ClaimContexts.GLOBAL_OVERRIDE_CONTEXT)) {
+                    this.addFilteredContexts(filteredContextMap, contextSet, MenuType.OVERRIDE, mapEntry.getValue());
+                }
+                if (contextSet.contains(claim.getOverrideClaimContext())) {
+                    this.addFilteredContexts(filteredContextMap, contextSet, MenuType.OVERRIDE, mapEntry.getValue());
+                } else if (contextSet.contains(claim.getOverrideTypeContext())) {
+                    this.addFilteredContexts(filteredContextMap, contextSet, MenuType.OVERRIDE, mapEntry.getValue());
+                }
+            }
+        }
+
+        final Map<String, Map<Integer, Component>> textMap = new TreeMap<>();
+        for (Entry<String, FlagData> mapEntry : filteredContextMap.entrySet()) {
+            final FlagData flagData = mapEntry.getValue();
+            final Flag flag = flagData.flag;
+            for (FlagContextHolder flagHolder : flagData.flagContextMap.values()) {
+                if (displayType != MenuType.CLAIM && flagHolder.getType() != displayType) {
+                    continue;
+                }
+
+                final Set<Context> contexts = flagHolder.getAllContexts();
+                Component flagText = TextComponent.builder()
+                    .append(getFlagText(flag, contexts))
+                    .append(" ")
+                    .append(this.getClickableText(src, claim, flag, flagHolder, contexts, displayType))
+                    .build();
+                final int hashCode = Objects.hash(flag.getPermission(), contexts);
+                Map<Integer, Component> componentMap = textMap.get(flag.getPermission());
+                if (componentMap == null) {
+                    componentMap = new HashMap<>();
+                    componentMap.put(hashCode, flagText);
+                    textMap.put(flag.getPermission(), componentMap);
+                } else {
+                    componentMap.put(hashCode, flagText);
+                }
+            }
+        }
+
+        List<Component> textList = new ArrayList<>();
+        for (Entry<String, Map<Integer, Component>> mapEntry : textMap.entrySet()) {
+            textList.addAll(mapEntry.getValue().values());
+        }
+
+        Collections.sort(textList, UIHelper.PLAIN_COMPARATOR);
+        int fillSize = 20 - (textList.size() + 2);
+        for (int i = 0; i < fillSize; i++) {
+            textList.add(TextComponent.of(" "));
+        }
+
+        String lastActivePresetMenu = this.lastActivePresetMenuMap.getIfPresent(src.getUniqueId());
+        if (lastActivePresetMenu == null) {
+            lastActivePresetMenu = "user";
+        }
+        Component footer = null;
+        if (player.hasPermission(GDPermissions.ADVANCED_FLAGS)) {
+            footer = TextComponent.builder().append(TextComponent.builder()
+                    .append(TextComponent.of("PRESET").color(TextColor.GRAY)
+                    .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createCustomFlagConsumer(src, claim, lastActivePresetMenu)))))
+                    .build())
+                .append(" ")
+                .append(whiteOpenBracket)
+                .append(TextComponent.of("ADVANCED").color(TextColor.RED))
+                .append(whiteCloseBracket)
+                .build();
+        }
+        PaginationList.Builder paginationBuilder = PaginationList.builder()
+                .title(claimFlagHead).padding(TextComponent.builder(" ").decoration(TextDecoration.STRIKETHROUGH, true).build()).contents(textList).footer(footer);
+        final PaginationList paginationList = paginationBuilder.build();
+        Integer activePage = 1;
+        activePage = PaginationUtil.getInstance().getActivePage(player.getUniqueId());
+        if (activePage == null) {
+            activePage = 1;
+        }
+        this.lastActiveMenuTypeMap.put(player.getUniqueId(), displayType.name().toLowerCase());
+        paginationList.sendTo(player, activePage);
+    }
+
+    private void addFilteredContexts(Map<String, FlagData> filteredContextMap, Set<Context> contexts, MenuType type, Map<String, Boolean> permissions) {
+        for (Map.Entry<String, Boolean> permissionEntry : permissions.entrySet()) {
+            final Flag flag = FlagRegistryModule.getInstance().getById(permissionEntry.getKey()).orElse(null);
+            if (flag == null) {
+                continue;
+            }
+            final FlagData flagData = filteredContextMap.get(permissionEntry.getKey());
+            if (flagData != null) {
+                flagData.addContexts(flag, permissionEntry.getValue(), type, contexts);
+            } else {
+                filteredContextMap.put(permissionEntry.getKey(), new FlagData(flag, permissionEntry.getValue(), type, contexts));
+            }
+        }
+    }
+
+    private Component getCustomFlagText(GDCustomFlagDefinition customFlag) {
+        TextComponent definitionType = TextComponent.empty();
+        TextColor flagColor = TextColor.GREEN;
+        for (Context context : customFlag.getDefinitionContexts()) {
+            if (context.getKey().contains("default")) {
+                definitionType = TextComponent.builder()
+                        .append("\n")
+                        .append(MessageCache.getInstance().LABEL_TYPE.color(TextColor.AQUA))
+                        .append(" : ", TextColor.WHITE)
+                        .append("DEFAULT", TextColor.LIGHT_PURPLE)
+                        .append(" ")
+                        .append(context.getValue().toUpperCase(), TextColor.GRAY)
+                        .build();
+                flagColor = TextColor.LIGHT_PURPLE;
+            } else if (context.getKey().contains("override")) {
+                definitionType = TextComponent.builder()
+                        .append("\n")
+                        .append(MessageCache.getInstance().LABEL_TYPE.color(TextColor.AQUA))
+                        .append(" : ", TextColor.WHITE)
+                        .append("OVERRIDE", TextColor.RED)
+                        .append(" ")
+                        .append(context.getValue().toUpperCase(), TextColor.GRAY)
+                        .build();
+                flagColor = TextColor.RED;
+            }
+        }
+        if (definitionType == TextComponent.empty()) {
+            definitionType = TextComponent.builder()
+                    .append("\n")
+                    .append(MessageCache.getInstance().LABEL_TYPE.color(TextColor.AQUA))
+                    .append(" : ", TextColor.WHITE)
+                    .append("CLAIM", TextColor.YELLOW)
+                    .build();
+        }
+        final Component baseFlagText = TextComponent.builder()
+                .append(customFlag.getDisplayName(), flagColor)
+                .append(" ")
+                .hoverEvent(HoverEvent.showText(TextComponent.builder()
+                        .append(customFlag.getDescription())
+                        .append(definitionType)
+                        .build())).build();
+        return baseFlagText;
+    }
+
+    private TextColor getCustomFlagColor(GDCustomFlagDefinition customFlag) {
+        TextColor flagColor = TextColor.GREEN;
+        for (Context context : customFlag.getDefinitionContexts()) {
+            if (context.getKey().contains("default")) {
+                flagColor = TextColor.LIGHT_PURPLE;
+                break;
+            } else if (context.getKey().contains("override")) {
+                flagColor = TextColor.RED;
+                break;
+            }
+        }
+        return flagColor;
+    }
+
+    private Component getFlagText(Flag flag, Set<Context> contexts) {
+        boolean customContext = UIHelper.containsCustomContext(contexts);
+
+        final Component baseFlagText = TextComponent.builder().color(customContext ? TextColor.YELLOW : TextColor.GREEN).append(flag.getName() + " ")
+                .hoverEvent(HoverEvent.showText(TextComponent.builder()
+                        .append(flag.getDescription())
+                        .build())).build();
+        return baseFlagText;
+    }
+
+    private Component getCustomClickableText(GDPermissionUser src, GDClaim claim, GDCustomFlagDefinition customFlag, String flagGroup) {
+        boolean hasHover = false;
+        TextComponent.Builder hoverBuilder = TextComponent.builder();
+        final Player player = src.getOnlinePlayer();
+        boolean hasEditPermission = true;
+        Component denyReason = claim.allowEdit(player);
+        if (denyReason != null) {
+            hoverBuilder.append(denyReason).append("\n");
+            hasEditPermission = false;
+            hasHover = true;
+        }
+
+        final boolean isAdminGroup = GriefDefenderPlugin.getGlobalConfig().getConfig().customFlags.getGroups().get(flagGroup).isAdminGroup();
+        final String permission = isAdminGroup ? GDPermissions.FLAG_CUSTOM_ADMIN_BASE : GDPermissions.FLAG_CUSTOM_USER_BASE;
+         // check flag perm
+        if (!player.hasPermission(permission + "." + flagGroup + "." + customFlag.getDisplayName())) {
+            hoverBuilder.append(MessageCache.getInstance().PERMISSION_FLAG_USE).append("\n");
+            hasEditPermission = false;
+            hasHover = true;
+        }
+
+        List<GDActiveFlagData> dataResults = new ArrayList<>();
+        boolean hasGDContext = false;
+        Set<Context> definitionContexts = new HashSet<>(customFlag.getDefinitionContexts());
+        for (Context context : customFlag.getDefinitionContexts()) {
+            if (context.getKey().contains("gd_claim")) {
+                hasGDContext = true;
+                break;
+            }
+        }
+        if (!hasGDContext) {
+            definitionContexts.add(claim.getContext());
+        }
+        for (CustomFlagData flagData : customFlag.getFlagData()) {
+            final Set<Context> filteredContexts = new HashSet<>();
+            for (Context context : definitionContexts) {
+                if (context.getKey().contains("gd_claim")) {
+                    continue;
+                }
+
+                filteredContexts.add(context);
+            }
+
+            // Check override
+            filteredContexts.addAll(flagData.getContexts());
+            Set<Context> newContexts = new HashSet<>(filteredContexts);
+            newContexts.add(ClaimContexts.GLOBAL_OVERRIDE_CONTEXT);
+            newContexts.add(claim.getOverrideTypeContext());
+            newContexts.add(claim.getOverrideClaimContext());
+            Tristate result = PermissionUtil.getInstance().getPermissionValueWithRequiredContexts(claim, GriefDefenderPlugin.DEFAULT_HOLDER, flagData.getFlag().getPermission(), newContexts, "gd_claim");
+            if (result != Tristate.UNDEFINED) {
+                dataResults.add(new GDActiveFlagData(flagData, result, GDActiveFlagData.Type.OVERRIDE));
+                continue;
+            }
+
+            // Check claim
+            newContexts = new HashSet<>(filteredContexts);
+            newContexts.add(claim.getContext());
+            result = PermissionUtil.getInstance().getPermissionValueWithRequiredContexts(claim, GriefDefenderPlugin.DEFAULT_HOLDER, flagData.getFlag().getPermission(), newContexts, "gd_claim");
+            if (result != Tristate.UNDEFINED) {
+                dataResults.add(new GDActiveFlagData(flagData, result, GDActiveFlagData.Type.CLAIM));
+                continue;
+            }
+
+            // Check default
+            newContexts = new HashSet<>(filteredContexts);
+            newContexts.add(ClaimContexts.GLOBAL_DEFAULT_CONTEXT);
+            newContexts.add(claim.getDefaultTypeContext());
+            result = PermissionUtil.getInstance().getPermissionValueWithRequiredContexts(claim, GriefDefenderPlugin.DEFAULT_HOLDER, flagData.getFlag().getPermission(), newContexts, "gd_claim");
+            if (result != Tristate.UNDEFINED) {
+                dataResults.add(new GDActiveFlagData(flagData, result, GDActiveFlagData.Type.DEFAULT));
+                continue;
+            }
+            dataResults.add(new GDActiveFlagData(flagData, result, GDActiveFlagData.Type.UNDEFINED));
+        }
+        boolean properResult = true;
+        Tristate lastResult = null;
+        for (GDActiveFlagData activeFlagData : dataResults) {
+            final Tristate result = activeFlagData.getValue();
+            if (lastResult == null) {
+                lastResult = result;
+            } else if (lastResult != result) {
+                properResult = false;
+                break;
+            }
+        }
+
+        TextComponent.Builder valueBuilder = TextComponent.builder();
+        if (!properResult) {
+            if (hasEditPermission) {
+                hoverBuilder.append("Active Data : \n");
+                for (GDActiveFlagData activeFlagData : dataResults) {
+                    hoverBuilder.append(activeFlagData.getComponent())
+                        .append("\n");
+                }
+                hasHover = true;
+            }
+            valueBuilder.append("partial");
+            lastResult = null;
+        } else {
+            TextColor valueColor = TextColor.GRAY;
+            if (lastResult == Tristate.TRUE) {
+                valueColor = TextColor.GOLD;
+            } else if (lastResult == Tristate.FALSE) {
+                valueColor = TextColor.RED;
+            }
+            valueBuilder.append(String.valueOf(lastResult).toLowerCase(), valueColor);
+        }
+
+        if (hasEditPermission) {
+            if (lastResult == null || lastResult == Tristate.UNDEFINED) {
+                hoverBuilder.append(MessageCache.getInstance().FLAG_UI_CLICK_ALLOW);
+            } else if (lastResult == Tristate.TRUE) {
+                hoverBuilder.append(MessageCache.getInstance().FLAG_UI_CLICK_DENY);
+            } else {
+                hoverBuilder.append(MessageCache.getInstance().FLAG_UI_CLICK_REMOVE);
+            }
+
+            if (!customFlag.getDefinitionContexts().isEmpty()) {
+                hoverBuilder.append("\nContexts: ");
+            }
+
+            for (Context context : customFlag.getDefinitionContexts()) {
+                hoverBuilder.append("\n");
+                final String key = context.getKey();
+                final String value = context.getValue();
+                TextColor keyColor = TextColor.AQUA;
+                if (key.contains("default")) {
+                    keyColor = TextColor.LIGHT_PURPLE;
+                } else if (key.contains("override")) {
+                    keyColor = TextColor.RED;
+                } else if (key.contains("server")) {
+                    keyColor = TextColor.GRAY;
+                }
+                hoverBuilder.append(key, keyColor)
+                        .append("=", TextColor.WHITE)
+                        .append(value.replace("minecraft:", ""), TextColor.GRAY);
+            }
+            hasHover = true;
+        }
+
+        if (hasHover) {
+            valueBuilder.hoverEvent(HoverEvent.showText(hoverBuilder.build()));
+        }
+        TextComponent.Builder textBuilder = null;
+        if (hasEditPermission) {
+            textBuilder = TextComponent.builder()
+            .append(valueBuilder
+                    .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createCustomFlagConsumer(src, claim, customFlag, lastResult, flagGroup))))
+                    .build());
+        } else {
+            textBuilder = TextComponent.builder()
+                    .append(valueBuilder
+                            .build());
+        }
+
+        return textBuilder.build();
+    }
+
+    private Component getClickableText(GDPermissionUser src, GDClaim claim, Flag flag, FlagContextHolder flagHolder, Set<Context> contexts, MenuType displayType) {
+        Component hoverEventText = TextComponent.empty();
+        final MenuType flagType = flagHolder.getType();
+        final Player player = src.getOnlinePlayer();
+        boolean hasEditPermission = true;
+        if (displayType == MenuType.DEFAULT) {
+            if (!src.getInternalPlayerData().canManageFlagDefaults) {
+                hoverEventText = MessageCache.getInstance().PERMISSION_FLAG_DEFAULTS;
+                hasEditPermission = false;
+            }
+        } else if (flagType == MenuType.OVERRIDE) {
+            if (!src.getInternalPlayerData().canManageFlagOverrides) {
+                hoverEventText = MessageCache.getInstance().PERMISSION_FLAG_OVERRIDES;
+                hasEditPermission = false;
+            }
+        } else if (flagType == MenuType.INHERIT) {
+            UUID parentUniqueId = null;
+            for (Context context : contexts) {
+                if (context.getKey().equals("gd_claim")) {
+                    try {
+                        parentUniqueId = UUID.fromString(context.getValue());
+                    } catch (IllegalArgumentException e) {
+                        // ignore
+                    }
+                }
+            }
+            // should never happen but just in case
+            if (parentUniqueId == null) {
+                hoverEventText = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.FLAG_UI_INHERIT_PARENT,
+                        ImmutableMap.of("name", "unknown"));
+                hasEditPermission = false;
+            } else {
+                final GDClaim parentClaim = (GDClaim) GriefDefenderPlugin.getInstance().dataStore.getClaim(claim.getWorldUniqueId(), parentUniqueId);
+                hoverEventText = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.FLAG_UI_INHERIT_PARENT,
+                        ImmutableMap.of("name", parentClaim.getFriendlyNameType()));
+                hasEditPermission = false;
+            }
+        } else {
+            Component denyReason = claim.allowEdit(player);
+            if (denyReason != null) {
+                hoverEventText = denyReason;
+                hasEditPermission = false;
+            } else {
+                // check flag perm
+                if (!player.hasPermission(GDPermissions.USER_CLAIM_FLAGS + "." + flag.getName().toLowerCase())) {
+                    hoverEventText = MessageCache.getInstance().PERMISSION_FLAG_USE;
+                    hasEditPermission = false;
+                }
+            }
+        }
+
+        Set<Context> sortedContexts = new TreeSet<>(new Comparator<Context>() {
+            @Override
+            public int compare(Context o1, Context o2) {
+                 return o1.getKey().compareTo(o2.getKey());
+            }
+        });
+        sortedContexts.addAll(contexts);
+
+        final boolean customContexts = UIHelper.containsCustomContext(contexts);
+        Component flagContexts = UIHelper.getFriendlyContextString(claim, contexts);
+
+        Component hoverText = TextComponent.builder()
+                .append(hoverEventText)
+                .append(hoverEventText == TextComponent.empty() ? "" : "\n")
+                .append(UIHelper.getPermissionMenuTypeHoverText(flagHolder, displayType))
+                .append("\n")
+                .append(flagContexts)
+                .build();
+
+        Tristate newValue = Tristate.UNDEFINED;
+        Boolean value = flagHolder.getValue();
+
+        if (customContexts) {
+            if (value) {
+                newValue = Tristate.FALSE;
+            } else {
+                newValue = Tristate.TRUE;
+            }
+        } else {
+            if (displayType == MenuType.DEFAULT || (displayType == MenuType.CLAIM && flagHolder.getType() == MenuType.DEFAULT)) {
+                newValue = Tristate.fromBoolean(!value);
+            } else {
+                // Always fall back to transient default
+                newValue = Tristate.UNDEFINED;
+            }
+        }
+
+        TextComponent.Builder valueBuilder = TextComponent.builder()
+                .append(String.valueOf(value), flagHolder.getColor())
+                .hoverEvent(HoverEvent.showText(hoverText));
+        TextComponent.Builder textBuilder = null;
+        if (hasEditPermission) {
+            textBuilder = TextComponent.builder()
+            .append(valueBuilder
+                    .hoverEvent(HoverEvent.showText(hoverText))
+                    .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createFlagConsumer(src, claim, flag, flagHolder, newValue, contexts, displayType))))
+                    .build());
+        } else {
+            textBuilder = TextComponent.builder()
+                    .append(valueBuilder
+                            .hoverEvent(HoverEvent.showText(hoverText))
+                            .build());
+        }
+
+        // check source/target
+        Component source = null;
+        Component target = null;
+        final Component whiteOpenBracket = TextComponent.of("[", TextColor.WHITE);
+        final Component whiteCloseBracket = TextComponent.of("]", TextColor.WHITE);
+        for (Context context : contexts) {
+            if (context.getKey().equals(ContextKeys.SOURCE)) {
+                source = TextComponent.builder()
+                            .append(whiteOpenBracket)
+                            .append("s", TextColor.GREEN)
+                            .append("=", TextColor.WHITE)
+                            .append(context.getValue().replace("minecraft:", ""), TextColor.GOLD)
+                            .append(whiteCloseBracket)
+                            .hoverEvent(HoverEvent.showText(MessageCache.getInstance().LABEL_SOURCE))
+                            .build();
+                textBuilder.append(" ").append(source);
+            } else if (context.getKey().equals(ContextKeys.TARGET)) {
+                target = TextComponent.builder()
+                        .append(whiteOpenBracket)
+                        .append("t", TextColor.GREEN)
+                        .append("=", TextColor.WHITE)
+                        .append(context.getValue().replace("minecraft:", ""), TextColor.GOLD)
+                        .append(whiteCloseBracket)
+                        .hoverEvent(HoverEvent.showText(MessageCache.getInstance().LABEL_TARGET))
+                        .build();
+                textBuilder.append(" ").append(target);
+            }
+        }
+
+        if (customContexts) {
+            textBuilder.append(" ")
+                .append("[", TextColor.WHITE)
+                .append(TextComponent.builder()
+                        .append("x", TextColor.RED)
+                        .hoverEvent(HoverEvent.showText(MessageCache.getInstance().FLAG_UI_CLICK_REMOVE))
+                        .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createFlagConsumer(src, claim, flag, flagHolder, Tristate.UNDEFINED, contexts, displayType))))
+                        .build())
+                .append("]", TextColor.WHITE);
+        }
+
+        return textBuilder.build();
+    }
+
+    private Consumer<CommandSource> createCustomFlagConsumer(GDPermissionUser src, GDClaim claim, GDCustomFlagDefinition customFlag, Tristate currentValue, String displayType) {
+        final Player player = src.getOnlinePlayer();
+        return consumer -> {
+            GDCauseStackManager.getInstance().pushCause(player);
+            boolean hasGDContext = false;
+            Set<Context> definitionContexts = new HashSet<>(customFlag.getDefinitionContexts());
+            for (Context context : customFlag.getDefinitionContexts()) {
+                if (context.getKey().contains("gd_claim")) {
+                    hasGDContext = true;
+                    break;
+                }
+            }
+            if (!hasGDContext) {
+                definitionContexts.add(claim.getContext());
+            }
+            for (CustomFlagData flagData : customFlag.getFlagData()) {
+                final Set<Context> newContexts = new HashSet<>(definitionContexts);
+                newContexts.addAll(flagData.getContexts());
+                Tristate newValue = Tristate.UNDEFINED;
+                if (currentValue == null || currentValue == Tristate.UNDEFINED) {
+                    newValue = Tristate.TRUE;
+                } else if (currentValue == Tristate.TRUE) {
+                    newValue = Tristate.FALSE;
+                } else {
+                    newValue = Tristate.UNDEFINED;
+                }
+
+                PermissionResult result = null;
+                final Flag flag = flagData.getFlag();
+                GDFlagPermissionEvent.Set event = new GDFlagPermissionEvent.Set(this.subject, flagData.getFlag(), newValue, newContexts);
+                GriefDefender.getEventManager().post(event);
+                if (event.cancelled()) {
+                    return;
+                }
+
+                result = GriefDefenderPlugin.getInstance().getPermissionProvider().setPermissionValue(GriefDefenderPlugin.DEFAULT_HOLDER, flag, newValue, newContexts);
+            }
+            GDCauseStackManager.getInstance().popCause();
+            showCustomFlags(src, claim, displayType);
+        };
+    }
+
+    private Consumer<CommandSource> createFlagConsumer(GDPermissionUser src, GDClaim claim, Flag flag, FlagContextHolder flagHolder, Tristate newValue, Set<Context> contexts, MenuType displayType) {
+        final Player player = src.getOnlinePlayer();
+        return consumer -> {
+            Set<Context> newContexts = new HashSet<>();
+            GDCauseStackManager.getInstance().pushCause(player);
+            final boolean isCustom = UIHelper.containsCustomContext(contexts);
+            if (!isCustom && displayType == MenuType.CLAIM) {
+                newContexts.add(claim.getContext());
+            } else {
+                newContexts.addAll(contexts);
+            }
+
+            // Remove server context
+            final Iterator<Context> iterator = newContexts.iterator();
+            while (iterator.hasNext()) {
+                final Context context = iterator.next();
+                if (context.getKey().equals("server")) {
+                    iterator.remove();
+                }
+            }
+
+            GDFlagPermissionEvent.Set event = new GDFlagPermissionEvent.Set(this.subject, flag, newValue, newContexts);
+            GriefDefender.getEventManager().post(event);
+            GDCauseStackManager.getInstance().popCause();
+            if (event.cancelled()) {
+                return;
+            }
+
+            PermissionResult result = PermissionUtil.getInstance().setPermissionValue(this.subject, flag, newValue, newContexts);
+            if (result.successful()) {
+                showFlagPermissions(src, claim, displayType);
+            }
+        };
+    }
+
+    private Consumer<CommandSource> createCustomFlagConsumer(GDPermissionUser src, GDClaim claim, String flagGroup) {
+        return consumer -> {
+            showCustomFlags(src, claim, flagGroup);
+        };
+    }
+
+    private Consumer<CommandSource> createClaimFlagConsumer(GDPermissionUser src, GDClaim claim, MenuType flagType) {
+        return consumer -> {
+            showFlagPermissions(src, claim, flagType);
+        };
+    }
+}
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/command/ClaimOptionBase.java b/sponge/src/main/java/com/griefdefender/command/ClaimOptionBase.java
new file mode 100644
index 0000000..581389d
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/ClaimOptionBase.java
@@ -0,0 +1,913 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.InvalidCommandArgument;
+import com.github.benmanes.caffeine.cache.Cache;
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import com.google.common.reflect.TypeToken;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.GriefDefender;
+import com.griefdefender.api.Tristate;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.ClaimContexts;
+import com.griefdefender.api.permission.Context;
+import com.griefdefender.api.permission.ContextKeys;
+import com.griefdefender.api.permission.flag.Flag;
+import com.griefdefender.api.permission.flag.Flags;
+import com.griefdefender.api.permission.option.Option;
+import com.griefdefender.api.permission.option.Options;
+import com.griefdefender.api.permission.option.type.CreateModeType;
+import com.griefdefender.api.permission.option.type.CreateModeTypes;
+import com.griefdefender.api.permission.option.type.GameModeType;
+import com.griefdefender.api.permission.option.type.GameModeTypes;
+import com.griefdefender.api.permission.option.type.WeatherType;
+import com.griefdefender.api.permission.option.type.WeatherTypes;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.event.GDCauseStackManager;
+import com.griefdefender.internal.pagination.PaginationList;
+import com.griefdefender.permission.GDPermissionHolder;
+import com.griefdefender.permission.GDPermissionUser;
+import com.griefdefender.permission.GDPermissions;
+import com.griefdefender.permission.option.GDOption;
+import com.griefdefender.permission.ui.ClaimClickData;
+import com.griefdefender.permission.ui.FlagData;
+import com.griefdefender.permission.ui.MenuType;
+import com.griefdefender.permission.ui.OptionData;
+import com.griefdefender.permission.ui.OptionData.OptionContextHolder;
+import com.griefdefender.permission.ui.UIHelper;
+import com.griefdefender.permission.ui.FlagData.FlagContextHolder;
+import com.griefdefender.registry.FlagRegistryModule;
+import com.griefdefender.registry.OptionRegistryModule;
+import com.griefdefender.text.action.GDCallbackHolder;
+import com.griefdefender.util.CauseContextHelper;
+import com.griefdefender.util.PaginationUtil;
+import com.griefdefender.util.PermissionUtil;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import net.kyori.text.event.ClickEvent;
+import net.kyori.text.event.HoverEvent;
+import net.kyori.text.format.TextColor;
+import net.kyori.text.format.TextDecoration;
+import org.spongepowered.api.command.CommandSource;
+import org.spongepowered.api.entity.living.player.Player;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+@SuppressWarnings({ "unchecked", "rawtypes" })
+public abstract class ClaimOptionBase extends BaseCommand {
+
+    protected GDPermissionHolder subject;
+    protected ClaimSubjectType subjectType;
+    protected String friendlySubjectName;
+    private final Cache<UUID, MenuType> lastActiveMenuTypeMap = Caffeine.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES)
+            .build();
+
+    protected ClaimOptionBase(ClaimSubjectType type) {
+        this.subjectType = type;
+    }
+
+    public void execute(Player player, String[] args) throws InvalidCommandArgument {
+        final GDPermissionUser src = PermissionHolderCache.getInstance().getOrCreateUser(player);
+        final GDPermissionHolder commandSubject = subject;
+        String commandOption = null;
+        String value = null;
+        String contexts = null;
+        final String arguments = String.join(" ", args);
+        int index = arguments.indexOf("context[");
+        if (index != -1) {
+            contexts = arguments.substring(index, arguments.length());
+        }
+        if (args.length > 0) {
+            if (args.length < 2) {
+                throw new InvalidCommandArgument();
+            }
+            commandOption = args[0];
+            value = args[1];
+        }
+
+        Option option = null;
+        if (commandOption != null) {
+            option = GriefDefender.getRegistry().getType(Option.class, commandOption).orElse(null);
+            if (commandOption != null && option == null) {
+                TextAdapter.sendComponent(player, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.OPTION_NOT_FOUND, ImmutableMap.of(
+                        "option", commandOption)));
+                return;
+            }
+        }
+
+        final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        final GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(playerData, player.getLocation());
+        if (claim.isWilderness()) {
+            if(!playerData.canManageWilderness && !playerData.canIgnoreClaim(claim)) {
+                GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().PERMISSION_GLOBAL_OPTION);
+                return;
+            }
+        } else if (!claim.isTown() && !playerData.canManageAdminClaims && !playerData.canIgnoreClaim(claim)) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().PERMISSION_GLOBAL_OPTION);
+            return;
+        }
+        if (option != null) {
+            if (option.isGlobal()) {
+                if (!player.hasPermission(GDPermissions.MANAGE_GLOBAL_OPTIONS +"." + option.getPermission().toLowerCase())) {
+                    GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().PERMISSION_GLOBAL_OPTION);
+                    return;
+                }
+            } else if (!player.hasPermission(GDPermissions.USER_CLAIM_OPTIONS +"." + option.getPermission().toLowerCase())) {
+                GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().PERMISSION_PLAYER_OPTION);
+                return;
+            }
+        }
+
+        if (!playerData.canManageAdminClaims && !playerData.canIgnoreClaim(claim)) {
+            final Component denyMessage = claim.allowEdit(player);
+            if (denyMessage != null) {
+                GriefDefenderPlugin.sendMessage(player, denyMessage);
+                return;
+            }
+        }
+
+        final Set<Context> contextSet = CauseContextHelper.generateContexts(player, claim, contexts);
+        if (contextSet == null) {
+            return;
+        }
+
+        if (claim != null) {
+            if (commandOption == null && value == null && player.hasPermission(GDPermissions.COMMAND_LIST_CLAIM_OPTIONS)) {
+                showOptionPermissions(src, (GDClaim) claim, MenuType.CLAIM);
+                return;
+            } else if (option != null && value != null) {
+                if (!((GDOption) option).validateStringValue(value, false)) {
+                    GriefDefenderPlugin.sendMessage(player, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.OPTION_INVALID_VALUE, 
+                            ImmutableMap.of(
+                                    "value", value,
+                                    "option", option.getName(),
+                                    "type", option.getAllowedType().getSimpleName())));
+                    return;
+                }
+
+                MenuType type = MenuType.DEFAULT;
+                for (Context context : contextSet) {
+                    if (context.getKey().equals(ContextKeys.CLAIM)) {
+                        type = MenuType.CLAIM;
+                        break;
+                    }
+                    if (context.getKey().equals(ContextKeys.CLAIM_OVERRIDE)) {
+                        type = MenuType.OVERRIDE;
+                        break;
+                    }
+                }
+                if (!option.isGlobal()) {
+                    contextSet.add(claim.getContext());
+                    if (contextSet.isEmpty() ) {
+                        type = MenuType.CLAIM;
+                    }
+                }
+                GDCauseStackManager.getInstance().pushCause(player);
+                PermissionUtil.getInstance().setOptionValue(this.subject, option.getPermission(), value, contextSet);
+                TextAdapter.sendComponent(player, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.OPTION_SET_TARGET,
+                                ImmutableMap.of(
+                                        "type", type.name().toUpperCase(),
+                                        "option", option.getName(),
+                                        "contexts", UIHelper.getFriendlyContextString(claim, contextSet),
+                                        "value", TextComponent.of(value).color(TextColor.LIGHT_PURPLE),
+                                        "target", subject.getFriendlyName())));
+                GDCauseStackManager.getInstance().popCause();
+                return;
+            }
+        }
+    }
+
+    protected void showOptionPermissions(GDPermissionUser src, GDClaim claim, MenuType displayType) {
+        boolean isAdmin = false;
+        final Player player = src.getOnlinePlayer();
+        final GDPlayerData playerData = src.getInternalPlayerData();
+        final boolean isTaxEnabled = GriefDefenderPlugin.getActiveConfig(player.getWorld().getProperties()).getConfig().claim.bankTaxSystem;
+        if (player.hasPermission(GDPermissions.DELETE_CLAIM_ADMIN)) {
+            isAdmin = true;
+        }
+
+        final MenuType lastFlagType = this.lastActiveMenuTypeMap.getIfPresent(player.getUniqueId());
+        if (lastFlagType != null && lastFlagType != displayType) {
+            PaginationUtil.getInstance().resetActivePage(player.getUniqueId());
+        }
+        final Component whiteOpenBracket = TextComponent.of("[", TextColor.AQUA);
+        final Component whiteCloseBracket = TextComponent.of("]", TextColor.AQUA);
+        final Component showOverrideText = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.UI_CLICK_FILTER_TYPE, 
+                ImmutableMap.of("type", TextComponent.of("OVERRIDE", TextColor.RED)));
+        final Component showDefaultText = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.UI_CLICK_FILTER_TYPE, 
+                ImmutableMap.of("type", TextComponent.of("DEFAULT", TextColor.LIGHT_PURPLE)));
+        final Component showClaimText = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.UI_CLICK_FILTER_TYPE, 
+                ImmutableMap.of("type", TextComponent.of("CLAIM", TextColor.GOLD)));
+        final Component showInheritText = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.UI_CLICK_FILTER_TYPE, 
+                ImmutableMap.of("type", TextComponent.of("INHERIT", TextColor.AQUA)));
+        Component defaultFlagText = TextComponent.empty();
+        if (isAdmin) {
+            defaultFlagText = TextComponent.builder("")
+                    .append(displayType == MenuType.DEFAULT ? TextComponent.builder("")
+                            .append(whiteOpenBracket)
+                            .append("DEFAULT", TextColor.LIGHT_PURPLE)
+                            .append(whiteCloseBracket).build() : TextComponent.of("DEFAULT", TextColor.GRAY))
+                    .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createClaimOptionConsumer(src, claim, MenuType.DEFAULT))))
+                    .hoverEvent(HoverEvent.showText(showDefaultText)).build();
+        }
+        final Component overrideFlagText = TextComponent.builder("")
+                .append(displayType == MenuType.OVERRIDE ? TextComponent.builder("")
+                        .append(whiteOpenBracket)
+                        .append("OVERRIDE", TextColor.RED)
+                        .append(whiteCloseBracket).build() : TextComponent.of("OVERRIDE", TextColor.GRAY))
+                .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createClaimOptionConsumer(src, claim, MenuType.OVERRIDE))))
+                .hoverEvent(HoverEvent.showText(showOverrideText)).build();
+        final Component claimFlagText = TextComponent.builder("")
+                .append(displayType == MenuType.CLAIM ? TextComponent.builder("")
+                        .append(whiteOpenBracket)
+                        .append("CLAIM", TextColor.YELLOW)
+                        .append(whiteCloseBracket).build() : TextComponent.of("CLAIM", TextColor.GRAY))
+                .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createClaimOptionConsumer(src, claim, MenuType.CLAIM))))
+                .hoverEvent(HoverEvent.showText(showClaimText)).build();
+        final Component inheritFlagText = TextComponent.builder("")
+                .append(displayType == MenuType.INHERIT ? TextComponent.builder("")
+                        .append(whiteOpenBracket)
+                        .append("INHERIT", TextColor.AQUA)
+                        .append(whiteCloseBracket).build() : TextComponent.of("INHERIT", TextColor.GRAY))
+                .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createClaimOptionConsumer(src, claim, MenuType.INHERIT))))
+                .hoverEvent(HoverEvent.showText(showInheritText)).build();
+        Component claimOptionHead = TextComponent.empty();
+        if (this.subjectType == ClaimSubjectType.GLOBAL) {
+            if (isAdmin) {
+                claimOptionHead = TextComponent.builder("")
+                        .append(" Displaying : ", TextColor.AQUA)
+                        .append(defaultFlagText)
+                        .append("  ")
+                        .append(claimFlagText)
+                        .append("  ")
+                        .append(inheritFlagText)
+                        .append("  ")
+                        .append(overrideFlagText).build();
+            } else {
+                claimOptionHead = TextComponent.builder("")
+                        .append(" Displaying : ", TextColor.AQUA)
+                        .append(claimFlagText)
+                        .append("  ")
+                        .append(inheritFlagText)
+                        .append("  ")
+                        .append(overrideFlagText).build();
+            }
+        } else {
+            claimOptionHead = TextComponent.builder("")
+                    .append(" " + this.subjectType.getFriendlyName() + " ", TextColor.AQUA)
+                    .append(this.friendlySubjectName, TextColor.YELLOW)
+                    .append(" : ", TextColor.AQUA)
+                    .append(claimFlagText)
+                    .append("  ")
+                    .append(inheritFlagText)
+                    .append("  ")
+                    .append(overrideFlagText).build();
+        }
+
+        Set<Context> defaultContexts = new HashSet<>();
+        Set<Context> overrideContexts = new HashSet<>();
+        if (claim.isAdminClaim()) {
+            defaultContexts.add(ClaimContexts.ADMIN_DEFAULT_CONTEXT);
+            overrideContexts.add(ClaimContexts.ADMIN_OVERRIDE_CONTEXT);
+        } else if (claim.isBasicClaim() || claim.isSubdivision()) {
+            defaultContexts.add(ClaimContexts.BASIC_DEFAULT_CONTEXT);
+            overrideContexts.add(ClaimContexts.BASIC_OVERRIDE_CONTEXT);
+        } else if (claim.isTown()) {
+            defaultContexts.add(ClaimContexts.TOWN_DEFAULT_CONTEXT);
+            overrideContexts.add(ClaimContexts.TOWN_OVERRIDE_CONTEXT);
+        } else {
+            defaultContexts.add(ClaimContexts.WILDERNESS_DEFAULT_CONTEXT);
+            overrideContexts.add(ClaimContexts.WILDERNESS_OVERRIDE_CONTEXT);
+        }
+        defaultContexts.add(ClaimContexts.GLOBAL_DEFAULT_CONTEXT);
+        overrideContexts.add(ClaimContexts.GLOBAL_OVERRIDE_CONTEXT);
+        overrideContexts.add(claim.getOverrideClaimContext());
+
+        Map<String, OptionData> filteredContextMap = new HashMap<>();
+        for (Map.Entry<Set<Context>, Map<String, String>> mapEntry : PermissionUtil.getInstance().getTransientOptions(this.subject).entrySet()) {
+            final Set<Context> contextSet = mapEntry.getKey();
+            if (contextSet.contains(claim.getDefaultTypeContext()) || contextSet.contains(ClaimContexts.GLOBAL_DEFAULT_CONTEXT)) {
+                this.addFilteredContexts(src, filteredContextMap, contextSet, MenuType.DEFAULT, mapEntry.getValue());
+            }
+        }
+
+        if (displayType == MenuType.DEFAULT) {
+            final Set<Context> contexts = new HashSet<>();
+            contexts.add(ClaimContexts.GLOBAL_DEFAULT_CONTEXT);
+            for (Option option : OptionRegistryModule.getInstance().getAll()) {
+                boolean found = false;
+                for (Entry<String, OptionData> optionEntry : filteredContextMap.entrySet()) {
+                    if (optionEntry.getValue().option == option) {
+                        found = true;
+                        break;
+                    }
+                }
+                if (!found) {
+                    filteredContextMap.put(option.getPermission(), new OptionData(option, "undefined", displayType, contexts));
+                }
+            }
+        }
+
+        if (displayType == MenuType.CLAIM) {
+            final Set<Context> contexts = new HashSet<>();
+            contexts.add(ClaimContexts.GLOBAL_DEFAULT_CONTEXT);
+            filteredContextMap.put(Options.PVP.getPermission(), new OptionData(Options.PVP, "undefined", MenuType.DEFAULT, contexts));
+        }
+        for (Map.Entry<Set<Context>, Map<String, String>> mapEntry : PermissionUtil.getInstance().getPermanentOptions(this.subject).entrySet()) {
+            final Set<Context> contextSet = mapEntry.getKey();
+            if (contextSet.contains(ClaimContexts.GLOBAL_DEFAULT_CONTEXT)) {
+                this.addFilteredContexts(src, filteredContextMap, contextSet, MenuType.DEFAULT, mapEntry.getValue());
+            }
+            if (contextSet.contains(claim.getDefaultTypeContext())) {
+                this.addFilteredContexts(src, filteredContextMap, contextSet, MenuType.DEFAULT, mapEntry.getValue());
+            }
+            if (displayType != MenuType.DEFAULT) {
+                if (claim.isTown() || isAdmin) {
+                    if (contextSet.contains(claim.getContext())) {
+                        this.addFilteredContexts(src, filteredContextMap, contextSet, MenuType.CLAIM, mapEntry.getValue());
+                    }
+                }
+                if (contextSet.contains(ClaimContexts.GLOBAL_OVERRIDE_CONTEXT)) {
+                    this.addFilteredContexts(src, filteredContextMap, contextSet, MenuType.OVERRIDE, mapEntry.getValue());
+                }
+                if (contextSet.contains(claim.getOverrideClaimContext())) {
+                    this.addFilteredContexts(src, filteredContextMap, contextSet, MenuType.OVERRIDE, mapEntry.getValue());
+                } else if (contextSet.contains(claim.getOverrideTypeContext())) {
+                    this.addFilteredContexts(src, filteredContextMap, contextSet, MenuType.OVERRIDE, mapEntry.getValue());
+                }
+            }
+        }
+
+        Map<Set<Context>, ClaimClickData> inheritPermissionMap = Maps.newHashMap();
+
+        final List<Claim> inheritParents = claim.getInheritedParents();
+        Collections.reverse(inheritParents);
+        for (Claim current : inheritParents) {
+            GDClaim currentClaim = (GDClaim) current;
+            for (Map.Entry<Set<Context>, Map<String, String>> mapEntry : PermissionUtil.getInstance().getPermanentOptions(this.subject).entrySet()) {
+                final Set<Context> contextSet = mapEntry.getKey();
+                if (contextSet.contains(currentClaim.getContext())) {
+                    inheritPermissionMap.put(mapEntry.getKey(), new ClaimClickData(currentClaim, mapEntry.getValue()));
+                }
+            }
+        }
+
+        final Map<String, Map<Integer, Component>> textMap = new TreeMap<>();
+        for (Entry<String, OptionData> mapEntry : filteredContextMap.entrySet()) {
+            final OptionData optionData = mapEntry.getValue();
+            final Option option = optionData.option;
+            if (option.getName().contains("tax") && !GriefDefenderPlugin.getGlobalConfig().getConfig().claim.bankTaxSystem) {
+                continue;
+            }
+            for (OptionContextHolder optionHolder : optionData.optionContextMap.values()) {
+                if (displayType != MenuType.CLAIM && optionHolder.getType() != displayType) {
+                    continue;
+                }
+
+                final Set<Context> contexts = optionHolder.getAllContexts();
+                Component optionText = getClickableOptionComponent(src, claim, option, optionHolder, contexts, displayType);
+                final int hashCode = Objects.hash(option.getPermission(), contexts);
+                Map<Integer, Component> componentMap = textMap.get(option.getPermission());
+                if (componentMap == null) {
+                    componentMap = new HashMap<>();
+                    componentMap.put(hashCode, optionText);
+                    textMap.put(option.getPermission(), componentMap);
+                } else {
+                    componentMap.put(hashCode, optionText);
+                }
+            }
+        }
+
+        List<Component> textList = new ArrayList<>();
+        for (Entry<String, Map<Integer, Component>> mapEntry : textMap.entrySet()) {
+            textList.addAll(mapEntry.getValue().values());
+        }
+
+        Collections.sort(textList, UIHelper.PLAIN_COMPARATOR);
+        int fillSize = 20 - (textList.size() + 2);
+        for (int i = 0; i < fillSize; i++) {
+            textList.add(TextComponent.of(" "));
+        }
+
+        PaginationList.Builder paginationBuilder = PaginationList.builder()
+                .title(claimOptionHead).padding(TextComponent.builder(" ").decoration(TextDecoration.STRIKETHROUGH, true).build()).contents(textList);
+        final PaginationList paginationList = paginationBuilder.build();
+        Integer activePage = 1;
+        activePage = PaginationUtil.getInstance().getActivePage(player.getUniqueId());
+        if (activePage == null) {
+            activePage = 1;
+        }
+        this.lastActiveMenuTypeMap.put(player.getUniqueId(), displayType);
+        paginationList.sendTo(player, activePage);
+    }
+
+    private void addFilteredContexts(GDPermissionUser src, Map<String, OptionData> filteredContextMap, Set<Context> contexts, MenuType type, Map<String, String> permissions) {
+        final Player player = src.getOnlinePlayer();
+        final GDPlayerData playerData = src.getInternalPlayerData();
+        for (Map.Entry<String, String> permissionEntry : permissions.entrySet()) {
+            final Option option = OptionRegistryModule.getInstance().getById(permissionEntry.getKey()).orElse(null);
+            if (option == null) {
+                continue;
+            }
+            if (option.getName().contains("tax") && !GriefDefenderPlugin.getGlobalConfig().getConfig().claim.bankTaxSystem) {
+                continue;
+            }
+
+            if (option.isGlobal()) {
+                if (!player.hasPermission(GDPermissions.MANAGE_GLOBAL_OPTIONS +"." + option.getName().toLowerCase())) {
+                    continue;
+                }
+            } else if (((GDOption) option).isAdmin()) {
+                if (!player.hasPermission(GDPermissions.MANAGE_ADMIN_OPTIONS +"." + option.getName().toLowerCase())) {
+                    continue;
+                }
+            } else {
+                if (!player.hasPermission(GDPermissions.USER_CLAIM_OPTIONS +"." + option.getName().toLowerCase())) {
+                    continue;
+                }
+            }
+            final OptionData optionData = filteredContextMap.get(permissionEntry.getKey());
+            if (optionData != null) {
+                optionData.addContexts(option, permissionEntry.getValue(), type, contexts);
+            } else {
+                filteredContextMap.put(permissionEntry.getKey(), new OptionData(option, permissionEntry.getValue(), type, contexts));
+            }
+        }
+    }
+
+    private Component getClickableOptionComponent(GDPermissionUser src, GDClaim claim, Option option, OptionContextHolder optionHolder, Set<Context> contexts, MenuType displayType) {
+        final Player player = src.getOnlinePlayer();
+        final GDPlayerData playerData = src.getInternalPlayerData();
+        boolean hasEditPermission = true;
+        Component hoverEventText = TextComponent.empty();
+        final MenuType flagType = optionHolder.getType();
+        if (flagType == MenuType.DEFAULT) {
+            if (!playerData.canManageGlobalOptions) {
+                hoverEventText = MessageCache.getInstance().PERMISSION_OPTION_DEFAULTS;
+                hasEditPermission = false;
+            }
+        } else if (flagType == MenuType.OVERRIDE) {
+            if (!playerData.canManageOverrideOptions) {
+                hoverEventText = MessageCache.getInstance().PERMISSION_OPTION_OVERRIDES;
+                hasEditPermission = false;
+            }
+        } else if (flagType == MenuType.INHERIT) {
+            hoverEventText = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.OPTION_UI_INHERIT_PARENT,
+                    ImmutableMap.of("name", claim.getFriendlyNameType()));
+            hasEditPermission = false;
+        }
+        if (displayType == MenuType.CLAIM) {
+            Component denyReason = claim.allowEdit(player);
+            if (denyReason != null) {
+                hoverEventText = denyReason;
+                hasEditPermission = false;
+            } else {
+                if (option.isGlobal()) {
+                    if (!player.hasPermission(GDPermissions.MANAGE_GLOBAL_OPTIONS +"." + option.getName().toLowerCase())) {
+                        hoverEventText = MessageCache.getInstance().PERMISSION_OPTION_USE;
+                        hasEditPermission = false;
+                    }
+                } else if (((GDOption) option).isAdmin()) {
+                    if (!player.hasPermission(GDPermissions.MANAGE_ADMIN_OPTIONS +"." + option.getName().toLowerCase())) {
+                        hoverEventText = MessageCache.getInstance().PERMISSION_OPTION_USE;
+                        hasEditPermission = false;
+                    }
+                }
+                else {
+                    if (!player.hasPermission(GDPermissions.USER_CLAIM_OPTIONS +"." + option.getName().toLowerCase())) {
+                        hoverEventText = MessageCache.getInstance().PERMISSION_OPTION_USE;
+                        hasEditPermission = false;
+                    }
+                }
+            }
+        }
+
+        final boolean customContexts = UIHelper.containsCustomContext(contexts);
+        Component optionContexts = UIHelper.getFriendlyContextString(claim, contexts);
+        String currentValue = optionHolder.getValue();
+        TextColor color = optionHolder.getColor();
+        boolean isNumber = false;
+        if (option.getAllowedType().isAssignableFrom(Integer.class) || option.getAllowedType().isAssignableFrom(Double.class)) {
+            isNumber = true;
+        }
+
+        TextComponent.Builder builder = null;
+        if (isNumber && hasEditPermission) {
+            builder = TextComponent.builder()
+                    .append(getOptionText(option, contexts))
+                    .append(" ")
+                    .append(TextComponent.builder()
+                            .append(TextComponent.of("< ").decoration(TextDecoration.BOLD, true))
+                            .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(newOptionValueConsumer(src, claim, option, optionHolder, contexts, displayType, true)))))
+                    .append(currentValue.toLowerCase(), color);
+        } else {
+            builder = TextComponent.builder()
+                    .append(getOptionText(option, contexts))
+                    .append(" ")
+                    .append(TextComponent.builder()
+                            .append(currentValue.toLowerCase(), color)
+                            .hoverEvent(HoverEvent.showText(hoverEventText)));
+        }
+        if (hasEditPermission) {
+            if (!option.getAllowedType().isAssignableFrom(Integer.class) && !option.getAllowedType().isAssignableFrom(Double.class)) {
+                builder.clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(newOptionValueConsumer(src, claim, option, optionHolder, contexts, displayType, false))));
+            } else {
+                builder.append(TextComponent.builder()
+                        .append(TextComponent.of(" >").decoration(TextDecoration.BOLD, true))
+                        .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(newOptionValueConsumer(src, claim, option, optionHolder, contexts, displayType, false)))));
+            }
+
+            if (option.getAllowedType().isAssignableFrom(String.class)) {
+                builder.clickEvent(createClickEvent(player, option));
+            }
+        }
+
+        if (displayType == MenuType.DEFAULT) {
+            builder.hoverEvent(HoverEvent.showText(TextComponent.builder().append(MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.OPTION_NOT_SET, 
+                    ImmutableMap.of(
+                            "option", TextComponent.of(option.getName().toLowerCase()).color(TextColor.GREEN),
+                            "value", TextComponent.of(currentValue).color(TextColor.GOLD)))).build()));
+        }
+
+        return builder.build();
+    }
+
+    private boolean containsDefaultContext(Set<Context> contexts) {
+        for (Context context : contexts) {
+            if (context.getKey().contains("gd_claim_default")) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private ClickEvent createClickEvent(Player src, Option option) {
+        return ClickEvent.suggestCommand("/gd option " + option.getName() + " ");
+    }
+ 
+    private Consumer<CommandSource> newOptionValueConsumer(GDPermissionUser src, GDClaim claim, Option option, OptionContextHolder optionHolder, Set<Context> contexts, MenuType displayType, boolean leftArrow) {
+        final String currentValue = optionHolder.getValue();
+        return consumer -> {
+            String newValue = "";
+            if (option.getAllowedType().isAssignableFrom(Tristate.class)) {
+                Tristate value = getMenuTypeValue(TypeToken.of(Tristate.class), currentValue);
+                if (value == Tristate.TRUE) {
+                    newValue = "false";
+                } else if (value == Tristate.FALSE) {
+                    newValue = "undefined";
+                } else {
+                    newValue = "true";
+                }
+            }
+            if (option.getAllowedType().isAssignableFrom(Boolean.class)) {
+                Boolean value = getMenuTypeValue(TypeToken.of(Boolean.class), currentValue);
+                Tristate result = Tristate.UNDEFINED;
+                if (displayType == MenuType.DEFAULT || (displayType == MenuType.CLAIM && optionHolder.getType() == MenuType.DEFAULT)) {
+                    result = Tristate.fromBoolean(!value);
+                } else {
+                    // Always fall back to transient default
+                    result = Tristate.UNDEFINED;
+                }
+                newValue = result.toString().toLowerCase();
+            }
+            if (option.getAllowedType().isAssignableFrom(GameModeType.class)) {
+                GameModeType value = getMenuTypeValue(TypeToken.of(GameModeType.class), currentValue);
+                if (value == null || value == GameModeTypes.UNDEFINED) {
+                    newValue = "adventure";
+                } else if (value == GameModeTypes.ADVENTURE) {
+                    newValue = "creative";
+                } else if (value == GameModeTypes.CREATIVE) {
+                    newValue = "survival";
+                } else if (value == GameModeTypes.SURVIVAL) {
+                    newValue = "spectator";
+                } else {
+                    newValue = "undefined";
+                }
+            }
+            if (option.getAllowedType().isAssignableFrom(CreateModeType.class)) {
+                CreateModeType value = getMenuTypeValue(TypeToken.of(CreateModeType.class), currentValue);
+                if (value == null || value == CreateModeTypes.UNDEFINED) {
+                    newValue = "area";
+                } else if (value == CreateModeTypes.AREA) {
+                    newValue = "volume";
+                } else {
+                    newValue = "undefined";
+                }
+            }
+            if (option.getAllowedType().isAssignableFrom(WeatherType.class)) {
+                WeatherType value = getMenuTypeValue(TypeToken.of(WeatherType.class), currentValue);
+                if (value == null || value == WeatherTypes.UNDEFINED) {
+                    newValue = "clear";
+                } else if (value == WeatherTypes.CLEAR) {
+                    newValue = "rain";
+                } else {
+                    newValue = "undefined";
+                }
+            }
+            if (option.getAllowedType().isAssignableFrom(Integer.class)) {
+                Integer value = getMenuTypeValue(TypeToken.of(Integer.class), currentValue);
+                if (leftArrow) {
+                    if (value == null || value < 1) {
+                        TextAdapter.sendComponent(src.getOnlinePlayer(), TextComponent.of("This value is NOT defined and cannot go any lower."));
+                    } else {
+                        if ((option == Options.MIN_LEVEL || option == Options.MAX_LEVEL || option == Options.MIN_SIZE_Y || option == Options.MAX_SIZE_Y) && value == 1) {
+                            value = null;
+                        } else {
+                            value -= 1;
+                            if (value <= 0) {
+                                if (option == Options.MAX_LEVEL) {
+                                    value = 255;
+                                }
+                            }
+                        }
+                    }
+                } else {
+                    if (value == null) {
+                        value = 1;
+                    } else {
+                        if ((option == Options.MIN_SIZE_Y || option == Options.MAX_SIZE_Y) && value == 256) {
+                            value = null;
+                        } else if ((option == Options.MIN_LEVEL || option == Options.MAX_LEVEL) && value == 255) {
+                            value = null;
+                        } else {
+                            value += 1;
+                        }
+                    }
+                }
+                newValue = value == null ? "undefined" :String.valueOf(value);
+            }
+            if (option.getAllowedType().isAssignableFrom(Double.class)) {
+                Double value = getMenuTypeValue(TypeToken.of(Double.class), currentValue);
+                if (leftArrow) {
+                    if (value == null || value < 1) {
+                        TextAdapter.sendComponent(src.getOnlinePlayer(), TextComponent.of("This value is NOT defined and cannot go any lower."));
+                    } else {
+                        value -= 1;
+                        if (option == Options.ABANDON_RETURN_RATIO && value <= 0) {
+                            value = null;
+                        } else {
+                            if (value < 0) {
+                                value = 0.0;
+                            }
+                        }
+                    }
+                } else {
+                    if (value == null) {
+                        value = 1.0;
+                    } else {
+                        value += 1;
+                    }
+                }
+                newValue = value == null ? "undefined" :String.valueOf(value);
+            }
+
+            Set<Context> newContexts = new HashSet<>();
+            final boolean isCustom = UIHelper.containsCustomContext(contexts);
+            if (!isCustom && displayType == MenuType.CLAIM) {
+                newContexts.add(claim.getContext());
+            } else {
+                newContexts.addAll(contexts);
+            }
+
+            // Remove server context
+            final Iterator<Context> iterator = newContexts.iterator();
+            while (iterator.hasNext()) {
+                final Context context = iterator.next();
+                if (context.getKey().equals("server")) {
+                    iterator.remove();
+                }
+            }
+
+            PermissionUtil.getInstance().setOptionValue(this.subject, option.getPermission(), newValue, newContexts);
+            showOptionPermissions(src, claim, displayType);
+        };
+    }
+
+    private Consumer<CommandSource> adjustNumberConsumer(GDPermissionUser src, GDClaim claim, Option option, String currentValue, MenuType menuType, Set<Context> contexts) {
+        return consumer -> {
+            String newValue = "";
+            final Set<Context> filteredContexts = applySelectedTypeContext(claim, menuType, contexts);
+            if (option.getAllowedType().isAssignableFrom(Boolean.class)) {
+                Boolean value = getMenuTypeValue(TypeToken.of(Boolean.class), currentValue);
+                if (value == null) {
+                    newValue = "true";
+                } else if (value) {
+                    newValue = "false";
+                } else {
+                    newValue = "undefined";
+                }
+                PermissionUtil.getInstance().setOptionValue(this.subject, option.getPermission(), newValue, filteredContexts);
+            }
+            if (option.getAllowedType().isAssignableFrom(Integer.class)) {
+                newValue = getMenuTypeValue(TypeToken.of(String.class), currentValue);
+                if (newValue == null) {
+                    newValue = "undefined";
+                }
+                PermissionUtil.getInstance().setOptionValue(this.subject, option.getPermission(), newValue, filteredContexts);
+            }
+            showOptionPermissions(src, claim, menuType);
+        };
+    }
+
+    private Set<Context> applySelectedTypeContext(Claim claim, MenuType menuType, Set<Context> contexts) {
+        Set<Context> filteredContexts = new HashSet<>(contexts);
+        for (Context context : contexts) {
+            if (context.getKey().contains("gd_claim")) {
+                filteredContexts.remove(context);
+            }
+        }
+        if (menuType == MenuType.DEFAULT) {
+            filteredContexts.add(ClaimContexts.GLOBAL_DEFAULT_CONTEXT);
+        }
+        if (menuType == MenuType.CLAIM) {
+            filteredContexts.add(claim.getContext());
+        }
+        if (menuType == MenuType.OVERRIDE) {
+            filteredContexts.add(ClaimContexts.GLOBAL_OVERRIDE_CONTEXT);
+        }
+        return filteredContexts;
+    }
+
+    private boolean contextsMatch(Set<Context> contexts, Set<Context> newContexts) {
+        // first filter out gd claim contexts
+        final Set<Context> filteredContexts = new HashSet<>();
+        final Set<Context> filteredNewContexts = new HashSet<>();
+        for (Context context : contexts) {
+            if (context.getKey().contains("gd_claim")) {
+                continue;
+            }
+            filteredContexts.add(context);
+        }
+        for (Context context : newContexts) {
+            if (context.getKey().contains("gd_claim")) {
+                continue;
+            }
+            filteredNewContexts.add(context);
+        }
+        return Objects.hash(filteredContexts) == Objects.hash(filteredNewContexts);
+    }
+
+    private static Set<Context> createClaimContextSet(GDClaim claim, Set<Context> contexts) {
+        Set<Context> claimContexts = new HashSet<>();
+        claimContexts.add(claim.getContext());
+        for (Context context : contexts) {
+            if (context.getKey().contains("world") || context.getKey().contains("gd_claim")) {
+                continue;
+            }
+            claimContexts.add(context);
+        }
+        return claimContexts;
+    }
+
+    private static Component getOptionText(Option option, Set<Context> contexts) {
+        boolean customContext = UIHelper.containsCustomContext(contexts);
+
+        final Component optionText = TextComponent.builder().color(customContext ? TextColor.YELLOW : TextColor.GREEN).append(option.getName() + " ")
+                .hoverEvent(HoverEvent.showText(TextComponent.builder()
+                        .append(option.getDescription())
+                        .build())).build();
+        return optionText;
+    }
+
+    private static <T> T getMenuTypeValue(TypeToken<T> type, String value) {
+        if (type.getRawType().isAssignableFrom(Double.class)) {
+            Double newValue = null;
+            try {
+                newValue = Double.valueOf(value);
+            } catch (NumberFormatException e) {
+                return null;
+            }
+            return (T) newValue;
+        }
+        if (type.getRawType().isAssignableFrom(Integer.class)) {
+            Integer newValue = null;
+            try {
+                newValue = Integer.valueOf(value);
+            } catch (NumberFormatException e) {
+                return null;
+            }
+            return (T) newValue;
+        }
+        if (type.getRawType().isAssignableFrom(String.class)) {
+            return (T) value;
+        }
+        if (type.getRawType().isAssignableFrom(Boolean.class)) {
+            if (value.equalsIgnoreCase("false")) {
+                return (T) Boolean.valueOf(value);
+            } else if (value.equalsIgnoreCase("true")) {
+                return (T) Boolean.valueOf(value);
+            }
+        }
+        if (type.getRawType().isAssignableFrom(Tristate.class)) {
+            if (value.equalsIgnoreCase("undefined")) {
+                return (T) Tristate.UNDEFINED;
+            }
+            if (value.equalsIgnoreCase("true")) {
+                return (T) Tristate.TRUE;
+            }
+            if (value.equalsIgnoreCase("false")) {
+                return (T) Tristate.FALSE;
+            }
+            int permValue = 0;
+            try {
+                permValue = Integer.parseInt(value);
+            } catch (NumberFormatException e) {
+                return (T) Tristate.UNDEFINED;
+            }
+            if (permValue == 0) {
+                return (T) Tristate.UNDEFINED;
+            }
+            return (T) (permValue == 1 ? Tristate.TRUE : Tristate.FALSE);
+        }
+        if (type.getRawType().isAssignableFrom(GameModeType.class)) {
+            if (value.equalsIgnoreCase("undefined")) {
+                return (T) GameModeTypes.UNDEFINED;
+            }
+            if (value.equalsIgnoreCase("adventure")) {
+                return (T) GameModeTypes.ADVENTURE;
+            }
+            if (value.equalsIgnoreCase("creative")) {
+                return (T) GameModeTypes.CREATIVE;
+            }
+            if (value.equalsIgnoreCase("survival")) {
+                return (T) GameModeTypes.SURVIVAL;
+            }
+            if (value.equalsIgnoreCase("spectator")) {
+                return (T) GameModeTypes.SPECTATOR;
+            }
+        }
+        if (type.getRawType().isAssignableFrom(WeatherType.class)) {
+            if (value.equalsIgnoreCase("undefined")) {
+                return (T) WeatherTypes.UNDEFINED;
+            }
+            if (value.equalsIgnoreCase("clear")) {
+                return (T) WeatherTypes.CLEAR;
+            }
+            if (value.equalsIgnoreCase("rain")) {
+                return (T) WeatherTypes.RAIN;
+            }
+        }
+        if (type.getRawType().isAssignableFrom(CreateModeType.class)) {
+            if (value.equalsIgnoreCase("undefined")) {
+                return (T) CreateModeTypes.UNDEFINED;
+            }
+            if (value.equalsIgnoreCase("area")) {
+                return (T) CreateModeTypes.AREA;
+            }
+            if (value.equalsIgnoreCase("volume")) {
+                return (T) CreateModeTypes.VOLUME;
+            }
+        }
+        return null;
+    }
+
+    private Consumer<CommandSource> createClaimOptionConsumer(GDPermissionUser src, GDClaim claim, MenuType optionType) {
+        return consumer -> {
+            showOptionPermissions(src, claim, optionType);
+        };
+    }
+}
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/command/ClaimSubjectType.java b/sponge/src/main/java/com/griefdefender/command/ClaimSubjectType.java
new file mode 100644
index 0000000..af6eb97
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/ClaimSubjectType.java
@@ -0,0 +1,42 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+public enum ClaimSubjectType {
+
+    GLOBAL("Global"),
+    GROUP("Group"),
+    PLAYER("Player");
+
+    private String friendlyName;
+
+    private ClaimSubjectType(String name) {
+        this.friendlyName = name;
+    }
+
+    public String getFriendlyName() {
+        return this.friendlyName;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandAdjustBonusClaimBlocks.java b/sponge/src/main/java/com/griefdefender/command/CommandAdjustBonusClaimBlocks.java
new file mode 100644
index 0000000..719937e
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandAdjustBonusClaimBlocks.java
@@ -0,0 +1,90 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandCompletion;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Optional;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.permission.GDPermissions;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import net.kyori.text.format.TextColor;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.command.CommandSource;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.entity.living.player.User;
+import org.spongepowered.api.world.storage.WorldProperties;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_SET_ACCRUED_CLAIM_BLOCKS)
+public class CommandAdjustBonusClaimBlocks extends BaseCommand {
+
+    @CommandCompletion("@gdplayers @gddummy")
+    @CommandAlias("acb|adjustclaimblocks")
+    @Description("Updates a player's accrued claim block total")
+    @Syntax("<player> <amount>")
+    @Subcommand("player adjustbonusblocks")
+    public void execute(CommandSource src, User user, int amount, @Optional String world) {
+        WorldProperties worldProperties = world == null ? null : Sponge.getServer().getWorldProperties(world).orElse(null);
+        if (worldProperties == null) {
+            if (src instanceof Player) {
+                worldProperties = ((Player) src).getWorld().getProperties();
+            } else {
+                worldProperties = Sponge.getServer().getDefaultWorld().get();
+            }
+        }
+        if (worldProperties == null || !GriefDefenderPlugin.getInstance().claimsEnabledForWorld(worldProperties.getUniqueId())) {
+            GriefDefenderPlugin.sendMessage(src, MessageCache.getInstance().CLAIM_DISABLED_WORLD);
+            return;
+        }
+
+        // parse the adjustment amount
+        int adjustment = amount;
+        //User user = args.<User>getOne("user").get();
+
+        // give blocks to player
+        GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(worldProperties.getUniqueId(), user.getUniqueId());
+        playerData.setBonusClaimBlocks(playerData.getBonusClaimBlocks() + adjustment);
+        playerData.getStorageData().save();
+        final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.ADJUST_BONUS_BLOCKS_SUCCESS, ImmutableMap.of(
+                "player", user.getName(),
+                "amount", adjustment,
+                "total", playerData.getBonusClaimBlocks()));
+        TextAdapter.sendComponent(src, message);
+        GriefDefenderPlugin.getInstance().getLogger().info(
+                src.getName() + " adjusted " + user.getName() + "'s bonus claim blocks by " + adjustment + ".");
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandCallback.java b/sponge/src/main/java/com/griefdefender/command/CommandCallback.java
new file mode 100644
index 0000000..715d158
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandCallback.java
@@ -0,0 +1,47 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.Description;
+import com.griefdefender.text.action.GDCallbackHolder;
+import org.spongepowered.api.command.CommandSource;
+
+import java.util.UUID;
+import java.util.function.Consumer;
+
+public class CommandCallback extends BaseCommand {
+
+    @CommandAlias("gd:callback")
+    @Description("Execute a callback registered as part of a Text object. Primarily for internal use")
+    public void execute(CommandSource src, String[] args) {
+        final UUID callbackId = UUID.fromString(args[0]);
+        Consumer<CommandSource> callback = GDCallbackHolder.getInstance().getCallbackForUUID(callbackId).orElse(null);
+        if (callback != null) {
+            callback.accept(src);
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimAbandon.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimAbandon.java
new file mode 100644
index 0000000..bd99737
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimAbandon.java
@@ -0,0 +1,218 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.reflect.TypeToken;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.GriefDefender;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.ClaimResult;
+import com.griefdefender.api.claim.TrustTypes;
+import com.griefdefender.api.permission.option.Options;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.claim.GDClaimManager;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.event.GDCauseStackManager;
+import com.griefdefender.event.GDRemoveClaimEvent;
+import com.griefdefender.permission.GDPermissionManager;
+import com.griefdefender.permission.GDPermissionUser;
+import com.griefdefender.permission.GDPermissions;
+import com.griefdefender.text.action.GDCallbackHolder;
+import com.griefdefender.util.PermissionUtil;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import net.kyori.text.event.ClickEvent;
+import net.kyori.text.event.HoverEvent;
+import net.kyori.text.format.TextColor;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.command.CommandSource;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.service.economy.Currency;
+import org.spongepowered.api.service.economy.account.Account;
+import org.spongepowered.api.service.economy.transaction.ResultType;
+import org.spongepowered.api.service.economy.transaction.TransactionResult;
+
+import java.math.BigDecimal;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
+import java.util.function.Consumer;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_ABANDON_BASIC)
+public class CommandClaimAbandon extends BaseCommand {
+
+    protected boolean abandonTopClaim = false;
+
+    @CommandAlias("abandon|abandonclaim")
+    @Description("Abandons a claim")
+    @Subcommand("abandon claim")
+    public void execute(Player player) {
+        final GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAt(player.getLocation());
+        final UUID ownerId = claim.getOwnerUniqueId();
+        final GDPermissionUser user = PermissionHolderCache.getInstance().getOrCreateUser(player);
+        final GDPlayerData playerData = user.getInternalPlayerData();
+
+        final boolean isAdmin = playerData.canIgnoreClaim(claim);
+        final boolean isTown = claim.isTown();
+        if (claim.isWilderness()) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().ABANDON_CLAIM_MISSING);
+            return;
+        } else if (!isAdmin && !player.getUniqueId().equals(ownerId) && claim.isUserTrusted(player, TrustTypes.MANAGER)) {
+            if (claim.parent == null) {
+                // Managers can only abandon child claims
+                GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().CLAIM_NOT_YOURS);
+                return;
+            }
+        } else if (!isAdmin && (claim.allowEdit(player) != null || (!claim.isAdminClaim() && !player.getUniqueId().equals(ownerId)))) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().CLAIM_NOT_YOURS);
+            return;
+        } else {
+            if (this.abandonTopClaim && claim.children.size() > 0) {
+                if (claim.isTown() || claim.isAdminClaim()) {
+                    Set<Claim> invalidClaims = new HashSet<>();
+                    for (Claim child : claim.getChildren(true)) {
+                        if (child.getOwnerUniqueId() == null || !child.getOwnerUniqueId().equals(ownerId)) {
+                            //return CommandResult.empty();
+                            invalidClaims.add(child);
+                        }
+                    }
+    
+                    if (!invalidClaims.isEmpty()) {
+                        GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().ABANDON_TOWN_CHILDREN);
+                        CommandHelper.showClaims(player, invalidClaims, 0, true);
+                        return;
+                    }
+                }
+            }
+        }
+
+        final int abandonDelay = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), user, Options.ABANDON_DELAY, claim);
+        if (abandonDelay > 0) {
+            final Instant localNow = Instant.now();
+            final Instant dateCreated = claim.getInternalClaimData().getDateCreated();
+            final Instant delayExpires = dateCreated.plus(Duration.ofDays(abandonDelay));
+            final boolean delayActive = !delayExpires.isBefore(localNow);
+            if (delayActive) {
+                TextAdapter.sendComponent(player, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.ABANDON_CLAIM_DELAY_WARNING, 
+                        ImmutableMap.of("date", Date.from(delayExpires))));
+                return;
+            }
+        }
+        final boolean autoSchematicRestore = GriefDefenderPlugin.getActiveConfig(player.getWorld().getProperties()).getConfig().claim.claimAutoSchematicRestore;
+        final Component confirmationText = TextComponent.builder()
+                .append(autoSchematicRestore ? MessageCache.getInstance().SCHEMATIC_ABANDON_RESTORE_WARNING : MessageCache.getInstance().ABANDON_WARNING)
+                    .append(TextComponent.builder()
+                    .append("\n[")
+                    .append("Confirm", TextColor.GREEN)
+                    .append("]\n")
+                    .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createConfirmationConsumer(player, playerData, claim, this.abandonTopClaim))))
+                    .hoverEvent(HoverEvent.showText(MessageCache.getInstance().UI_CLICK_CONFIRM)).build())
+                .build();
+        TextAdapter.sendComponent(player, confirmationText);
+    }
+
+    private static Consumer<CommandSource> createConfirmationConsumer(Player player, GDPlayerData playerData, GDClaim claim, boolean abandonTopClaim) {
+        return confirm -> {
+
+            GDCauseStackManager.getInstance().pushCause(player);
+            GDRemoveClaimEvent.Abandon event = new GDRemoveClaimEvent.Abandon(claim);
+            GriefDefender.getEventManager().post(event);
+            GDCauseStackManager.getInstance().popCause();
+            if (event.cancelled()) {
+                TextAdapter.sendComponent(player, event.getMessage().orElse(MessageCache.getInstance().PLUGIN_EVENT_CANCEL));
+                return;
+            }
+
+            if (!claim.isSubdivision() && !claim.isAdminClaim()) {
+                if (GriefDefenderPlugin.getGlobalConfig().getConfig().economy.economyMode) {
+                    final Account playerAccount = GriefDefenderPlugin.getInstance().economyService.get().getOrCreateAccount(playerData.playerID).orElse(null);
+                    if (playerAccount == null) {
+                        return;
+                    }
+                }
+            }
+
+            GDClaimManager claimManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(player.getWorld().getUniqueId());
+            playerData.useRestoreSchematic = event.isRestoring();
+            final ClaimResult claimResult = claimManager.deleteClaimInternal(claim, abandonTopClaim);
+            playerData.useRestoreSchematic = false;
+            if (!claimResult.successful()) {
+                TextAdapter.sendComponent(player, event.getMessage().orElse(GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.ABANDON_FAILED,
+                        ImmutableMap.of("result", claimResult.getMessage().orElse(TextComponent.of(claimResult.getResultType().toString()))))));
+                return;
+            }
+
+            // remove all context permissions
+            PermissionUtil.getInstance().clearPermissions(claim);
+            playerData.revertActiveVisual(player);
+
+            if (claim.isTown()) {
+                playerData.inTown = false;
+                playerData.townChat = false;
+            }
+
+            if (!claim.isSubdivision() && !claim.isAdminClaim()) {
+                final double abandonReturnRatio = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Double.class), player, Options.ABANDON_RETURN_RATIO, claim);
+                if (GriefDefenderPlugin.getInstance().isEconomyModeEnabled()) {
+                    final Account playerAccount = GriefDefenderPlugin.getInstance().economyService.get().getOrCreateAccount(playerData.playerID).orElse(null);
+                    if (playerAccount == null) {
+                        return;
+                    }
+
+                    final Currency defaultCurrency = GriefDefenderPlugin.getInstance().economyService.get().getDefaultCurrency();
+                    final double requiredClaimBlocks = claim.getClaimBlocks() * abandonReturnRatio;
+                    final double refund = requiredClaimBlocks * claim.getOwnerEconomyBlockCost();
+                    final TransactionResult result = playerAccount.deposit(defaultCurrency, BigDecimal.valueOf(refund), Sponge.getCauseStackManager().getCurrentCause());
+                    if (result.getResult() == ResultType.SUCCESS) {
+                        final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.ECONOMY_CLAIM_ABANDON_SUCCESS, ImmutableMap.of(
+                                "amount", refund));
+                        GriefDefenderPlugin.sendMessage(player, message);
+                    }
+                } else {
+                    int newAccruedClaimCount = playerData.getAccruedClaimBlocks() - ((int) Math.ceil(claim.getClaimBlocks() * (1 - abandonReturnRatio)));
+                    playerData.setAccruedClaimBlocks(newAccruedClaimCount);
+                    int remainingBlocks = playerData.getRemainingClaimBlocks();
+                    final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.ABANDON_SUCCESS, ImmutableMap.of(
+                            "amount", remainingBlocks));
+                    GriefDefenderPlugin.sendMessage(player, message);
+                }
+            }
+        };
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimAbandonAll.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimAbandonAll.java
new file mode 100644
index 0000000..2f7d1d6
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimAbandonAll.java
@@ -0,0 +1,188 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.reflect.TypeToken;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.GriefDefender;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.permission.option.Options;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.event.GDCauseStackManager;
+import com.griefdefender.event.GDRemoveClaimEvent;
+import com.griefdefender.permission.GDPermissionManager;
+import com.griefdefender.permission.GDPermissionUser;
+import com.griefdefender.permission.GDPermissions;
+import com.griefdefender.text.action.GDCallbackHolder;
+import com.griefdefender.util.PermissionUtil;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import net.kyori.text.event.ClickEvent;
+import net.kyori.text.event.HoverEvent;
+import net.kyori.text.format.TextColor;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.command.CommandSource;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.service.economy.Currency;
+import org.spongepowered.api.service.economy.account.Account;
+import org.spongepowered.api.service.economy.transaction.ResultType;
+import org.spongepowered.api.service.economy.transaction.TransactionResult;
+
+import java.math.BigDecimal;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.function.Consumer;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_ABANDON_ALL_CLAIMS)
+public class CommandClaimAbandonAll extends BaseCommand {
+
+    @CommandAlias("abandonall|abandonallclaims")
+    @Description("Abandons ALL your claims")
+    @Subcommand("abandon all")
+    public void execute(Player player) {
+        final GDPermissionUser user = PermissionHolderCache.getInstance().getOrCreateUser(player);
+        int originalClaimCount = user.getInternalPlayerData().getInternalClaims().size();
+
+        if (originalClaimCount == 0) {
+            try {
+                throw new CommandException(MessageCache.getInstance().CLAIM_NO_CLAIMS);
+            } catch (CommandException e) {
+                TextAdapter.sendComponent(player, e.getText());
+                return;
+            }
+        }
+
+        final boolean autoSchematicRestore = GriefDefenderPlugin.getActiveConfig(player.getWorld().getUniqueId()).getConfig().claim.claimAutoSchematicRestore;
+        final Component confirmationText = TextComponent.builder()
+                .append(autoSchematicRestore ? MessageCache.getInstance().SCHEMATIC_ABANDON_ALL_RESTORE_WARNING : MessageCache.getInstance().ABANDON_ALL_WARNING)
+                .append(TextComponent.builder()
+                    .append("\n[")
+                    .append("Confirm", TextColor.GREEN)
+                    .append("]\n")
+                    .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createConfirmationConsumer(user))))
+                    .hoverEvent(HoverEvent.showText(MessageCache.getInstance().UI_CLICK_CONFIRM)).build())
+                .build();
+        TextAdapter.sendComponent(player, confirmationText);
+    }
+
+    private static Consumer<CommandSource> createConfirmationConsumer(GDPermissionUser user) {
+        return confirm -> {
+            Set<Claim> allowedClaims = new HashSet<>();
+            Set<Claim> delayedClaims = new HashSet<>();
+            final int abandonDelay = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), user, Options.ABANDON_DELAY);
+            final Player player = user.getOnlinePlayer();
+            final GDPlayerData playerData = user.getInternalPlayerData();
+            for (Claim claim : playerData.getInternalClaims()) {
+                if (abandonDelay > 0) {
+                    final Instant localNow = Instant.now();
+                    final Instant dateCreated = ((GDClaim) claim).getInternalClaimData().getDateCreated();
+                    final Instant dateExpires = dateCreated.plus(Duration.ofDays(abandonDelay));
+                    final boolean delayActive = !dateExpires.isBefore(localNow);
+                    if (delayActive) {
+                        delayedClaims.add(claim);
+                    } else {
+                        allowedClaims.add(claim);
+                    }
+                } else {
+                    allowedClaims.add(claim);
+                }
+            }
+
+            if (!allowedClaims.isEmpty()) {
+                GDCauseStackManager.getInstance().pushCause(user);
+                GDRemoveClaimEvent.Abandon event = new GDRemoveClaimEvent.Abandon(ImmutableList.copyOf(allowedClaims));
+                GriefDefender.getEventManager().post(event);
+                GDCauseStackManager.getInstance().popCause();
+                if (event.cancelled()) {
+                    TextAdapter.sendComponent(user.getOnlinePlayer(), event.getMessage().orElse(MessageCache.getInstance().PLUGIN_EVENT_CANCEL).color(TextColor.RED));
+                    return;
+                }
+
+                double refund = 0;
+                // adjust claim blocks
+                for (Claim claim : allowedClaims) {
+                    // remove all context permissions
+                    PermissionUtil.getInstance().clearPermissions((GDClaim) claim);
+                    if (claim.isSubdivision() || claim.isAdminClaim() || claim.isWilderness()) {
+                        continue;
+                    }
+                    final double abandonReturnRatio = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Double.class), user, Options.ABANDON_RETURN_RATIO, claim);
+                    if (GriefDefenderPlugin.getInstance().isEconomyModeEnabled()) {
+                        refund += claim.getClaimBlocks() * abandonReturnRatio;
+                    } else {
+                        playerData.setAccruedClaimBlocks(playerData.getAccruedClaimBlocks() - ((int) Math.ceil(claim.getClaimBlocks() * (1 - abandonReturnRatio))));
+                    }
+                }
+    
+                playerData.useRestoreSchematic = event.isRestoring();
+                GriefDefenderPlugin.getInstance().dataStore.abandonClaimsForPlayer(user, allowedClaims);
+                playerData.useRestoreSchematic = false;
+    
+                if (GriefDefenderPlugin.getInstance().isEconomyModeEnabled()) {
+                    final Account playerAccount = GriefDefenderPlugin.getInstance().economyService.get().getOrCreateAccount(playerData.playerID).orElse(null);
+                    if (playerAccount == null) {
+                        return;
+                    }
+
+                    final Currency defaultCurrency = GriefDefenderPlugin.getInstance().economyService.get().getDefaultCurrency();
+                    final TransactionResult result = playerAccount.deposit(defaultCurrency, BigDecimal.valueOf(refund), Sponge.getCauseStackManager().getCurrentCause());
+                    if (result.getResult() == ResultType.SUCCESS) {
+                        final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.ECONOMY_CLAIM_ABANDON_SUCCESS, ImmutableMap.of(
+                                "amount", TextComponent.of(String.valueOf(refund))));
+                        GriefDefenderPlugin.sendMessage(player, message);
+                    }
+                 } else {
+                    int remainingBlocks = playerData.getRemainingClaimBlocks();
+                    final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.ABANDON_SUCCESS, ImmutableMap.of(
+                                "amount", remainingBlocks));
+                    TextAdapter.sendComponent(player, message);
+                 }
+
+                playerData.revertActiveVisual(player);
+            }
+
+            if (!delayedClaims.isEmpty()) {
+                TextAdapter.sendComponent(player, MessageCache.getInstance().ABANDON_ALL_DELAY_WARNING);
+                CommandHelper.showClaims(player, delayedClaims, 0, true);
+            }
+        };
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimAbandonTop.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimAbandonTop.java
new file mode 100644
index 0000000..3ea5cc1
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimAbandonTop.java
@@ -0,0 +1,48 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import com.griefdefender.permission.GDPermissions;
+import org.spongepowered.api.entity.living.player.Player;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_ABANDON_TOP_LEVEL_CLAIM)
+public class CommandClaimAbandonTop extends CommandClaimAbandon {
+
+    public CommandClaimAbandonTop() {
+        this.abandonTopClaim = true;
+    }
+
+    @CommandAlias("abandontop")
+    @Description("Abandons top level claim")
+    @Subcommand("abandon top")
+    public void execute(Player player) {
+        super.execute(player);
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimAdmin.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimAdmin.java
new file mode 100644
index 0000000..c913dc2
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimAdmin.java
@@ -0,0 +1,52 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.claim.ShovelTypes;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.permission.GDPermissions;
+import org.spongepowered.api.entity.living.player.Player;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_ADMIN_CLAIMS)
+public class CommandClaimAdmin extends BaseCommand {
+
+    @CommandAlias("modeadmin|adminclaims|ac")
+    @Description("Switches the shovel tool to administrative claims mode")
+    @Subcommand("mode admin")
+    public void execute(Player player) {
+
+        final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        playerData.shovelMode = ShovelTypes.ADMIN;
+        GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().MODE_ADMIN);
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimBan.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimBan.java
new file mode 100644
index 0000000..74a9fbd
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimBan.java
@@ -0,0 +1,129 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandCompletion;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Optional;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import net.kyori.text.format.TextColor;
+import net.kyori.text.serializer.legacy.LegacyComponentSerializer;
+import org.spongepowered.api.data.key.Keys;
+import org.spongepowered.api.data.type.HandTypes;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.item.inventory.ItemStack;
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.internal.registry.BlockTypeRegistryModule;
+import com.griefdefender.internal.registry.EntityTypeRegistryModule;
+import com.griefdefender.internal.registry.ItemTypeRegistryModule;
+import com.griefdefender.permission.GDPermissions;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_CLAIM_BAN)
+public class CommandClaimBan extends BaseCommand {
+
+    @CommandCompletion("@gdbantypes @gdmcids @gddummy")
+    @CommandAlias("claimban")
+    @Description("Bans target id from all usage.")
+    @Syntax("hand | <type> <target> [<message>]")
+    @Subcommand("ban")
+    public void execute(Player player, String type, @Optional String id, @Optional String message) {
+        Component component = null;
+        if (type.equalsIgnoreCase("block")) {
+            if (!BlockTypeRegistryModule.getInstance().getById(id).isPresent()) {
+                TextAdapter.sendComponent(player, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.REGISTRY_BLOCK_NOT_FOUND,
+                        ImmutableMap.of("id", TextComponent.of(id, TextColor.LIGHT_PURPLE))));
+                return;
+            }
+            if (message == null) {
+                component = TextComponent.empty();
+            } else {
+                component = LegacyComponentSerializer.legacy().deserialize(message, '&');
+            }
+            GriefDefenderPlugin.getGlobalConfig().getConfig().bans.addBlockBan(id, component);
+            GriefDefenderPlugin.getGlobalConfig().save();
+            TextAdapter.sendComponent(player, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.COMMAND_CLAIMBAN_SUCCESS_BLOCK,
+                    ImmutableMap.of("id", TextComponent.of(id, TextColor.LIGHT_PURPLE))));
+        } else if (type.equalsIgnoreCase("entity")) {
+            if (!EntityTypeRegistryModule.getInstance().getById(id).isPresent()) {
+                TextAdapter.sendComponent(player, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.REGISTRY_ENTITY_NOT_FOUND,
+                        ImmutableMap.of("id", TextComponent.of(id, TextColor.LIGHT_PURPLE))));
+                return;
+            }
+            if (message == null) {
+                component = TextComponent.empty();
+            } else {
+                component = LegacyComponentSerializer.legacy().deserialize(message, '&');
+            }
+            GriefDefenderPlugin.getGlobalConfig().getConfig().bans.addEntityBan(id, component);
+            GriefDefenderPlugin.getGlobalConfig().save();
+            TextAdapter.sendComponent(player, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.COMMAND_CLAIMBAN_SUCCESS_ENTITY,
+                    ImmutableMap.of("id", TextComponent.of(id, TextColor.LIGHT_PURPLE))));
+        } else if (type.equalsIgnoreCase("item")) {
+            if (!ItemTypeRegistryModule.getInstance().getById(id).isPresent()) {
+                TextAdapter.sendComponent(player, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.REGISTRY_ITEM_NOT_FOUND,
+                        ImmutableMap.of("id", TextComponent.of(id, TextColor.LIGHT_PURPLE))));
+                return;
+            }
+            if (message == null) {
+                component = TextComponent.empty();
+            } else {
+                component = LegacyComponentSerializer.legacy().deserialize(message, '&');
+            }
+            GriefDefenderPlugin.getGlobalConfig().getConfig().bans.addItemBan(id, component);
+            GriefDefenderPlugin.getGlobalConfig().save();
+            TextAdapter.sendComponent(player, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.COMMAND_CLAIMBAN_SUCCESS_ITEM,
+                    ImmutableMap.of("id", TextComponent.of(id, TextColor.LIGHT_PURPLE))));
+        } else if (type.equalsIgnoreCase("hand")) {
+            final ItemStack itemInHand = player.getItemInHand(HandTypes.MAIN_HAND).orElse(null);
+            if (itemInHand == null) {
+                return;
+            }
+            final String handItemId = itemInHand.getType().getId();
+            if (message == null) {
+                component = TextComponent.empty();
+            } else {
+                component = LegacyComponentSerializer.legacy().deserialize(message, '&');
+            }
+            GriefDefenderPlugin.getGlobalConfig().getConfig().bans.addItemBan(handItemId, component);
+            GriefDefenderPlugin.getGlobalConfig().save();
+            TextAdapter.sendComponent(player, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.COMMAND_CLAIMBAN_SUCCESS_ITEM,
+                    ImmutableMap.of("id", TextComponent.of(handItemId, TextColor.LIGHT_PURPLE))));
+        }
+        if (component == null) {
+            TextAdapter.sendComponent(player, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.COMMAND_INVALID_TYPE,
+                    ImmutableMap.of("type", type)));
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimBank.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimBank.java
new file mode 100644
index 0000000..c5f16cb
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimBank.java
@@ -0,0 +1,76 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Optional;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.permission.GDPermissions;
+import org.spongepowered.api.command.CommandException;
+import org.spongepowered.api.entity.living.player.Player;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_CLAIM_BANK)
+public class CommandClaimBank extends BaseCommand {
+
+    protected boolean townOnly = false;
+
+    @CommandAlias("claimbank")
+    @Description("Used for claim bank queries")
+    @Syntax("<withdraw|deposit> <amount>")
+    @Subcommand("claim bank")
+    public void execute(Player player, @Optional String[] args) throws CommandException {
+        if (!GriefDefenderPlugin.getActiveConfig(player.getWorld().getProperties()).getConfig().claim.bankTaxSystem) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().BANK_TAX_SYSTEM_DISABLED);
+            return;
+        }
+        GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAt(player.getLocation());
+        if (this.townOnly) {
+            if (!claim.isInTown()) {
+                GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().TOWN_NOT_IN);
+                return;
+            }
+            claim = claim.getTownClaim();
+        } else {
+            if (claim.isSubdivision() || claim.isAdminClaim()) {
+                return;
+            }
+        }
+
+        if (args.length == 0 || args.length < 2) {
+            CommandHelper.displayClaimBankInfo(player, claim);
+            return;
+        }
+
+        CommandHelper.handleBankTransaction(player, args, claim);
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimBasic.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimBasic.java
new file mode 100644
index 0000000..bf9191f
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimBasic.java
@@ -0,0 +1,52 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.claim.ShovelTypes;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.permission.GDPermissions;
+import org.spongepowered.api.entity.living.player.Player;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_BASIC_MODE)
+public class CommandClaimBasic extends BaseCommand {
+
+    @CommandAlias("modebasic|basicclaims|bc")
+    @Description("Switches the shovel tool back to basic claims mode")
+    @Subcommand("mode basic")
+    public void execute(Player player) {
+        final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        playerData.shovelMode = ShovelTypes.BASIC;
+        playerData.claimSubdividing = null;
+        GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().MODE_BASIC);
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimBuy.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimBuy.java
new file mode 100644
index 0000000..c57de0f
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimBuy.java
@@ -0,0 +1,97 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.claim.GDClaimManager;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.internal.pagination.PaginationList;
+import com.griefdefender.permission.GDPermissions;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.format.TextDecoration;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.service.economy.account.Account;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_CLAIM_BUY)
+public class CommandClaimBuy extends BaseCommand {
+
+    @CommandAlias("claimbuy")
+    @Description("List all claims available for purchase.\nNote: Requires economy plugin.")
+    @Subcommand("buy claim")
+    public void execute(Player player) {
+        if (!GriefDefenderPlugin.getInstance().economyService.isPresent()) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().ECONOMY_NOT_INSTALLED);
+            return;
+        }
+
+        Account playerAccount = GriefDefenderPlugin.getInstance().economyService.get().getOrCreateAccount(player.getUniqueId()).orElse(null);
+        if (playerAccount == null) {
+            final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.ECONOMY_PLAYER_NOT_FOUND, ImmutableMap.of(
+                    "player", player.getName()));
+            GriefDefenderPlugin.sendMessage(player, message);
+            return;
+        }
+
+        Set<Claim> claimsForSale = new HashSet<>();
+        GDClaimManager claimManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(player.getWorld().getUniqueId());
+        for (Claim worldClaim : claimManager.getWorldClaims()) {
+            if (worldClaim.isWilderness()) {
+                continue;
+            }
+            if (!worldClaim.isAdminClaim() && worldClaim.getEconomyData().isForSale() && worldClaim.getEconomyData().getSalePrice() > -1) {
+                claimsForSale.add(worldClaim);
+            }
+            for (Claim child : worldClaim.getChildren(true)) {
+                if (child.isAdminClaim()) {
+                    continue;
+                }
+                if (child.getEconomyData().isForSale() && child.getEconomyData().getSalePrice() > -1) {
+                    claimsForSale.add(child);
+                }
+            }
+        }
+
+        List<Component> claimsTextList = CommandHelper.generateClaimTextListCommand(new ArrayList<Component>(), claimsForSale, player.getWorld().getName(), null, player, CommandHelper.createCommandConsumer(player, "claimbuy", ""), false);
+        PaginationList.Builder paginationBuilder = PaginationList.builder()
+                .title(MessageCache.getInstance().COMMAND_CLAIMBUY_TITLE).padding(TextComponent.of(" ").decoration(TextDecoration.STRIKETHROUGH, true)).contents(claimsTextList);
+        paginationBuilder.sendTo(player);
+        return;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimBuyBlocks.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimBuyBlocks.java
new file mode 100644
index 0000000..1782354
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimBuyBlocks.java
@@ -0,0 +1,143 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Optional;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.reflect.TypeToken;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.permission.option.Options;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.permission.GDPermissionManager;
+import com.griefdefender.permission.GDPermissions;
+import net.kyori.text.Component;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.event.CauseStackManager;
+import org.spongepowered.api.service.economy.account.Account;
+import org.spongepowered.api.service.economy.transaction.ResultType;
+import org.spongepowered.api.service.economy.transaction.TransactionResult;
+
+import java.math.BigDecimal;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_BUY_CLAIM_BLOCKS)
+public class CommandClaimBuyBlocks extends BaseCommand {
+
+    @CommandAlias("buyclaim|buyclaimblocks|buyblocks")
+    @Description("Purchases additional claim blocks with server money.\nNote: Requires economy plugin.")
+    @Syntax("[<amount>]")
+    @Subcommand("buy blocks")
+    public void execute(Player player, @Optional Integer blockCount) {
+        if (GriefDefenderPlugin.getInstance().isEconomyModeEnabled()) {
+            TextAdapter.sendComponent(player, MessageCache.getInstance().COMMAND_NOT_AVAILABLE_ECONOMY);
+            return;
+        }
+
+        if (!GriefDefenderPlugin.getInstance().economyService.isPresent()) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().ECONOMY_NOT_INSTALLED);
+            return;
+        }
+
+        Account playerAccount = GriefDefenderPlugin.getInstance().economyService.get().getOrCreateAccount(player.getUniqueId()).orElse(null);
+        if (playerAccount == null) {
+            final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.ECONOMY_PLAYER_NOT_FOUND, ImmutableMap.of(
+                    "player", player.getName()));
+            GriefDefenderPlugin.sendMessage(player, message);
+            return;
+        }
+
+        final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        final GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAt(player.getLocation());
+        final double economyBlockCost = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Double.class), player, Options.ECONOMY_BLOCK_COST);
+        final double economyBlockSell = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Double.class), player, Options.ECONOMY_BLOCK_SELL_RETURN);
+        if (economyBlockCost == 0 && economyBlockSell == 0) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().ECONOMY_BLOCK_BUY_SELL_DISABLED);
+            return;
+        }
+
+        if (economyBlockCost == 0) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().ECONOMY_BLOCK_ONLY_SELL);
+            return;
+        }
+
+        double balance = playerAccount.getBalance(GriefDefenderPlugin.getInstance().economyService.get().getDefaultCurrency()).doubleValue();
+        if (blockCount == null) {
+            final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.ECONOMY_BLOCK_PURCHASE_COST, ImmutableMap.of(
+                    "amount", economyBlockCost,
+                    "balance", String.valueOf("$" + balance)));
+            GriefDefenderPlugin.sendMessage(player, message);
+            return;
+        } else {
+            if (blockCount <= 0) {
+                GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().ECONOMY_BLOCK_BUY_INVALID);
+                return;
+            }
+
+            final double totalCost = blockCount * economyBlockCost;
+            final int newClaimBlockTotal = playerData.getAccruedClaimBlocks() + blockCount;
+            if (newClaimBlockTotal > playerData.getMaxAccruedClaimBlocks()) {
+                final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.ECONOMY_BLOCK_PURCHASE_LIMIT, ImmutableMap.of(
+                            "total", newClaimBlockTotal,
+                            "limit", playerData.getMaxAccruedClaimBlocks()));
+                    GriefDefenderPlugin.sendMessage(player, message);
+                    return;
+            }
+
+            try (final CauseStackManager.StackFrame frame = Sponge.getCauseStackManager().pushCauseFrame()) {
+                Sponge.getCauseStackManager().addContext(GriefDefenderPlugin.PLUGIN_CONTEXT, GriefDefenderPlugin.getInstance());
+                TransactionResult transactionResult = playerAccount.withdraw
+                    (GriefDefenderPlugin.getInstance().economyService.get().getDefaultCurrency(), BigDecimal.valueOf(totalCost),
+                        Sponge.getCauseStackManager().getCurrentCause());
+
+                if (transactionResult.getResult() != ResultType.SUCCESS) {
+                    final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.ECONOMY_WITHDRAW_ERROR, ImmutableMap.of(
+                            "reason", transactionResult.getResult()));
+                    GriefDefenderPlugin.sendMessage(player, message);
+                    return;
+                }
+            }
+
+            final int bonusTotal = playerData.getBonusClaimBlocks();
+            playerData.setBonusClaimBlocks(bonusTotal + blockCount);
+            playerData.getStorageData().save();
+
+            final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.ECONOMY_BLOCK_PURCHASE_CONFIRMATION, ImmutableMap.of(
+                    "amount", totalCost,
+                    "balance", playerData.getRemainingClaimBlocks()));
+            GriefDefenderPlugin.sendMessage(player, message);
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimClear.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimClear.java
new file mode 100644
index 0000000..009b1c3
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimClear.java
@@ -0,0 +1,173 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandCompletion;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Optional;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.internal.util.NMSUtil;
+import com.griefdefender.permission.GDPermissions;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.format.TextColor;
+import net.minecraft.entity.EnumCreatureType;
+import net.minecraft.entity.IEntityOwnable;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.entity.Entity;
+import org.spongepowered.api.entity.EntityType;
+import org.spongepowered.api.entity.living.Villager;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.world.World;
+import org.spongepowered.common.SpongeImplHooks;
+import org.spongepowered.common.entity.SpongeEntityType;
+
+import java.util.UUID;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_CLAIM_CLEAR)
+public class CommandClaimClear extends BaseCommand {
+
+    @CommandCompletion("@gdentityids @gddummy")
+    @CommandAlias("claimclear")
+    @Description("Allows clearing of entities within one or more claims.")
+    @Syntax("<entity_id> [claim_uuid]")
+    @Subcommand("claim clear")
+    public void execute(Player player, String target, @Optional String claimId) {
+        World world = player.getWorld();
+        if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(world.getUniqueId())) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().CLAIM_DISABLED_WORLD);
+            return;
+        }
+
+        UUID claimUniqueId = null;
+        GDClaim targetClaim = null;
+        if (claimId == null) {
+            targetClaim = GriefDefenderPlugin.getInstance().dataStore.getClaimAt(player.getLocation());
+            final Component result = targetClaim.allowEdit(player);
+            if (result != null) {
+                GriefDefenderPlugin.sendMessage(player, result);
+                return;
+            }
+            claimUniqueId = targetClaim.getUniqueId();
+        } else {
+            if (!player.hasPermission(GDPermissions.COMMAND_DELETE_CLAIMS)) {
+                GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().COMMAND_CLAIMCLEAR_UUID_DENY);
+                return;
+            }
+            try {
+                claimUniqueId = UUID.fromString(claimId);
+            } catch (IllegalArgumentException e) {
+                return;
+            }
+        }
+
+        if (targetClaim.isWilderness()) {
+            GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.CLAIM_ACTION_NOT_AVAILABLE, 
+                    ImmutableMap.of("type", TextComponent.of("the wilderness"))));
+            return;
+        }
+        // Unfortunately this is required until Pixelmon registers their entities correctly in FML
+        // If target was not found in registry, assume its a pixelmon animal
+        EntityType entityType = Sponge.getRegistry().getType(EntityType.class, target).orElse(null);
+        boolean isPixelmonAnimal = target.contains("pixelmon") && entityType == null;
+
+        int count = 0;
+        String[] parts = target.split(":");
+        String modId = "minecraft";
+        EnumCreatureType creatureType = null;
+        if (parts.length > 1) {
+            modId = parts[0];
+            creatureType = NMSUtil.SPAWN_TYPES.get(parts[1]);
+        } else {
+            creatureType = NMSUtil.SPAWN_TYPES.get(parts[0]);
+            if (creatureType == null) {
+                target = "minecraft:" + target;
+            }
+        }
+
+        for (Entity entity : world.getEntities()) {
+            net.minecraft.entity.Entity mcEntity = (net.minecraft.entity.Entity) entity;
+            if (entity instanceof Villager) {
+                continue;
+            }
+            if (entity instanceof IEntityOwnable) {
+                IEntityOwnable ownable = (IEntityOwnable) entity;
+                if (ownable.getOwnerId() != null) {
+                    continue;
+                }
+            }
+
+            if (isPixelmonAnimal) {
+                if (parts[1].equalsIgnoreCase(mcEntity.getName().toLowerCase())) {
+                    GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAt(entity.getLocation());
+                    if (claim != null && claim.getUniqueId().equals(claimUniqueId)) {
+                        mcEntity.setDead();
+                        count++;
+                    }
+                }
+            } else if (creatureType != null && SpongeImplHooks.isCreatureOfType(mcEntity, creatureType)) {
+                GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAt(entity.getLocation());
+                if (claim != null && claim.getUniqueId().equals(claimUniqueId)) {
+                    // check modId
+                    String mod = ((SpongeEntityType) entity.getType()).getModId();
+                    if (modId.equalsIgnoreCase(mod)) {
+                        mcEntity.setDead();
+                        count++;
+                    }
+                }
+            } else {
+                if (entityType == null || !entityType.equals(((SpongeEntityType) entity.getType()))) {
+                    continue;
+                }
+
+                GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAt(entity.getLocation());
+                if (claim != null && claim.getUniqueId().equals(claimUniqueId)) {
+                    mcEntity.setDead();
+                    count++;
+                }
+            }
+        }
+
+        if (count == 0) {
+            GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.COMMAND_CLAIMCLEAR_NO_ENTITIES,
+                    ImmutableMap.of("type", TextComponent.of(target, TextColor.GREEN))));
+        } else {
+            GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.COMMAND_CLAIMCLEAR_NO_ENTITIES,
+                    ImmutableMap.of(
+                        "amount", count,
+                        "type", TextComponent.of(target, TextColor.GREEN))));
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimContract.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimContract.java
new file mode 100644
index 0000000..d628b6a
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimContract.java
@@ -0,0 +1,201 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import java.math.BigDecimal;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.data.property.entity.EyeLocationProperty;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.service.economy.Currency;
+import org.spongepowered.api.service.economy.account.Account;
+import org.spongepowered.api.util.Direction;
+
+import com.flowpowered.math.vector.Vector3i;
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.ClaimBlockSystem;
+import com.griefdefender.api.claim.ClaimResult;
+import com.griefdefender.api.claim.ClaimResultType;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.permission.GDPermissionUser;
+import com.griefdefender.permission.GDPermissions;
+import com.griefdefender.util.PlayerUtil;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandCompletion;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Optional;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+import net.kyori.text.TextComponent;
+import net.kyori.text.format.TextColor;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_CLAIM_EXPAND)
+public class CommandClaimContract extends BaseCommand {
+
+    @CommandCompletion("@gddummy @gdblockfaces @gddummy")
+    @CommandAlias("claimcontract|contractclaim")
+    @Description("Contracts/Shrinks the claim from the direction you are facing.")
+    @Syntax("<amount> [direction]")
+    @Subcommand("claim contract")
+    public void execute(Player player, int amount, @Optional String direction) {
+        final GDPermissionUser user = PermissionHolderCache.getInstance().getOrCreateUser(player);
+        final GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(user.getInternalPlayerData(), player.getLocation());
+        final GDPlayerData playerData = user.getInternalPlayerData();
+
+        if (claim.isWilderness()) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().CLAIM_NOT_YOURS);
+            return;
+        }
+        if (!claim.getInternalClaimData().isResizable() || (!claim.getOwnerUniqueId().equals(player.getUniqueId()) && !playerData.canIgnoreClaim(claim) && claim.allowEdit(player) != null)) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().PERMISSION_CLAIM_RESIZE);
+            return;
+        }
+
+        final Vector3i lesser = claim.lesserBoundaryCorner;
+        final Vector3i greater = claim.greaterBoundaryCorner;
+        Vector3i point1 = null;
+        Vector3i point2 = null;
+        if (direction == null || !direction.equalsIgnoreCase("all")) {
+            final Direction face = direction == null ? PlayerUtil.getInstance().getBlockFace(player) : PlayerUtil.getInstance().getBlockFace(direction);
+            if (face == null || amount <= 0) {
+                GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().COMMAND_INVALID);
+                return;
+            }
+    
+            if ((face == Direction.UP || face == Direction.DOWN) && !claim.cuboid) {
+                GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().COMMAND_INVALID);
+                return;
+            }
+            if (face == Direction.EAST) {
+                point1 = new Vector3i(lesser.getX(), lesser.getY(), lesser.getZ());
+                point2 = new Vector3i(greater.getX() - amount, greater.getY(), greater.getZ());
+            } else if (face == Direction.WEST) {
+                point1 = new Vector3i(lesser.getX() + amount, lesser.getY(), lesser.getZ());
+                point2 = new Vector3i(greater.getX(), greater.getY(), greater.getZ());
+            } else if (face == Direction.NORTH) {
+                point1 = new Vector3i(lesser.getX(), lesser.getY(), lesser.getZ() + amount);
+                point2 = new Vector3i(greater.getX(), greater.getY(), greater.getZ());
+            } else if (face == Direction.SOUTH) {
+                point1 = new Vector3i(lesser.getX(), lesser.getY(), lesser.getZ());
+                point2 = new Vector3i(greater.getX(), greater.getY(), greater.getZ() - amount);
+            }
+        } else {
+            point1 = new Vector3i(
+                    lesser.getX() + amount,
+                    lesser.getY(),
+                    lesser.getZ() + amount);
+            point2 = new Vector3i(
+                greater.getX() - amount,
+                greater.getY(),
+                greater.getZ() - amount);
+        }
+
+        final ClaimResult result = claim.resize(point1, point2);
+        if (!result.successful()) {
+            if (result.getResultType() == ClaimResultType.OVERLAPPING_CLAIM) {
+                GDClaim overlapClaim = (GDClaim) result.getClaim().get();
+                GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().RESIZE_OVERLAP);
+                Set<Claim> claims = new HashSet<>();
+                claims.add(overlapClaim);
+                CommandHelper.showOverlapClaims(player, claims, player.getProperty(EyeLocationProperty.class).get().getValue().getFloorY());
+            } else {
+                // TODO add to lang
+                GriefDefenderPlugin.sendMessage(player, TextComponent.of("Could not resize claim. Reason : " + result.getResultType()).color(TextColor.RED));
+            }
+        } else {
+            int claimBlocksRemaining = playerData.getRemainingClaimBlocks();;
+            if (!claim.isAdminClaim()) {
+                UUID ownerID = claim.getOwnerUniqueId();
+                if (claim.parent != null) {
+                    ownerID = claim.parent.getOwnerUniqueId();
+                }
+
+                if (ownerID.equals(player.getUniqueId())) {
+                    claimBlocksRemaining = playerData.getRemainingClaimBlocks();
+                } else {
+                    GDPlayerData ownerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), ownerID);
+                    claimBlocksRemaining = ownerData.getRemainingClaimBlocks();
+                    final Player owner = Sponge.getServer().getPlayer(ownerID).orElse(null);
+                    if (owner == null || !owner.isOnline()) {
+                        GriefDefenderPlugin.getInstance().dataStore.clearCachedPlayerData(player.getWorld().getUniqueId(), ownerID);
+                    }
+                }
+            }
+            if (GriefDefenderPlugin.getInstance().isEconomyModeEnabled()) {
+                final Account playerAccount = GriefDefenderPlugin.getInstance().economyService.get().getOrCreateAccount(player.getUniqueId()).orElse(null);
+                if (playerAccount != null) {
+                    final Currency defaultCurrency = GriefDefenderPlugin.getInstance().economyService.get().getDefaultCurrency();
+                    final BigDecimal currentFunds = playerAccount.getBalance(defaultCurrency);
+                    if (GriefDefenderPlugin.CLAIM_BLOCK_SYSTEM == ClaimBlockSystem.VOLUME) {
+                        final double claimableChunks = claimBlocksRemaining / 65536.0;
+                        final Map<String, Object> params = ImmutableMap.of(
+                                "balance", String.valueOf("$" + currentFunds.intValue()),
+                                "chunk-amount", Math.round(claimableChunks * 100.0)/100.0, 
+                                "block-amount", claimBlocksRemaining);
+                        GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.ECONOMY_MODE_RESIZE_SUCCESS_3D, params));
+                    } else {
+                        final Map<String, Object> params = ImmutableMap.of(
+                                "balance", String.valueOf("$" + currentFunds.intValue()),
+                                "block-amount", claimBlocksRemaining);
+                        GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.ECONOMY_MODE_RESIZE_SUCCESS_2D, params));
+                    }
+                }
+            } else {
+                if (GriefDefenderPlugin.CLAIM_BLOCK_SYSTEM == ClaimBlockSystem.VOLUME) {
+                    final double claimableChunks = claimBlocksRemaining / 65536.0;
+                    final Map<String, Object> params = ImmutableMap.of(
+                            "chunk-amount", Math.round(claimableChunks * 100.0)/100.0, 
+                            "block-amount", claimBlocksRemaining);
+                    GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.RESIZE_SUCCESS_3D, params));
+                } else {
+                    final Map<String, Object> params = ImmutableMap.of(
+                            "block-amount", claimBlocksRemaining);
+                    GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.RESIZE_SUCCESS_2D, params));
+                }
+            }
+            playerData.revertActiveVisual(player);
+            claim.getVisualizer().resetVisuals();
+            claim.getVisualizer().createClaimBlockVisuals(player.getProperty(EyeLocationProperty.class).get().getValue().getFloorY(), player.getLocation(), playerData);
+            claim.getVisualizer().apply(player);
+            if (GriefDefenderPlugin.getInstance().getWorldEditProvider() != null) {
+                GriefDefenderPlugin.getInstance().getWorldEditProvider().visualizeClaim(claim, player, playerData, false);
+            }
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimCreate.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimCreate.java
new file mode 100644
index 0000000..457f87a
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimCreate.java
@@ -0,0 +1,146 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.flowpowered.math.vector.Vector3i;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.reflect.TypeToken;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.GriefDefender;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.ClaimResult;
+import com.griefdefender.api.claim.ClaimResultType;
+import com.griefdefender.api.claim.ClaimType;
+import com.griefdefender.api.claim.ClaimTypes;
+import com.griefdefender.api.permission.option.Options;
+import com.griefdefender.api.permission.option.type.CreateModeTypes;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.internal.visual.ClaimVisual;
+import com.griefdefender.permission.GDPermissionManager;
+import com.griefdefender.permission.GDPermissionUser;
+import com.griefdefender.permission.GDPermissions;
+import com.griefdefender.registry.ClaimTypeRegistryModule;
+import com.griefdefender.util.EconomyUtil;
+import com.griefdefender.util.PlayerUtil;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandCompletion;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Optional;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+import net.kyori.text.Component;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.world.Location;
+import org.spongepowered.api.world.World;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.CLAIM_CREATE)
+public class CommandClaimCreate extends BaseCommand {
+
+    @CommandCompletion("@gddummy @gdclaimtypes @gddummy")
+    @CommandAlias("claimcreate")
+    @Description("Creates a claim around the player.")
+    @Syntax("<radius> [type] [player]")
+    @Subcommand("claim create")
+    public void execute(Player player, int radius, @Optional String type) {
+        final Location<World> location = player.getLocation();
+        final World world = location.getExtent();
+        final int minClaimLevel = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), player, Options.MIN_LEVEL);
+        final int maxClaimLevel = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), player, Options.MAX_LEVEL);
+        if (location.getBlockY() < minClaimLevel || location.getBlockY() > maxClaimLevel) {
+            final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.CLAIM_CHEST_OUTSIDE_LEVEL,
+                    ImmutableMap.of(
+                    "min-level", minClaimLevel,
+                    "max-level", maxClaimLevel));
+            GriefDefenderPlugin.sendMessage(player, message);
+            return;
+        }
+
+        final Vector3i lesserBoundary = new Vector3i(
+                location.getBlockX() - radius,
+                minClaimLevel,
+                location.getBlockZ() - radius);
+        final Vector3i greaterBoundary = new Vector3i(
+            location.getBlockX() + radius,
+            maxClaimLevel,
+            location.getBlockZ() + radius);
+
+        final GDPermissionUser user = PermissionHolderCache.getInstance().getOrCreateUser(player);
+        final GDPlayerData playerData = user.getInternalPlayerData();
+        final ClaimType claimType = ClaimTypeRegistryModule.getInstance().getById(type).orElse(ClaimTypes.BASIC);
+        final boolean cuboid = playerData.getClaimCreateMode() == CreateModeTypes.VOLUME;
+        if ((claimType == ClaimTypes.BASIC || claimType == ClaimTypes.TOWN) && GriefDefenderPlugin.getGlobalConfig().getConfig().economy.economyMode) {
+            EconomyUtil.getInstance().economyCreateClaimConfirmation(player, playerData, location.getBlockY(), lesserBoundary, greaterBoundary, claimType,
+                    cuboid, playerData.claimSubdividing);
+            return;
+        }
+
+        final ClaimResult result = GriefDefender.getRegistry().createBuilder(Claim.Builder.class)
+                .bounds(lesserBoundary, greaterBoundary)
+                .cuboid(cuboid)
+                .owner(user.getUniqueId())
+                .type(claimType)
+                .world(world.getUniqueId())
+                .build();
+        GDClaim gdClaim = (GDClaim) result.getClaim().orElse(null);
+        if (!result.successful()) {
+            if (result.getResultType() == ClaimResultType.OVERLAPPING_CLAIM) {
+                GDClaim overlapClaim = (GDClaim) result.getClaim().get();
+                GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().CREATE_OVERLAP_SHORT);
+                Set<Claim> claims = new HashSet<>();
+                claims.add(overlapClaim);
+                CommandHelper.showOverlapClaims(player, claims, location.getBlockY());
+            } else if (result.getResultType() == ClaimResultType.CLAIM_EVENT_CANCELLED) {
+                GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().CREATE_CANCEL);
+            } else {
+                GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.CREATE_FAILED_RESULT,
+                        ImmutableMap.of("reason", result.getResultType())));
+            }
+            return;
+        } else {
+            playerData.lastShovelLocation = null;
+            final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.CREATE_SUCCESS,
+                    ImmutableMap.of(
+                    "type", gdClaim.getFriendlyNameType(true)));
+            GriefDefenderPlugin.sendMessage(player, message);
+            if (GriefDefenderPlugin.getInstance().getWorldEditProvider() != null) {
+                GriefDefenderPlugin.getInstance().getWorldEditProvider().stopVisualDrag(player);
+                GriefDefenderPlugin.getInstance().getWorldEditProvider().visualizeClaim(gdClaim, player, playerData, false);
+            }
+            gdClaim.getVisualizer().createClaimBlockVisuals(location.getBlockY(), player.getLocation(), playerData);
+            gdClaim.getVisualizer().apply(player, false);
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimCuboid.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimCuboid.java
new file mode 100644
index 0000000..4205422
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimCuboid.java
@@ -0,0 +1,56 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.permission.option.type.CreateModeTypes;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.permission.GDPermissions;
+import org.spongepowered.api.entity.living.player.Player;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_CUBOID_CLAIMS)
+public class CommandClaimCuboid extends BaseCommand {
+
+    @CommandAlias("cuboid")
+    @Description("Toggles cuboid claims mode.")
+    @Subcommand("cuboid")
+    public void execute(Player player) {
+        final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        if (playerData.getClaimCreateMode() == CreateModeTypes.AREA) {
+            playerData.setClaimCreateMode(CreateModeTypes.VOLUME);
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().COMMAND_CUBOID_ENABLED);
+        } else {
+            playerData.setClaimCreateMode(CreateModeTypes.AREA);
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().COMMAND_CUBOID_DISABLED);
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimDelete.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimDelete.java
new file mode 100644
index 0000000..1b730d6
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimDelete.java
@@ -0,0 +1,121 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.ClaimResult;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.event.GDCauseStackManager;
+import com.griefdefender.permission.GDPermissions;
+import com.griefdefender.text.action.GDCallbackHolder;
+import com.griefdefender.util.PermissionUtil;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import net.kyori.text.event.ClickEvent;
+import net.kyori.text.event.HoverEvent;
+import net.kyori.text.format.TextColor;
+import org.spongepowered.api.command.CommandSource;
+import org.spongepowered.api.entity.living.player.Player;
+
+import java.util.function.Consumer;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_DELETE_CLAIMS)
+public class CommandClaimDelete extends BaseCommand {
+
+    protected boolean deleteTopLevelClaim = false;
+
+    @CommandAlias("deleteclaim")
+    @Description("Deletes the claim you're standing in, even if it's not your claim.")
+    @Subcommand("delete claim")
+    public void execute(Player player) {
+        final GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAt(player.getLocation());
+        if (claim.isWilderness()) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().CLAIM_NOT_FOUND);
+            return;
+        }
+
+        final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.PERMISSION_CLAIM_DELETE,
+                ImmutableMap.of(
+                "type", claim.getType().getName()));
+
+        if (claim.isAdminClaim() && !player.hasPermission(GDPermissions.DELETE_CLAIM_ADMIN)) {
+            GriefDefenderPlugin.sendMessage(player, message);
+            return;
+        }
+        if (claim.isBasicClaim() && !player.hasPermission(GDPermissions.DELETE_CLAIM_BASIC)) {
+            GriefDefenderPlugin.sendMessage(player, message);
+            return;
+        }
+
+        final Component confirmationText = TextComponent.builder()
+                .append(GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.DELETE_CLAIM_WARNING, 
+                        ImmutableMap.of("player", claim.getOwnerName().color(TextColor.AQUA))))
+                .append(TextComponent.builder()
+                    .append("\n[")
+                    .append("Confirm", TextColor.GREEN)
+                    .append("]\n")
+                    .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createConfirmationConsumer(player, claim, deleteTopLevelClaim))))
+                    .hoverEvent(HoverEvent.showText(MessageCache.getInstance().UI_CLICK_CONFIRM)).build())
+                .build();
+        TextAdapter.sendComponent(player, confirmationText);
+    }
+
+    private static Consumer<CommandSource> createConfirmationConsumer(Player player, Claim claim, boolean deleteTopLevelClaim) {
+        return confirm -> {
+            final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+            GDCauseStackManager.getInstance().pushCause(player);
+            ClaimResult claimResult = GriefDefenderPlugin.getInstance().dataStore.deleteClaim(claim, !deleteTopLevelClaim);
+            GDCauseStackManager.getInstance().popCause();
+            if (!claimResult.successful()) {
+                GriefDefenderPlugin.sendMessage(player, claimResult.getMessage().orElse(MessageCache.getInstance().PLUGIN_EVENT_CANCEL));
+                return;
+            }
+
+            PermissionUtil.getInstance().clearPermissions((GDClaim) claim);
+            playerData.revertActiveVisual(player);
+
+            if (claim.isTown()) {
+                playerData.inTown = false;
+                playerData.townChat = false;
+            }
+
+            final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.DELETE_CLAIM_SUCCESS, 
+                ImmutableMap.of("player", claim.getOwnerName().color(TextColor.AQUA)));
+            GriefDefenderPlugin.sendMessage(player, message);
+        };
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimDeleteAll.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimDeleteAll.java
new file mode 100644
index 0000000..e213952
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimDeleteAll.java
@@ -0,0 +1,110 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandCompletion;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.GriefDefender;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.event.GDCauseStackManager;
+import com.griefdefender.event.GDRemoveClaimEvent;
+import com.griefdefender.permission.GDPermissions;
+import com.griefdefender.text.action.GDCallbackHolder;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import net.kyori.text.event.ClickEvent;
+import net.kyori.text.event.HoverEvent;
+import net.kyori.text.format.TextColor;
+import org.spongepowered.api.command.CommandSource;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.entity.living.player.User;
+
+import java.util.function.Consumer;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_DELETE_CLAIMS)
+public class CommandClaimDeleteAll extends BaseCommand {
+
+    @CommandCompletion("@gdplayers @gddummy")
+    @CommandAlias("deleteall")
+    @Description("Delete all of another player's claims.")
+    @Syntax("<player>")
+    @Subcommand("delete all")
+    public void execute(Player src, User otherPlayer) {
+        final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(src.getWorld(), otherPlayer.getUniqueId());
+        int originalClaimCount = playerData.getInternalClaims().size();
+
+        if (originalClaimCount == 0) {
+            TextAdapter.sendComponent(src, TextComponent.of("Player " + otherPlayer.getName() + " has no claims to delete.", TextColor.RED));
+            return;
+        }
+
+        final Component confirmationText = TextComponent.builder("")
+                .append(GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.DELETE_ALL_PLAYER_WARNING, 
+                        ImmutableMap.of("player", TextComponent.of(otherPlayer.getName()).color(TextColor.AQUA))))
+                .append(TextComponent.builder()
+                    .append("\n[")
+                    .append("Confirm", TextColor.GREEN)
+                    .append("]\n")
+                    .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createConfirmationConsumer(src, otherPlayer, playerData))))
+                    .hoverEvent(HoverEvent.showText(MessageCache.getInstance().UI_CLICK_CONFIRM)).build())
+                .build();
+        TextAdapter.sendComponent(src, confirmationText);
+    }
+
+    private static Consumer<CommandSource> createConfirmationConsumer(Player src, User otherPlayer, GDPlayerData playerData) {
+        return confirm -> {
+            GDCauseStackManager.getInstance().pushCause(src);
+            GDRemoveClaimEvent event = new GDRemoveClaimEvent(ImmutableList.copyOf(playerData.getInternalClaims()));
+            GriefDefender.getEventManager().post(event);
+            GDCauseStackManager.getInstance().popCause();
+            if (event.cancelled()) {
+                GriefDefenderPlugin.sendMessage(src, event.getMessage().orElse(MessageCache.getInstance().PLUGIN_EVENT_CANCEL));
+                return;
+            }
+
+            GriefDefenderPlugin.getInstance().dataStore.deleteClaimsForPlayer(otherPlayer.getUniqueId());
+            if (src != null) {
+                final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.DELETE_ALL_PLAYER_SUCCESS, ImmutableMap.of(
+                        "player", TextComponent.of(otherPlayer.getName()).color(TextColor.AQUA)));
+                GriefDefenderPlugin.sendMessage(src, message);
+                // revert any current visualization
+                playerData.revertActiveVisual(src);
+            }
+        };
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimDeleteAllAdmin.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimDeleteAllAdmin.java
new file mode 100644
index 0000000..2be0f54
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimDeleteAllAdmin.java
@@ -0,0 +1,90 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.claim.ClaimResult;
+import com.griefdefender.api.claim.ClaimTypes;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.permission.GDPermissions;
+import com.griefdefender.text.action.GDCallbackHolder;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import net.kyori.text.event.ClickEvent;
+import net.kyori.text.event.HoverEvent;
+import net.kyori.text.format.TextColor;
+import org.spongepowered.api.command.CommandSource;
+import org.spongepowered.api.entity.living.player.Player;
+
+import java.util.function.Consumer;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_DELETE_ADMIN_CLAIMS)
+public class CommandClaimDeleteAllAdmin extends BaseCommand {
+
+    @CommandAlias("deletealladmin")
+    @Description("Deletes all administrative claims.")
+    @Subcommand("delete alladmin")
+    public void execute(Player player) {
+        final Component confirmationText = TextComponent.builder("")
+                .append(GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.DELETE_ALL_TYPE_WARNING, 
+                        ImmutableMap.of("type", TextComponent.of("ADMIN").color(TextColor.RED))))
+                .append(TextComponent.builder()
+                    .append("\n[")
+                    .append("Confirm", TextColor.GREEN)
+                    .append("]\n")
+                    .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createConfirmationConsumer(player))))
+                    .hoverEvent(HoverEvent.showText(MessageCache.getInstance().UI_CLICK_CONFIRM)).build())
+                .build();
+        TextAdapter.sendComponent(player, confirmationText);
+    }
+
+    private static Consumer<CommandSource> createConfirmationConsumer(Player player) {
+        return confirm -> {
+            ClaimResult claimResult = GriefDefenderPlugin.getInstance().dataStore.deleteAllAdminClaims(player, player.getWorld());
+            if (!claimResult.successful()) {
+                final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.CLAIM_TYPE_NOT_FOUND,
+                        ImmutableMap.of(
+                        "type", ClaimTypes.ADMIN.getName().toLowerCase()));
+                GriefDefenderPlugin.sendMessage(player, claimResult.getMessage().orElse(message));
+                return;
+            }
+
+            GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.DELETE_ALL_TYPE_SUCCESS,
+                    ImmutableMap.of("type", TextComponent.of("ADMIN").color(TextColor.RED))));
+            GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+            playerData.revertActiveVisual(player);
+        };
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimDeleteTop.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimDeleteTop.java
new file mode 100644
index 0000000..a5a901f
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimDeleteTop.java
@@ -0,0 +1,49 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import com.griefdefender.permission.GDPermissions;
+import org.spongepowered.api.entity.living.player.Player;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_DELETE_CLAIMS)
+public class CommandClaimDeleteTop extends CommandClaimDelete {
+
+
+    public CommandClaimDeleteTop() {
+        this.deleteTopLevelClaim = true;
+    }
+
+    @CommandAlias("deletetop")
+    @Description("Deletes the claim you're standing in, even if it's not your claim.")
+    @Subcommand("delete top")
+    public void execute(Player player) {
+        super.execute(player);
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimExpand.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimExpand.java
new file mode 100644
index 0000000..4442d94
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimExpand.java
@@ -0,0 +1,201 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import java.math.BigDecimal;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.data.property.entity.EyeLocationProperty;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.service.economy.Currency;
+import org.spongepowered.api.service.economy.account.Account;
+import org.spongepowered.api.util.Direction;
+
+import com.flowpowered.math.vector.Vector3i;
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.ClaimBlockSystem;
+import com.griefdefender.api.claim.ClaimResult;
+import com.griefdefender.api.claim.ClaimResultType;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.permission.GDPermissionUser;
+import com.griefdefender.permission.GDPermissions;
+import com.griefdefender.util.PlayerUtil;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandCompletion;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Optional;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+import net.kyori.text.TextComponent;
+import net.kyori.text.format.TextColor;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_CLAIM_EXPAND)
+public class CommandClaimExpand extends BaseCommand {
+
+    @CommandCompletion("@gddummy @gdDirections @gddummy")
+    @CommandAlias("claimexpand|expandclaim")
+    @Description("Expands the claim in the direction you are facing.")
+    @Syntax("<amount> [direction]")
+    @Subcommand("claim expand")
+    public void execute(Player player, int amount, @Optional String direction) {
+        final GDPermissionUser user = PermissionHolderCache.getInstance().getOrCreateUser(player);
+        final GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(user.getInternalPlayerData(), player.getLocation());
+        final GDPlayerData playerData = user.getInternalPlayerData();
+
+        if (claim.isWilderness()) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().CLAIM_NOT_YOURS);
+            return;
+        }
+        if (!claim.getInternalClaimData().isResizable() || (!claim.getOwnerUniqueId().equals(player.getUniqueId()) && !playerData.canIgnoreClaim(claim) && claim.allowEdit(player) != null)) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().PERMISSION_CLAIM_RESIZE);
+            return;
+        }
+
+        final Vector3i lesser = claim.lesserBoundaryCorner;
+        final Vector3i greater = claim.greaterBoundaryCorner;
+        Vector3i point1 = null;
+        Vector3i point2 = null;
+        if (direction == null || !direction.equalsIgnoreCase("all")) {
+            final Direction face = direction == null ? PlayerUtil.getInstance().getBlockFace(player) : PlayerUtil.getInstance().getBlockFace(direction);
+            if (face == null || amount <= 0) {
+                GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().COMMAND_INVALID);
+                return;
+            }
+    
+            if ((face == Direction.UP || face == Direction.DOWN) && !claim.cuboid) {
+                GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().COMMAND_INVALID);
+                return;
+            }
+            if (face == Direction.EAST) {
+                point1 = new Vector3i(lesser.getX(), lesser.getY(), lesser.getZ());
+                point2 = new Vector3i(greater.getX() + amount, greater.getY(), greater.getZ());
+            } else if (face == Direction.WEST) {
+                point1 = new Vector3i(lesser.getX() - amount, lesser.getY(), lesser.getZ());
+                point2 = new Vector3i(greater.getX(), greater.getY(), greater.getZ());
+            } else if (face == Direction.NORTH) {
+                point1 = new Vector3i(lesser.getX(), lesser.getY(), lesser.getZ() - amount);
+                point2 = new Vector3i(greater.getX(), greater.getY(), greater.getZ());
+            } else if (face == Direction.SOUTH) {
+                point1 = new Vector3i(lesser.getX(), lesser.getY(), lesser.getZ());
+                point2 = new Vector3i(greater.getX(), greater.getY(), greater.getZ() + amount);
+            }
+        } else {
+            point1 = new Vector3i(
+                    lesser.getX() - amount,
+                    lesser.getY(),
+                    lesser.getZ() - amount);
+            point2 = new Vector3i(
+                greater.getX() + amount,
+                greater.getY(),
+                greater.getZ() + amount);
+        }
+
+        final ClaimResult result = claim.resize(point1, point2);
+        if (!result.successful()) {
+            if (result.getResultType() == ClaimResultType.OVERLAPPING_CLAIM) {
+                GDClaim overlapClaim = (GDClaim) result.getClaim().get();
+                GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().RESIZE_OVERLAP);
+                Set<Claim> claims = new HashSet<>();
+                claims.add(overlapClaim);
+                CommandHelper.showOverlapClaims(player, claims, player.getProperty(EyeLocationProperty.class).get().getValue().getFloorY());
+            } else {
+                // TODO add to lang
+                GriefDefenderPlugin.sendMessage(player, TextComponent.of("Could not resize claim. Reason : " + result.getResultType()).color(TextColor.RED));
+            }
+        } else {
+            int claimBlocksRemaining = playerData.getRemainingClaimBlocks();;
+            if (!claim.isAdminClaim()) {
+                UUID ownerID = claim.getOwnerUniqueId();
+                if (claim.parent != null) {
+                    ownerID = claim.parent.getOwnerUniqueId();
+                }
+
+                if (ownerID.equals(player.getUniqueId())) {
+                    claimBlocksRemaining = playerData.getRemainingClaimBlocks();
+                } else {
+                    GDPlayerData ownerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), ownerID);
+                    claimBlocksRemaining = ownerData.getRemainingClaimBlocks();
+                    final Player owner = Sponge.getServer().getPlayer(ownerID).orElse(null);
+                    if (owner == null || !owner.isOnline()) {
+                        GriefDefenderPlugin.getInstance().dataStore.clearCachedPlayerData(player.getWorld().getUniqueId(), ownerID);
+                    }
+                }
+            }
+            if (GriefDefenderPlugin.getInstance().isEconomyModeEnabled()) {
+                final Account playerAccount = GriefDefenderPlugin.getInstance().economyService.get().getOrCreateAccount(player.getUniqueId()).orElse(null);
+                if (playerAccount != null) {
+                    final Currency defaultCurrency = GriefDefenderPlugin.getInstance().economyService.get().getDefaultCurrency();
+                    final BigDecimal currentFunds = playerAccount.getBalance(defaultCurrency);
+                    if (GriefDefenderPlugin.CLAIM_BLOCK_SYSTEM == ClaimBlockSystem.VOLUME) {
+                        final double claimableChunks = claimBlocksRemaining / 65536.0;
+                        final Map<String, Object> params = ImmutableMap.of(
+                                "balance", String.valueOf("$" + currentFunds.intValue()),
+                                "chunk-amount", Math.round(claimableChunks * 100.0)/100.0, 
+                                "block-amount", claimBlocksRemaining);
+                        GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.ECONOMY_MODE_RESIZE_SUCCESS_3D, params));
+                    } else {
+                        final Map<String, Object> params = ImmutableMap.of(
+                                "balance", String.valueOf("$" + currentFunds.intValue()),
+                                "block-amount", claimBlocksRemaining);
+                        GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.ECONOMY_MODE_RESIZE_SUCCESS_2D, params));
+                    }
+                }
+            } else {
+                if (GriefDefenderPlugin.CLAIM_BLOCK_SYSTEM == ClaimBlockSystem.VOLUME) {
+                    final double claimableChunks = claimBlocksRemaining / 65536.0;
+                    final Map<String, Object> params = ImmutableMap.of(
+                            "chunk-amount", Math.round(claimableChunks * 100.0)/100.0, 
+                            "block-amount", claimBlocksRemaining);
+                    GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.RESIZE_SUCCESS_3D, params));
+                } else {
+                    final Map<String, Object> params = ImmutableMap.of(
+                            "block-amount", claimBlocksRemaining);
+                    GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.RESIZE_SUCCESS_2D, params));
+                }
+            }
+            playerData.revertActiveVisual(player);
+            claim.getVisualizer().resetVisuals();
+            claim.getVisualizer().createClaimBlockVisuals(player.getProperty(EyeLocationProperty.class).get().getValue().getFloorY(), player.getLocation(), playerData);
+            claim.getVisualizer().apply(player);
+            if (GriefDefenderPlugin.getInstance().getWorldEditProvider() != null) {
+                GriefDefenderPlugin.getInstance().getWorldEditProvider().visualizeClaim(claim, player, playerData, false);
+            }
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimFarewell.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimFarewell.java
new file mode 100644
index 0000000..706f80d
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimFarewell.java
@@ -0,0 +1,80 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.permission.GDPermissions;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import net.kyori.text.serializer.legacy.LegacyComponentSerializer;
+import org.spongepowered.api.entity.living.player.Player;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_SET_CLAIM_FAREWELL)
+public class CommandClaimFarewell extends BaseCommand {
+
+    @CommandAlias("claimfarewell")
+    @Description("Sets the farewell message of your claim.")
+    @Syntax("<message>")
+    @Subcommand("claim farewell")
+    public void execute(Player player, String message) {
+        final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        final GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(playerData, player.getLocation());
+        if (claim.allowEdit(player) != null) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().PERMISSION_EDIT_CLAIM);
+            return;
+        }
+
+        TextComponent farewell = LegacyComponentSerializer.legacy().deserialize(message, '&');
+        if (farewell == TextComponent.empty() || farewell.content().equals("clear")) {
+            claim.getInternalClaimData().setFarewell(null);
+        } else {
+            claim.getInternalClaimData().setFarewell(farewell);
+        }
+        claim.getInternalClaimData().setRequiresSave(true);
+        claim.getInternalClaimData().save();
+        Component resultMessage = null;
+        if (!claim.getInternalClaimData().getFarewell().isPresent()) {
+            resultMessage = MessageCache.getInstance().CLAIM_FAREWELL_CLEAR;
+        } else {
+            resultMessage = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.CLAIM_FAREWELL,
+                    ImmutableMap.of(
+                    "farewell", farewell));
+        }
+        TextAdapter.sendComponent(player, resultMessage);
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimFlag.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimFlag.java
new file mode 100644
index 0000000..484811d
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimFlag.java
@@ -0,0 +1,57 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.InvalidCommandArgument;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandCompletion;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Optional;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.permission.GDPermissions;
+import org.spongepowered.api.entity.living.player.Player;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_FLAGS_CLAIM)
+public class CommandClaimFlag extends ClaimFlagBase {
+
+    public CommandClaimFlag() {
+        super(ClaimSubjectType.GLOBAL);
+    }
+
+    @CommandCompletion("@gdflags @gdmcids @gdtristates @gdcontexts @gddummy")
+    @CommandAlias("cf|claimflag")
+    @Description("Gets/Sets claim flags in the claim you are standing in.")
+    @Syntax("<flag> <target> <value> [context[key=value]]")
+    @Subcommand("flag claim")
+    public void execute(Player player, @Optional String[] args) throws InvalidCommandArgument {
+        this.subject = GriefDefenderPlugin.DEFAULT_HOLDER;
+        this.friendlySubjectName = "ALL";
+        super.execute(player, args);
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimFlagDebug.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimFlagDebug.java
new file mode 100644
index 0000000..8127c26
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimFlagDebug.java
@@ -0,0 +1,67 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.permission.GDPermissionUser;
+import com.griefdefender.permission.GDPermissions;
+import net.kyori.text.Component;
+import org.spongepowered.api.entity.living.player.Player;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_FLAGS_DEBUG)
+public class CommandClaimFlagDebug extends BaseCommand {
+
+    @CommandAlias("cfd")
+    @Description("Toggles claim flag debug mode.")
+    @Subcommand("claim debug")
+    public void execute(Player player) {
+        final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        final GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(playerData, player.getLocation());
+        final GDPermissionUser user = PermissionHolderCache.getInstance().getOrCreateUser(player);
+        final Component result = claim.allowEdit(user, true);
+        if (result != null) {
+            GriefDefenderPlugin.sendMessage(player, result);
+            return;
+        }
+
+        playerData.debugClaimPermissions = !playerData.debugClaimPermissions;
+
+        if (!playerData.debugClaimPermissions) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().COMMAND_CLAIMFLAGDEBUG_DISABLED);
+        } else {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().COMMAND_CLAIMFLAGDEBUG_ENABLED);
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimFlagGroup.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimFlagGroup.java
new file mode 100644
index 0000000..9c5399b
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimFlagGroup.java
@@ -0,0 +1,80 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.InvalidCommandArgument;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandCompletion;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Optional;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.permission.GDPermissions;
+import com.griefdefender.util.PermissionUtil;
+import net.kyori.text.Component;
+import org.spongepowered.api.entity.living.player.Player;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_FLAGS_GROUP)
+public class CommandClaimFlagGroup extends ClaimFlagBase {
+
+    public CommandClaimFlagGroup() {
+        super(ClaimSubjectType.GROUP);
+    }
+
+    @CommandCompletion("@gdgroups @gdflags @gdmcids @gdtristates @gdcontexts @gddummy")
+    @CommandAlias("cfg")
+    @Description("Gets/Sets flag permission for a group in claim you are standing in.")
+    @Syntax("<group> <flag> <target> <value> [context[key=value]]")
+    @Subcommand("flag group")
+    public void execute(Player player, String group, @Optional String[] args) throws InvalidCommandArgument {
+        if (args.length < 2 || args.length > 3) {
+            throw new InvalidCommandArgument();
+        }
+
+        if (!PermissionUtil.getInstance().hasGroupSubject(group)) {
+            final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.COMMAND_INVALID_GROUP, ImmutableMap.of(
+                    "group", group));
+            GriefDefenderPlugin.sendMessage(player, message);
+            return;
+        }
+
+        /*String reason = ctx.<String>getOne("reason").orElse(null);
+        Text reasonText = null;
+        if (reason != null) {
+            reasonText = TextSerializers.FORMATTING_CODE.deserialize(reason);
+        }*/
+
+        this.subject = PermissionHolderCache.getInstance().getOrCreateHolder(group);
+        this.friendlySubjectName = group;
+
+        super.execute(player, args);
+    }
+}
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimFlagPlayer.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimFlagPlayer.java
new file mode 100644
index 0000000..2788e3e
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimFlagPlayer.java
@@ -0,0 +1,66 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.InvalidCommandArgument;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandCompletion;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Optional;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.permission.GDPermissions;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.entity.living.player.User;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_FLAGS_PLAYER)
+public class CommandClaimFlagPlayer extends ClaimFlagBase  {
+
+    public CommandClaimFlagPlayer() {
+        super(ClaimSubjectType.PLAYER);
+    }
+
+    @CommandCompletion("@gdplayers @gdflags @gdmcids @gdtristates @gdcontexts @gddummy")
+    @CommandAlias("cfp")
+    @Description("Gets/Sets flag permission for a player in claim you are standing in.")
+    @Syntax("<player> <flag> <target> <value> [context[key=value]]")
+    @Subcommand("flag player")
+    public void execute(Player player, User user, @Optional String[] args) throws InvalidCommandArgument {
+        this.subject = PermissionHolderCache.getInstance().getOrCreateUser(user);
+        this.friendlySubjectName = user.getName();
+
+        if (user.hasPermission(GDPermissions.COMMAND_ADMIN_CLAIMS) && !player.hasPermission(GDPermissions.SET_ADMIN_FLAGS)) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().PERMISSION_PLAYER_ADMIN_FLAGS);
+            return;
+        }
+
+        super.execute(player, args);
+    }
+}
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimFlagReset.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimFlagReset.java
new file mode 100644
index 0000000..114abe9
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimFlagReset.java
@@ -0,0 +1,102 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.permission.GDPermissions;
+import com.griefdefender.text.action.GDCallbackHolder;
+import com.griefdefender.util.PermissionUtil;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import net.kyori.text.event.ClickEvent;
+import net.kyori.text.event.HoverEvent;
+import net.kyori.text.format.TextColor;
+import org.spongepowered.api.command.CommandSource;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.service.context.Context;
+
+import java.util.Set;
+import java.util.function.Consumer;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_FLAGS_RESET)
+public class CommandClaimFlagReset extends BaseCommand {
+
+    @CommandAlias("cfr")
+    @Description("Resets a claim to flag defaults.")
+    @Subcommand("flag reset")
+    public void execute(Player player) {
+        final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        final GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(playerData, player.getLocation());
+        final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.PERMISSION_CLAIM_RESET_FLAGS,
+                ImmutableMap.of(
+                "type", claim.getType().getName()));
+        if (claim.isWilderness()) {
+            if (!player.hasPermission(GDPermissions.MANAGE_WILDERNESS)) {
+                GriefDefenderPlugin.sendMessage(player, message);
+                return;
+            }
+        } else if (claim.isAdminClaim()) {
+            if (!player.getUniqueId().equals(claim.getOwnerUniqueId()) && !player.hasPermission(GDPermissions.COMMAND_ADMIN_CLAIMS)) {
+                GriefDefenderPlugin.sendMessage(player, message);
+                return;
+            }
+        } else if (!player.hasPermission(GDPermissions.COMMAND_ADMIN_CLAIMS) && (claim.isBasicClaim() || claim.isSubdivision()) && !player.getUniqueId().equals(claim.getOwnerUniqueId())) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().PERMISSION_CLAIM_RESET_FLAGS_SELF);
+            return;
+        }
+
+        final Component confirmationText = TextComponent.builder()
+                .append(MessageCache.getInstance().FLAG_RESET_WARNING)
+                .append(TextComponent.builder()
+                    .append("\n[")
+                    .append("Confirm", TextColor.GREEN)
+                    .append("]\n")
+                    .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createConfirmationConsumer(player, claim))))
+                    .hoverEvent(HoverEvent.showText(MessageCache.getInstance().UI_CLICK_CONFIRM)).build())
+                .build();
+        TextAdapter.sendComponent(player, confirmationText);
+    }
+
+    private static Consumer<CommandSource> createConfirmationConsumer(Player player, Claim claim) {
+        return confirm -> {
+            // Remove persisted data
+            PermissionUtil.getInstance().clearPermissions((GDClaim) claim);
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().FLAG_RESET_SUCCESS);
+        };
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimGreeting.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimGreeting.java
new file mode 100644
index 0000000..85165fb
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimGreeting.java
@@ -0,0 +1,81 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.permission.GDPermissions;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import net.kyori.text.serializer.legacy.LegacyComponentSerializer;
+import org.spongepowered.api.entity.living.player.Player;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_SET_CLAIM_GREETING)
+public class CommandClaimGreeting extends BaseCommand {
+
+    @CommandAlias("claimgreeting")
+    @Description("Sets the greeting message of your claim.")
+    @Syntax("<message>")
+    @Subcommand("claim greeting")
+    public void execute(Player player, String message) {
+        final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        final GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(playerData, player.getLocation());
+        final Component result = claim.allowEdit(player);
+        if (result != null) {
+            GriefDefenderPlugin.sendMessage(player, result);
+            return;
+        }
+
+        final TextComponent greeting = LegacyComponentSerializer.legacy().deserialize(message, '&');
+        if (greeting == TextComponent.empty() || greeting.content().equals("clear")) {
+            claim.getInternalClaimData().setGreeting(null);
+        } else {
+            claim.getInternalClaimData().setGreeting(greeting);
+        }
+        claim.getInternalClaimData().setRequiresSave(true);
+        claim.getInternalClaimData().save();
+        Component resultMessage = null;
+        if (!claim.getInternalClaimData().getGreeting().isPresent()) {
+            resultMessage = MessageCache.getInstance().CLAIM_GREETING_CLEAR;
+        } else {
+            resultMessage = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.CLAIM_GREETING,
+                    ImmutableMap.of(
+                    "greeting", greeting));
+        }
+        TextAdapter.sendComponent(player, resultMessage);
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimIgnore.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimIgnore.java
new file mode 100644
index 0000000..0198433
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimIgnore.java
@@ -0,0 +1,67 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.permission.GDPermissions;
+import net.kyori.text.Component;
+import org.spongepowered.api.entity.living.player.Player;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_IGNORE_CLAIMS)
+public class CommandClaimIgnore extends BaseCommand {
+
+    @CommandAlias("claimignore|ignoreclaims|ic")
+    @Description("Toggles ignore claims mode.")
+    @Subcommand("claim ignore")
+    public void execute(Player player) {
+        final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        final GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAt(player.getLocation());
+        if (claim.isBasicClaim() && !playerData.ignoreBasicClaims || claim.isWilderness() && !playerData.ignoreWilderness || claim.isAdminClaim() && !playerData.ignoreAdminClaims) {
+            final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.PERMISSION_CLAIM_IGNORE, ImmutableMap.of(
+                    "type", claim.getType().getName()));
+            GriefDefenderPlugin.sendMessage(player, message);
+            return;
+        }
+
+        playerData.ignoreClaims = !playerData.ignoreClaims;
+
+        if (!playerData.ignoreClaims) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().CLAIM_RESPECTING);
+        } else {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().CLAIM_IGNORE);
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimInfo.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimInfo.java
new file mode 100644
index 0000000..c553ea1
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimInfo.java
@@ -0,0 +1,888 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+import com.flowpowered.math.vector.Vector3i;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.reflect.TypeToken;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.Tristate;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.ClaimResult;
+import com.griefdefender.api.claim.ClaimType;
+import com.griefdefender.api.claim.ClaimTypes;
+import com.griefdefender.api.claim.TrustTypes;
+import com.griefdefender.api.permission.option.Options;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.claim.GDClaimManager;
+import com.griefdefender.configuration.MessageDataConfig;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.internal.pagination.PaginationList;
+import com.griefdefender.internal.util.VecHelper;
+import com.griefdefender.permission.GDPermissionManager;
+import com.griefdefender.permission.GDPermissionUser;
+import com.griefdefender.permission.GDPermissions;
+import com.griefdefender.text.action.GDCallbackHolder;
+import com.griefdefender.util.PlayerUtil;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import net.kyori.text.event.ClickEvent;
+import net.kyori.text.event.HoverEvent;
+import net.kyori.text.format.TextColor;
+import net.kyori.text.format.TextDecoration;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.command.CommandSource;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.world.Location;
+import org.spongepowered.api.world.World;
+
+import java.time.Instant;
+import java.time.format.DateTimeParseException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.function.Consumer;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_CLAIM_INFO_BASE)
+public class CommandClaimInfo extends BaseCommand {
+
+    private final Component NONE = TextComponent.of("none", TextColor.GRAY);
+    private final int ADMIN_SETTINGS = 0;
+    private final int CLAIM_EXPIRATION = 1;
+    private final int DENY_MESSAGES = 2;
+    private final int FLAG_OVERRIDES = 3;
+    private final int INHERIT_PARENT = 4;
+    private final int PVP_OVERRIDE = 5;
+    private final int RAID_OVERRIDE = 6;
+    private final int RESIZABLE = 7;
+    private final int REQUIRES_CLAIM_BLOCKS = 8;
+    private final int SIZE_RESTRICTIONS = 9;
+    private final int FOR_SALE = 10;
+    private boolean useTownInfo = false;
+
+    public CommandClaimInfo() {
+        
+    }
+
+    public CommandClaimInfo(boolean useTownInfo) {
+        this.useTownInfo = useTownInfo;
+    }
+
+    @CommandAlias("claiminfo")
+    @Syntax("[claim_uuid]")
+    @Subcommand("claim info")
+    public void execute(CommandSource src, String[] args) {
+        String claimIdentifier = null;
+        if (args.length > 0) {
+            claimIdentifier = args[0];
+        }
+
+        Player player = null;
+        if (src instanceof Player) {
+            player = (Player) src;
+            if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(player.getWorld().getUniqueId())) {
+                GriefDefenderPlugin.sendMessage(src, MessageCache.getInstance().CLAIM_DISABLED_WORLD);
+                return;
+            }
+        }
+
+        if (player == null && claimIdentifier == null) {
+            TextAdapter.sendComponent(src, MessageCache.getInstance().COMMAND_CLAIMINFO_NOT_FOUND);
+            return;
+        }
+
+        boolean isAdmin = src.hasPermission(GDPermissions.COMMAND_ADMIN_CLAIMS);
+        final GDPlayerData playerData = player != null ? GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId()) : null;
+        Claim claim = null;
+        if (claimIdentifier == null) {
+            if (player != null) {
+                claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAt(player.getLocation());
+            } else {
+                TextAdapter.sendComponent(src, MessageCache.getInstance().COMMAND_CLAIMINFO_UUID_REQUIRED);
+                return;
+            }
+        } else {
+            for (World world : Sponge.getServer().getWorlds()) {
+                if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(world.getUniqueId())) {
+                    continue;
+                }
+
+                final GDClaimManager claimManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(world.getUniqueId());
+                UUID uuid = null;
+                try {
+                    uuid = UUID.fromString(claimIdentifier);
+                    claim = claimManager.getClaimByUUID(uuid).orElse(null);
+                    if (claim != null) {
+                        break;
+                    }
+                } catch (IllegalArgumentException e) {
+                    
+                }
+                if (uuid == null) {
+                    final List<Claim> claimList = claimManager.getClaimsByName(claimIdentifier);
+                    if (!claimList.isEmpty()) {
+                        claim = claimList.get(0);
+                    }
+                }
+            }
+        }
+
+        if (claim == null) {
+            GriefDefenderPlugin.sendMessage(src, MessageCache.getInstance().CLAIM_NOT_FOUND);
+            return;
+        }
+
+        if (this.useTownInfo) {
+            if (!claim.isInTown()) {
+                GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().TOWN_NOT_IN);
+                return;
+            }
+            claim = claim.getTown().get();
+        }
+
+        final GDClaim gdClaim = (GDClaim) claim;
+        final GDPermissionUser owner = PermissionHolderCache.getInstance().getOrCreateUser(claim.getOwnerUniqueId());
+        final UUID ownerUniqueId = claim.getOwnerUniqueId();
+
+        if (!isAdmin) {
+            isAdmin = playerData.canIgnoreClaim(gdClaim);
+        }
+        // if not owner of claim, validate perms
+        if (!isAdmin && !player.getUniqueId().equals(claim.getOwnerUniqueId())) {
+            if (!gdClaim.getInternalClaimData().getContainers().contains(player.getUniqueId()) 
+                    && !gdClaim.getInternalClaimData().getBuilders().contains(player.getUniqueId())
+                    && !gdClaim.getInternalClaimData().getManagers().contains(player.getUniqueId())
+                    && !player.hasPermission(GDPermissions.COMMAND_CLAIM_INFO_OTHERS)) {
+                TextAdapter.sendComponent(player, MessageCache.getInstance().CLAIM_NOT_YOURS);
+                return;
+            }
+        }
+
+        final Component allowEdit = gdClaim.allowEdit(player);
+
+        List<Component> textList = new ArrayList<>();
+        Component name = claim.getName().orElse(null);
+        Component greeting = claim.getData().getGreeting().orElse(null);
+        Component farewell = claim.getData().getFarewell().orElse(null);
+        String accessors = "";
+        String builders = "";
+        String containers = "";
+        String managers = "";
+        String accessorGroups = "";
+        String builderGroups = "";
+        String containerGroups = "";
+        String managerGroups = "";
+
+        final int minClaimLevel = gdClaim.getOwnerMinClaimLevel();
+        double claimY = gdClaim.getOwnerPlayerData() == null ? 65.0D : (minClaimLevel > 65.0D ? minClaimLevel : 65);
+        if (gdClaim.isCuboid()) {
+            claimY = gdClaim.lesserBoundaryCorner.getY();
+        }
+
+        Location<World> southWest = new Location<>(gdClaim.getWorld(), gdClaim.lesserBoundaryCorner.getX(), claimY, gdClaim.greaterBoundaryCorner.getZ());
+        Location<World> northWest = new Location<>(gdClaim.getWorld(), gdClaim.lesserBoundaryCorner.getX(), claimY, gdClaim.lesserBoundaryCorner.getZ());
+        Location<World> southEast = new Location<>(gdClaim.getWorld(), gdClaim.greaterBoundaryCorner.getX(), claimY, gdClaim.greaterBoundaryCorner.getZ());
+        Location<World> northEast = new Location<>(gdClaim.getWorld(), gdClaim.greaterBoundaryCorner.getX(), claimY, gdClaim.lesserBoundaryCorner.getZ());
+        // String southWestCorner = 
+        Date created = null;
+        Date lastActive = null;
+        try {
+            Instant instant = claim.getData().getDateCreated();
+            created = Date.from(instant);
+        } catch(DateTimeParseException ex) {
+            // ignore
+        }
+
+        try {
+            Instant instant = claim.getData().getDateLastActive();
+            lastActive = Date.from(instant);
+        } catch(DateTimeParseException ex) {
+            // ignore
+        }
+
+        final int sizeX = Math.abs(claim.getGreaterBoundaryCorner().getX() - claim.getLesserBoundaryCorner().getX()) + 1;
+        final int sizeY = Math.abs(claim.getGreaterBoundaryCorner().getY() - claim.getLesserBoundaryCorner().getY()) + 1;
+        final int sizeZ = Math.abs(claim.getGreaterBoundaryCorner().getZ() - claim.getLesserBoundaryCorner().getZ()) + 1;
+        Component claimSize = TextComponent.empty();
+        if (claim.isCuboid()) {
+            claimSize = TextComponent.builder(" ")
+                    .append(MessageCache.getInstance().LABEL_AREA.color(TextColor.YELLOW))
+                    .append(": ", TextColor.YELLOW)
+                    .append(sizeX + "x" + sizeY + "x" + sizeZ, TextColor.GRAY).build();
+        } else {
+            claimSize = TextComponent.builder(" ")
+                    .append(MessageCache.getInstance().LABEL_AREA.color(TextColor.YELLOW))
+                    .append(": ", TextColor.YELLOW)
+                    .append(sizeX + "x" + sizeZ, TextColor.GRAY).build();
+        }
+        final Component claimCost = TextComponent.builder("  ")
+                .append(MessageCache.getInstance().LABEL_BLOCKS.color(TextColor.YELLOW))
+                .append(": ", TextColor.YELLOW)
+                .append(String.valueOf(claim.getClaimBlocks()), TextColor.GRAY).build();
+        if (claim.isWilderness() && name == null) {
+            name = TextComponent.of("Wilderness", TextColor.GREEN);
+        }
+        Component claimName = TextComponent.builder()
+                .append(MessageCache.getInstance().LABEL_NAME.color(TextColor.YELLOW))
+                .append(" : ", TextColor.YELLOW)
+                .append(name == null ? NONE : name).build();
+        Component worldName = TextComponent.builder()
+                .append(MessageCache.getInstance().LABEL_WORLD.color(TextColor.YELLOW))
+                .append(" : ")
+                .append(gdClaim.getWorld().getName(), TextColor.GRAY).build();
+        if (!claim.isWilderness() && !claim.isAdminClaim()) {
+            claimName = TextComponent.builder()
+                    .append(claimName)
+                    .append("  ")
+                    .append(worldName)
+                    .append(claimSize)
+                    .append(claimCost).build();
+        }
+        // users
+        final List<UUID> accessorList = gdClaim.getUserTrustList(TrustTypes.ACCESSOR, true);
+        final List<UUID> builderList = gdClaim.getUserTrustList(TrustTypes.BUILDER, true);
+        final List<UUID> containerList = gdClaim.getUserTrustList(TrustTypes.CONTAINER, true);
+        final List<UUID> managerList = gdClaim.getUserTrustList(TrustTypes.MANAGER, true);
+        for (UUID uuid : accessorList) {
+            final GDPermissionUser user = PermissionHolderCache.getInstance().getOrCreateUser(uuid);
+            final String userName = user.getFriendlyName();
+            if (userName != null) {
+                accessors += userName + " ";
+            }
+        }
+        for (UUID uuid : builderList) {
+            final GDPermissionUser user = PermissionHolderCache.getInstance().getOrCreateUser(uuid);
+            final String userName = user.getFriendlyName();
+            if (userName != null) {
+                builders += userName + " ";
+            }
+        }
+        for (UUID uuid : containerList) {
+            final GDPermissionUser user = PermissionHolderCache.getInstance().getOrCreateUser(uuid);
+            final String userName = user.getFriendlyName();
+            if (userName != null) {
+                containers += userName + " ";
+            }
+        }
+        for (UUID uuid : managerList) {
+            final GDPermissionUser user = PermissionHolderCache.getInstance().getOrCreateUser(uuid);
+            final String userName = user.getFriendlyName();
+            if (userName != null) {
+                managers += userName + " ";
+            }
+        }
+
+        // groups
+        for (String group : gdClaim.getGroupTrustList(TrustTypes.ACCESSOR, true)) {
+            accessorGroups += group + " ";
+        }
+        for (String group : gdClaim.getGroupTrustList(TrustTypes.BUILDER, true)) {
+            builderGroups += group + " ";
+        }
+        for (String group : gdClaim.getGroupTrustList(TrustTypes.CONTAINER, true)) {
+            containerGroups += group + " ";
+        }
+        for (String group : gdClaim.getGroupTrustList(TrustTypes.MANAGER, true)) {
+            managerGroups += group + " ";
+        }
+
+        /*if (gpClaim.isInTown()) {
+            Text returnToClaimInfo = Text.builder().append(Text.of(
+                    TextColors.WHITE, "\n[", TextColors.AQUA, "Return to standard settings", TextColors.WHITE, "]\n"))
+                .onClick(TextActions.executeCallback(CommandHelper.createCommandConsumer(src, "claiminfo", ""))).build();
+            Text townName = Text.of(TextColors.YELLOW, "Name", TextColors.WHITE, " : ", TextColors.RESET,
+                    gpClaim.getTownClaim().getTownData().getName().orElse(NONE));
+            Text townTag = Text.of(TextColors.YELLOW, "Tag", TextColors.WHITE, " : ", TextColors.RESET,
+                    gpClaim.getTownClaim().getTownData().getTownTag().orElse(NONE));
+            townTextList.add(returnToClaimInfo);
+            townTextList.add(townName);
+            townTextList.add(townTag);
+            Text townSettings = Text.builder()
+                    .append(Text.of(TextStyles.ITALIC, TextColors.GREEN, TOWN_SETTINGS))
+                    .onClick(TextActions.executeCallback(createSettingsConsumer(src, claim, townTextList, ClaimTypes.TOWN)))
+                    .onHover(TextActions.showText(Text.of("Click here to view town settings")))
+                    .build();
+            textList.add(townSettings);
+        }*/
+
+        if (isAdmin) {
+            Component adminSettings = TextComponent.builder()
+                    .append(MessageCache.getInstance().CLAIMINFO_UI_ADMIN_SETTINGS).decoration(TextDecoration.ITALIC, true).color(TextColor.RED)
+                    .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createSettingsConsumer(src, claim, generateAdminSettings(src, gdClaim), ClaimTypes.ADMIN))))
+                    .hoverEvent(HoverEvent.showText(MessageCache.getInstance().CLAIMINFO_UI_CLICK_ADMIN))
+                    .build();
+            textList.add(adminSettings);
+        }
+
+        Component bankInfo = null;
+        Component forSaleText = null;
+        if (GriefDefenderPlugin.getInstance().economyService.isPresent()) {
+             if (GriefDefenderPlugin.getActiveConfig(gdClaim.getWorld().getProperties()).getConfig().claim.bankTaxSystem) {
+                 bankInfo = TextComponent.builder()
+                         .append(MessageCache.getInstance().CLAIMINFO_UI_BANK_INFO.color(TextColor.GOLD).decoration(TextDecoration.ITALIC, true))
+                         .hoverEvent(HoverEvent.showText(MessageCache.getInstance().CLAIMINFO_UI_BANK_INFO))
+                         .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(Consumer -> { CommandHelper.displayClaimBankInfo(src, gdClaim, gdClaim.isTown() ? true : false, true); })))
+                         .build();
+             }
+             forSaleText = TextComponent.builder()
+                     .append(MessageCache.getInstance().CLAIMINFO_UI_FOR_SALE.color(TextColor.YELLOW))
+                     .append(" : ")
+                     .append(getClickableInfoText(src, claim, FOR_SALE, claim.getEconomyData().isForSale() ? MessageCache.getInstance().LABEL_YES.color(TextColor.GREEN) : MessageCache.getInstance().LABEL_NO.color(TextColor.GRAY))).build();
+             if (claim.getEconomyData().isForSale()) {
+                 forSaleText = TextComponent.builder()
+                         .append(forSaleText)
+                         .append("  ")
+                         .append(MessageCache.getInstance().LABEL_PRICE.color(TextColor.YELLOW))
+                         .append(" : ")
+                         .append(String.valueOf(claim.getEconomyData().getSalePrice()), TextColor.GOLD)
+                         .build();
+             }
+        }
+
+        Component claimId = TextComponent.builder()
+                .append("UUID", TextColor.YELLOW)
+                .append(" : ")
+                .append(TextComponent.builder()
+                        .append(claim.getUniqueId().toString(), TextColor.GRAY)
+                        .insertion(claim.getUniqueId().toString()).build()).build();
+        final String ownerName = PlayerUtil.getInstance().getUserName(ownerUniqueId);
+        Component ownerLine = TextComponent.builder()
+                .append(MessageCache.getInstance().LABEL_OWNER.color(TextColor.YELLOW))
+                .append(" : ")
+                .append(ownerName != null && !claim.isAdminClaim() && !claim.isWilderness() ? ownerName : "administrator", TextColor.GOLD).build();
+        Component adminShowText = TextComponent.empty();
+        Component basicShowText = TextComponent.empty();
+        Component subdivisionShowText = TextComponent.empty();
+        Component townShowText = TextComponent.empty();
+        Component claimType = TextComponent.empty();
+        final Component whiteOpenBracket = TextComponent.of("[");
+        final Component whiteCloseBracket = TextComponent.of("]");
+        Component defaultTypeText = TextComponent.builder()
+                .append(whiteOpenBracket)
+                .append(gdClaim.getFriendlyNameType(true))
+                .append(whiteCloseBracket).build();
+        if (allowEdit != null && !isAdmin) {
+            adminShowText = allowEdit;
+            basicShowText = allowEdit;
+            subdivisionShowText = allowEdit;
+            townShowText = allowEdit;
+            Component adminTypeText = TextComponent.builder()
+                    .append(claim.getType() == ClaimTypes.ADMIN ? 
+                            defaultTypeText : TextComponent.of("ADMIN", TextColor.GRAY))
+                    .hoverEvent(HoverEvent.showText(adminShowText)).build();
+            Component basicTypeText = TextComponent.builder()
+                    .append(claim.getType() == ClaimTypes.BASIC ? 
+                            defaultTypeText : TextComponent.of("BASIC", TextColor.GRAY))
+                    .hoverEvent(HoverEvent.showText(basicShowText)).build();
+            Component subTypeText = TextComponent.builder()
+                    .append(claim.getType() == ClaimTypes.SUBDIVISION ? 
+                            defaultTypeText : TextComponent.of("SUBDIVISION", TextColor.GRAY))
+                    .hoverEvent(HoverEvent.showText(subdivisionShowText)).build();
+            Component townTypeText = TextComponent.builder()
+                    .append(claim.getType() == ClaimTypes.TOWN ? 
+                            defaultTypeText : TextComponent.of("TOWN", TextColor.GRAY))
+                    .hoverEvent(HoverEvent.showText(townShowText)).build();
+            claimType = TextComponent.builder()
+                    .append(claim.isCuboid() ? "3D " : "2D ", TextColor.GREEN)
+                    .append(adminTypeText)
+                    .append(" ")
+                    .append(basicTypeText)
+                    .append(" ")
+                    .append(subTypeText)
+                    .append(" ")
+                    .append(townTypeText)
+                    .build();
+        } else {
+            Component adminTypeText = defaultTypeText;
+            Component basicTypeText = defaultTypeText;
+            Component subTypeText = defaultTypeText;
+            Component townTypeText = defaultTypeText;
+            if (!claim.isAdminClaim()) {
+                final Component message = ((GDClaim) claim).validateClaimType(ClaimTypes.ADMIN, ownerUniqueId, playerData).getMessage().orElse(null);
+                adminShowText = message != null ? message : MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.CLAIMINFO_UI_CLICK_CHANGE_CLAIM,
+                        ImmutableMap.of("type", TextComponent.of("ADMIN ", TextColor.RED)));
+
+                if (message == null) {
+                    adminTypeText = TextComponent.builder()
+                        .append(claim.getType() == ClaimTypes.ADMIN ? 
+                                defaultTypeText : TextComponent.of("ADMIN", TextColor.GRAY))
+                        .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createClaimTypeConsumer(src, claim, ClaimTypes.ADMIN, isAdmin))))
+                        .hoverEvent(HoverEvent.showText(adminShowText)).build();
+                } else {
+                    adminTypeText = TextComponent.builder()
+                        .append(claim.getType() == ClaimTypes.ADMIN ? 
+                                defaultTypeText : TextComponent.of("ADMIN", TextColor.GRAY))
+                        .hoverEvent(HoverEvent.showText(adminShowText)).build();
+                }
+            }
+            if (!claim.isBasicClaim()) {
+                final Component message = ((GDClaim) claim).validateClaimType(ClaimTypes.BASIC, ownerUniqueId, playerData).getMessage().orElse(null);
+                basicShowText = message != null ? message : MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.CLAIMINFO_UI_CLICK_CHANGE_CLAIM,
+                        ImmutableMap.of("type", TextComponent.of("BASIC ", TextColor.YELLOW)));
+
+                if (message == null) {
+                    basicTypeText = TextComponent.builder()
+                            .append(claim.getType() == ClaimTypes.BASIC ? defaultTypeText : TextComponent.of("BASIC", TextColor.GRAY))
+                            .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createClaimTypeConsumer(src, claim, ClaimTypes.BASIC, isAdmin))))
+                            .hoverEvent(HoverEvent.showText(basicShowText)).build();
+                } else {
+                    basicTypeText = TextComponent.builder()
+                            .append(claim.getType() == ClaimTypes.BASIC ? defaultTypeText : TextComponent.of("BASIC", TextColor.GRAY))
+                            .hoverEvent(HoverEvent.showText(basicShowText)).build();
+                }
+            }
+            if (!claim.isSubdivision()) {
+                final Component message = ((GDClaim) claim).validateClaimType(ClaimTypes.SUBDIVISION, ownerUniqueId, playerData).getMessage().orElse(null);
+                subdivisionShowText = message != null ? message : MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.CLAIMINFO_UI_CLICK_CHANGE_CLAIM,
+                        ImmutableMap.of("type", TextComponent.of("SUBDIVISION ", TextColor.AQUA)));
+
+                if (message == null) {
+                    subTypeText = TextComponent.builder()
+                            .append(claim.getType() == ClaimTypes.SUBDIVISION ? defaultTypeText : TextComponent.of("SUBDIVISION", TextColor.GRAY))
+                            .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createClaimTypeConsumer(src, claim, ClaimTypes.SUBDIVISION, isAdmin))))
+                            .hoverEvent(HoverEvent.showText(subdivisionShowText)).build();
+                } else {
+                    subTypeText = TextComponent.builder()
+                            .append(claim.getType() == ClaimTypes.SUBDIVISION ? defaultTypeText : TextComponent.of("SUBDIVISION", TextColor.GRAY))
+                            .hoverEvent(HoverEvent.showText(subdivisionShowText)).build();
+                }
+            }
+            if (!claim.isTown()) {
+                final Component message = ((GDClaim) claim).validateClaimType(ClaimTypes.TOWN, ownerUniqueId, playerData).getMessage().orElse(null);
+                townShowText = message != null ? message : MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.CLAIMINFO_UI_CLICK_CHANGE_CLAIM,
+                        ImmutableMap.of("type", TextComponent.of("TOWN ", TextColor.GREEN)));
+
+                if (message == null) {
+                    townTypeText = TextComponent.builder()
+                            .append(claim.getType() == ClaimTypes.TOWN ? defaultTypeText : TextComponent.of("TOWN", TextColor.GRAY))
+                            .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createClaimTypeConsumer(src, claim, ClaimTypes.TOWN, isAdmin))))
+                            .hoverEvent(HoverEvent.showText(townShowText)).build();
+                } else {
+                    townTypeText = TextComponent.builder()
+                            .append(claim.getType() == ClaimTypes.TOWN ? defaultTypeText : TextComponent.of("TOWN", TextColor.GRAY))
+                            .hoverEvent(HoverEvent.showText(townShowText)).build();
+                }
+            }
+
+            claimType = TextComponent.builder()
+                    .append(claim.isCuboid() ? "3D " : "2D ", TextColor.GREEN)
+                    .append(adminTypeText)
+                    .append(" ")
+                    .append(basicTypeText)
+                    .append(" ")
+                    .append(subTypeText)
+                    .append(" ")
+                    .append(townTypeText)
+                    .build();
+        }
+
+        Component claimTypeInfo = TextComponent.builder()
+                .append(MessageCache.getInstance().LABEL_TYPE.color(TextColor.YELLOW))
+                .append(" : ")
+                .append(claimType).build();
+        Component claimInherit = TextComponent.builder()
+                .append(MessageCache.getInstance().LABEL_INHERIT.color(TextColor.YELLOW))
+                .append(" : ")
+                .append(getClickableInfoText(src, claim, INHERIT_PARENT, claim.getData().doesInheritParent() ? TextComponent.of("ON", TextColor.GREEN) : TextComponent.of("OFF", TextColor.RED))).build();
+        Component claimExpired = TextComponent.builder()
+                .append(MessageCache.getInstance().LABEL_EXPIRED.color(TextColor.YELLOW))
+                .append(" : ")
+                .append(claim.getData().isExpired() ? TextComponent.of("YES", TextColor.RED) : TextComponent.of("NO", TextColor.GRAY)).build();
+        Component claimFarewell = TextComponent.builder()
+                .append(MessageCache.getInstance().LABEL_FAREWELL.color(TextColor.YELLOW))
+                .append(" : ")
+                .append(farewell == null ? NONE : farewell).build();
+        Component claimGreeting = TextComponent.builder()
+                .append(MessageCache.getInstance().LABEL_GREETING.color(TextColor.YELLOW))
+                .append(" : ")
+                .append(greeting == null ? NONE : greeting).build();
+        Component claimDenyMessages = TextComponent.builder()
+                .append(MessageCache.getInstance().CLAIMINFO_UI_DENY_MESSAGES.color(TextColor.YELLOW))
+                .append(" : ")
+                .append(getClickableInfoText(src, claim, DENY_MESSAGES, claim.getData().allowDenyMessages() ? TextComponent.of("ON", TextColor.GREEN) : TextComponent.of("OFF", TextColor.RED))).build();
+        Component pvpSetting = TextComponent.of("UNDEFINED", TextColor.GRAY);
+        if (claim.getData().getPvpOverride() == Tristate.TRUE) {
+            pvpSetting = TextComponent.of("ON", TextColor.GREEN);
+        } else if (claim.getData().getPvpOverride() == Tristate.FALSE) {
+            pvpSetting = TextComponent.of("OFF", TextColor.RED);
+        }
+        Component claimPvP = TextComponent.builder()
+                .append("PvP", TextColor.YELLOW)
+                .append(" : ")
+                .append(getClickableInfoText(src, claim, PVP_OVERRIDE, pvpSetting)).build();
+       /* Component claimRaid = TextComponent.builder()
+                .append("Raid", TextColor.YELLOW)
+                .append(" : ")
+                .append(getClickableInfoText(src, claim, RAID_OVERRIDE, GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Boolean.class), owner, Options.RAID, gdClaim) == true ? TextComponent.of("ON", TextColor.GREEN) : TextComponent.of("OFF", TextColor.RED))).build();
+                */
+        Component claimSpawn = null;
+        if (claim.getData().getSpawnPos().isPresent()) {
+            Vector3i spawnPos = claim.getData().getSpawnPos().get();
+            Location<World> spawnLoc = new Location<>(gdClaim.getWorld(), spawnPos.getX(), spawnPos.getY(), spawnPos.getZ());
+            claimSpawn = TextComponent.builder()
+                    .append(MessageCache.getInstance().LABEL_SPAWN.color(TextColor.GREEN))
+                    .append(" : ")
+                    .append(spawnPos.toString(), TextColor.GRAY)
+                    .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(CommandHelper.createTeleportConsumer(player, spawnLoc, claim, true))))
+                    .hoverEvent(HoverEvent.showText(MessageCache.getInstance().CLAIMINFO_UI_TELEPORT_SPAWN))
+                    .build();
+        }
+        Component southWestCorner = TextComponent.builder()
+                .append("SW", TextColor.LIGHT_PURPLE)
+                .append(" : ")
+                .append(VecHelper.toVector3i(southWest).toString(), TextColor.GRAY)
+                .append(" ")
+                .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(CommandHelper.createTeleportConsumer(player, southWest, claim))))
+                .hoverEvent(HoverEvent.showText(MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.CLAIMINFO_UI_TELEPORT_DIRECTION, 
+                        ImmutableMap.of("direction", TextComponent.of("SW").color(TextColor.AQUA)))))
+                .build();
+        Component southEastCorner = TextComponent.builder()
+                .append("SE", TextColor.LIGHT_PURPLE)
+                .append(" : ")
+                .append(VecHelper.toVector3i(southEast).toString(), TextColor.GRAY)
+                .append(" ")
+                .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(CommandHelper.createTeleportConsumer(player, southEast, claim))))
+                .hoverEvent(HoverEvent.showText(MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.CLAIMINFO_UI_TELEPORT_DIRECTION, 
+                        ImmutableMap.of("direction", TextComponent.of("SE").color(TextColor.AQUA)))))
+                .build();
+        Component southCorners = TextComponent.builder()
+                .append(MessageCache.getInstance().CLAIMINFO_UI_SOUTH_CORNERS.color(TextColor.YELLOW))
+                .append(" : ")
+                .append(southWestCorner)
+                .append(southEastCorner).build();
+        Component northWestCorner = TextComponent.builder()
+                .append("NW", TextColor.LIGHT_PURPLE)
+                .append(" : ")
+                .append(VecHelper.toVector3i(northWest).toString(), TextColor.GRAY)
+                .append(" ")
+                .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(CommandHelper.createTeleportConsumer(player, northWest, claim))))
+                .hoverEvent(HoverEvent.showText(MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.CLAIMINFO_UI_TELEPORT_DIRECTION, 
+                        ImmutableMap.of("direction", TextComponent.of("NW").color(TextColor.AQUA)))))
+                .build();
+        Component northEastCorner = TextComponent.builder()
+                .append("NE", TextColor.LIGHT_PURPLE)
+                .append(" : ")
+                .append(VecHelper.toVector3i(northEast).toString(), TextColor.GRAY)
+                .append(" ")
+                .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(CommandHelper.createTeleportConsumer(player, northEast, claim))))
+                .hoverEvent(HoverEvent.showText(MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.CLAIMINFO_UI_TELEPORT_DIRECTION, 
+                        ImmutableMap.of("direction", TextComponent.of("NE").color(TextColor.AQUA)))))
+                .build();
+        Component northCorners = TextComponent.builder()
+                .append(MessageCache.getInstance().CLAIMINFO_UI_NORTH_CORNERS.color(TextColor.YELLOW))
+                .append(" : ")
+                .append(northWestCorner)
+                .append(northEastCorner).build();
+        Component claimAccessors = TextComponent.builder()
+                .append(MessageCache.getInstance().LABEL_ACCESSORS.color(TextColor.YELLOW))
+                .append(" : ")
+                .append(accessors.equals("") ? NONE : TextComponent.of(accessors, TextColor.BLUE))
+                .append(" ")
+                .append(accessorGroups, TextColor.LIGHT_PURPLE).build();
+        Component claimBuilders = TextComponent.builder()
+                .append(MessageCache.getInstance().LABEL_BUILDERS.color(TextColor.YELLOW))
+                .append(" : ")
+                .append(builders.equals("") ? NONE : TextComponent.of(builders, TextColor.BLUE))
+                .append(" ")
+                .append(builderGroups, TextColor.LIGHT_PURPLE).build();
+        Component claimContainers = TextComponent.builder()
+                .append(MessageCache.getInstance().LABEL_CONTAINERS.color(TextColor.YELLOW))
+                .append(" : ")
+                .append(containers.equals("") ? NONE : TextComponent.of(containers, TextColor.BLUE))
+                .append(" ")
+                .append(containerGroups, TextColor.LIGHT_PURPLE).build();
+        Component claimCoowners = TextComponent.builder()
+                .append(MessageCache.getInstance().LABEL_MANAGERS.color(TextColor.YELLOW))
+                .append(" : ")
+                .append(managers.equals("") ? NONE : TextComponent.of(managers, TextColor.BLUE))
+                .append(" ")
+                .append(managerGroups, TextColor.LIGHT_PURPLE).build();
+        Component dateCreated = TextComponent.builder()
+                .append(MessageCache.getInstance().LABEL_CREATED.color(TextColor.YELLOW))
+                .append(" : ")
+                .append(created != null ? TextComponent.of(created.toString(), TextColor.GRAY) : MessageCache.getInstance().LABEL_UNKNOWN.color(TextColor.GRAY)).build();
+        Component dateLastActive = TextComponent.builder()
+                .append(MessageCache.getInstance().CLAIMINFO_UI_LAST_ACTIVE.color(TextColor.YELLOW))
+                .append(" : ")
+                .append(lastActive != null ? TextComponent.of(lastActive.toString(), TextColor.GRAY) : MessageCache.getInstance().LABEL_UNKNOWN.color(TextColor.GRAY)).build();
+
+        if (claimSpawn != null) {
+            textList.add(claimSpawn);
+        }
+        if (bankInfo != null) {
+            textList.add(bankInfo);
+        }
+        textList.add(claimName);
+        textList.add(ownerLine);
+        textList.add(claimTypeInfo);
+        if (!claim.isAdminClaim() && !claim.isWilderness()) {
+            textList.add(TextComponent.builder()
+                    .append(claimInherit)
+                    .append("   ")
+                    .append(claimExpired).build());
+            if (forSaleText != null) {
+                textList.add(forSaleText);
+            }
+        }
+        textList.add(TextComponent.builder()
+                .append(claimPvP)
+                .append("   ")
+                .append(claimDenyMessages)
+                .build());
+        textList.add(claimAccessors);
+        textList.add(claimBuilders);
+        textList.add(claimContainers);
+        textList.add(claimCoowners);
+        textList.add(claimGreeting);
+        textList.add(claimFarewell);
+        textList.add(dateCreated);
+        textList.add(dateLastActive);
+        textList.add(claimId);
+        textList.add(northCorners);
+        textList.add(southCorners);
+        if (!claim.getParent().isPresent()) {
+            textList.remove(claimInherit);
+        }
+        if (claim.isAdminClaim()) {
+            textList.remove(bankInfo);
+            textList.remove(dateLastActive);
+        }
+        if (claim.isWilderness()) {
+            textList.remove(bankInfo);
+            textList.remove(claimInherit);
+            textList.remove(claimTypeInfo);
+            textList.remove(dateLastActive);
+            textList.remove(northCorners);
+            textList.remove(southCorners);
+        }
+
+        PaginationList.Builder paginationBuilder = PaginationList.builder()
+                .title(MessageCache.getInstance().CLAIMINFO_UI_TITLE_CLAIMINFO.color(TextColor.AQUA)).padding(TextComponent.of(" ").decoration(TextDecoration.STRIKETHROUGH, true)).contents(textList);
+        paginationBuilder.sendTo(src);
+    }
+
+    public static Consumer<CommandSource> createSettingsConsumer(CommandSource src, Claim claim, List<Component> textList, ClaimType type) {
+        return settings -> {
+            Component name = type == ClaimTypes.TOWN ? MessageCache.getInstance().CLAIMINFO_UI_TOWN_SETTINGS : MessageCache.getInstance().CLAIMINFO_UI_ADMIN_SETTINGS;
+            PaginationList.Builder paginationBuilder = PaginationList.builder()
+                    .title(name.color(TextColor.AQUA)).padding(TextComponent.of(" ").decoration(TextDecoration.STRIKETHROUGH, true)).contents(textList);
+            paginationBuilder.sendTo(src);
+        };
+    }
+
+    private List<Component> generateAdminSettings(CommandSource src, GDClaim claim) {
+        List<Component> textList = new ArrayList<>();
+        Component returnToClaimInfo = TextComponent.builder()
+                .append("\n[")
+                .append(MessageCache.getInstance().CLAIMINFO_UI_RETURN_SETTINGS.color(TextColor.AQUA))
+                .append("]\n")
+            .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(CommandHelper.createCommandConsumer(src, "claiminfo", claim.getUniqueId().toString())))).build();
+        Component claimResizable = TextComponent.builder()
+                .append(MessageCache.getInstance().LABEL_RESIZABLE.color(TextColor.YELLOW))
+                .append(" : ")
+                .append(getClickableInfoText(src, claim, RESIZABLE, claim.getInternalClaimData().isResizable() ? TextComponent.of("ON", TextColor.GREEN) : TextComponent.of("OFF", TextColor.RED))).build();
+        Component claimRequiresClaimBlocks = TextComponent.builder()
+                .append(MessageCache.getInstance().CLAIMINFO_UI_REQUIRES_CLAIM_BLOCKS.color(TextColor.YELLOW))
+                .append(" : ")
+                .append(getClickableInfoText(src, claim, REQUIRES_CLAIM_BLOCKS, claim.getInternalClaimData().requiresClaimBlocks() ? TextComponent.of("ON", TextColor.GREEN) : TextComponent.of("OFF", TextColor.RED))).build();
+        Component claimSizeRestrictions = TextComponent.builder()
+                .append(MessageCache.getInstance().CLAIMINFO_UI_SIZE_RESTRICTIONS.color(TextColor.YELLOW))
+                .append(" : ")
+                .append(getClickableInfoText(src, claim, SIZE_RESTRICTIONS, claim.getInternalClaimData().hasSizeRestrictions() ? TextComponent.of("ON", TextColor.GREEN) : TextComponent.of("OFF", TextColor.RED))).build();
+        Component claimExpiration = TextComponent.builder()
+                .append(MessageCache.getInstance().CLAIMINFO_UI_CLAIM_EXPIRATION.color(TextColor.YELLOW))
+                .append(" : ")
+                .append(getClickableInfoText(src, claim, CLAIM_EXPIRATION, claim.getInternalClaimData().allowExpiration() ? TextComponent.of("ON", TextColor.GREEN) : TextComponent.of("OFF", TextColor.RED))).build();
+        Component claimFlagOverrides = TextComponent.builder()
+                .append(MessageCache.getInstance().CLAIMINFO_UI_FLAG_OVERRIDES.color(TextColor.YELLOW))
+                .append(" : ")
+                .append(getClickableInfoText(src, claim, FLAG_OVERRIDES, claim.getInternalClaimData().allowFlagOverrides() ? TextComponent.of("ON", TextColor.GREEN) : TextComponent.of("OFF", TextColor.RED))).build();
+        textList.add(returnToClaimInfo);
+        if (!claim.isAdminClaim() && !claim.isWilderness()) {
+            textList.add(claimRequiresClaimBlocks);
+            textList.add(claimExpiration);
+            textList.add(claimResizable);
+            textList.add(claimSizeRestrictions);
+        }
+        textList.add(claimFlagOverrides);
+        int fillSize = 20 - (textList.size() + 4);
+        for (int i = 0; i < fillSize; i++) {
+            textList.add(TextComponent.of(" "));
+        }
+        return textList;
+    }
+
+    private void executeAdminSettings(CommandSource src, GDClaim claim) {
+        PaginationList.Builder paginationBuilder = PaginationList.builder()
+                .title(MessageCache.getInstance().CLAIMINFO_UI_ADMIN_SETTINGS).padding(TextComponent.of(" ").decoration(TextDecoration.STRIKETHROUGH, true)).contents(generateAdminSettings(src, claim));
+        paginationBuilder.sendTo(src);
+    }
+
+    public Component getClickableInfoText(CommandSource src, Claim claim, int titleId, Component infoText) {
+        Component onClickText = MessageCache.getInstance().CLAIMINFO_UI_CLICK_TOGGLE;
+        boolean hasPermission = true;
+        if (src instanceof Player) {
+            Component denyReason = ((GDClaim) claim).allowEdit((Player) src);
+            if (denyReason != null) {
+                onClickText = denyReason;
+                hasPermission = false;
+            }
+        }
+
+        TextComponent.Builder textBuilder = TextComponent.builder()
+                .append(infoText)
+                .hoverEvent(HoverEvent.showText(onClickText));
+        if (hasPermission) {
+            textBuilder.clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createClaimInfoConsumer(src, claim, titleId))));
+        }
+        return textBuilder.build();
+    }
+
+    private Consumer<CommandSource> createClaimInfoConsumer(CommandSource src, Claim claim, int titleId) {
+        GDClaim gpClaim = (GDClaim) claim;
+        return info -> {
+            switch (titleId) {
+                case INHERIT_PARENT : 
+                    if (!claim.getParent().isPresent() || !src.hasPermission(GDPermissions.COMMAND_CLAIM_INHERIT)) {
+                        return;
+                    }
+
+                    gpClaim.getInternalClaimData().setInheritParent(!gpClaim.getInternalClaimData().doesInheritParent());
+                    gpClaim.getInternalClaimData().setRequiresSave(true);
+                    claim.getData().save();
+                    CommandHelper.executeCommand(src, "claiminfo", gpClaim.getUniqueId().toString());
+                    return;
+                case CLAIM_EXPIRATION :
+                    gpClaim.getInternalClaimData().setExpiration(!gpClaim.getInternalClaimData().allowExpiration());
+                    gpClaim.getInternalClaimData().setRequiresSave(true);
+                    gpClaim.getClaimStorage().save();
+                    break;
+                case DENY_MESSAGES :
+                    gpClaim.getInternalClaimData().setDenyMessages(!gpClaim.getInternalClaimData().allowDenyMessages());
+                    gpClaim.getInternalClaimData().setRequiresSave(true);
+                    gpClaim.getClaimStorage().save();
+                    CommandHelper.executeCommand(src, "claiminfo", gpClaim.getUniqueId().toString());
+                    return;
+                case FLAG_OVERRIDES :
+                    gpClaim.getInternalClaimData().setFlagOverrides(!gpClaim.getInternalClaimData().allowFlagOverrides());
+                    gpClaim.getInternalClaimData().setRequiresSave(true);
+                    gpClaim.getClaimStorage().save();
+                    break;
+                case PVP_OVERRIDE :
+                    Tristate value = gpClaim.getInternalClaimData().getPvpOverride();
+                    if (value == Tristate.UNDEFINED) {
+                        gpClaim.getInternalClaimData().setPvpOverride(Tristate.TRUE);
+                    } else if (value == Tristate.TRUE) {
+                        gpClaim.getInternalClaimData().setPvpOverride(Tristate.FALSE);
+                    } else {
+                        gpClaim.getInternalClaimData().setPvpOverride(Tristate.UNDEFINED);
+                    }
+                    gpClaim.getInternalClaimData().setRequiresSave(true);
+                    gpClaim.getClaimStorage().save();
+                    CommandHelper.executeCommand(src, "claiminfo", gpClaim.getUniqueId().toString());
+                    return;
+                /*case RAID_OVERRIDE :
+                    GDPermissionHolder holder = null;
+                    final GDPlayerData playerData = ((GDClaim) claim).getOwnerPlayerData();
+                    if (playerData == null) {
+                        holder = GriefDefenderPlugin.DEFAULT_HOLDER;
+                    } else {
+                        holder = playerData.getSubject();
+                    }
+                    final Boolean result = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Boolean.class), holder, Options.RAID, gpClaim);
+                    final Set<Context> contexts = new HashSet<>();
+                    contexts.add(claim.getContext());
+                    if (result) {
+                        PermissionUtil.getInstance().setOptionValue(holder, Options.RAID.getPermission(), "false", contexts);
+                    } else {
+                        PermissionUtil.getInstance().setOptionValue(holder, Options.RAID.getPermission(), "true", contexts);
+                    }
+                    CommandHelper.executeCommand(src, "claiminfo", gpClaim.getUniqueId().toString());
+                    return;*/
+                case RESIZABLE :
+                    boolean resizable = gpClaim.getInternalClaimData().isResizable();
+                    gpClaim.getInternalClaimData().setResizable(!resizable);
+                    gpClaim.getInternalClaimData().setRequiresSave(true);
+                    gpClaim.getClaimStorage().save();
+                    break;
+                case REQUIRES_CLAIM_BLOCKS :
+                    boolean requiresClaimBlocks = gpClaim.getInternalClaimData().requiresClaimBlocks();
+                    gpClaim.getInternalClaimData().setRequiresClaimBlocks(!requiresClaimBlocks);
+                    gpClaim.getInternalClaimData().setRequiresSave(true);
+                    gpClaim.getClaimStorage().save();
+                    break;
+                case SIZE_RESTRICTIONS :
+                    boolean sizeRestrictions = gpClaim.getInternalClaimData().hasSizeRestrictions();
+                    gpClaim.getInternalClaimData().setSizeRestrictions(!sizeRestrictions);
+                    gpClaim.getInternalClaimData().setRequiresSave(true);
+                    gpClaim.getClaimStorage().save();
+                    break;
+                case FOR_SALE :
+                    boolean forSale = gpClaim.getEconomyData().isForSale();
+                    gpClaim.getEconomyData().setForSale(!forSale);
+                    gpClaim.getInternalClaimData().setRequiresSave(true);
+                    gpClaim.getClaimStorage().save();
+                    CommandHelper.executeCommand(src, "claiminfo", gpClaim.getUniqueId().toString());
+                    return;
+                default:
+            }
+            executeAdminSettings(src, gpClaim);
+        };
+    }
+
+    private static Consumer<CommandSource> createClaimTypeConsumer(CommandSource src, Claim gpClaim, ClaimType clicked, boolean isAdmin) {
+        GDClaim claim = (GDClaim) gpClaim;
+        return type -> {
+            if (!(src instanceof Player)) {
+                // ignore
+                return;
+            }
+
+            final Player player = (Player) src;
+            if (!isAdmin && ((GDClaim) gpClaim).allowEdit(player) != null) {
+                TextAdapter.sendComponent(src, MessageCache.getInstance().CLAIM_NOT_YOURS);
+                return;
+            }
+            final ClaimResult result = claim.changeType(clicked, Optional.of(player.getUniqueId()), src);
+            if (result.successful()) {
+                CommandHelper.executeCommand(src, "claiminfo", gpClaim.getUniqueId().toString());
+            } else {
+                TextAdapter.sendComponent(src, result.getMessage().get());
+            }
+        };
+    }
+}
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimInherit.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimInherit.java
new file mode 100644
index 0000000..2c37418
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimInherit.java
@@ -0,0 +1,69 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.permission.GDPermissions;
+import net.kyori.text.Component;
+import org.spongepowered.api.entity.living.player.Player;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_CLAIM_INHERIT)
+public class CommandClaimInherit extends BaseCommand {
+
+    @CommandAlias("claiminherit")
+    @Description("Toggles subdivision inherit mode.")
+    @Subcommand("claim inherit")
+    public void execute(Player player) {
+        final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        final GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(playerData, player.getLocation());
+        final Component result = claim.allowEdit(player);
+        if (result != null) {
+            GriefDefenderPlugin.sendMessage(player, result);
+            return;
+        }
+
+        if (claim.parent == null) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().COMMAND_INHERIT_ONLY_CHILD);
+            return;
+        }
+        claim.getData().setInheritParent(!claim.getData().doesInheritParent());
+        claim.getInternalClaimData().setRequiresSave(true);
+
+        if (!claim.getData().doesInheritParent()) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().COMMAND_CLAIMINHERIT_DISABLED);
+        } else {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().COMMAND_CLAIMINHERIT_ENABLED);
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimList.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimList.java
new file mode 100644
index 0000000..a1c8eaa
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimList.java
@@ -0,0 +1,246 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandCompletion;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Optional;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+import com.github.benmanes.caffeine.cache.Cache;
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.ClaimType;
+import com.griefdefender.api.claim.ClaimTypes;
+import com.griefdefender.api.claim.TrustTypes;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.claim.GDClaimManager;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.internal.pagination.PaginationList;
+import com.griefdefender.permission.GDPermissionUser;
+import com.griefdefender.permission.GDPermissions;
+import com.griefdefender.text.action.GDCallbackHolder;
+import com.griefdefender.util.PaginationUtil;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.event.ClickEvent;
+import net.kyori.text.event.HoverEvent;
+import net.kyori.text.format.TextColor;
+import net.kyori.text.format.TextDecoration;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.command.CommandSource;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.entity.living.player.User;
+import org.spongepowered.api.world.World;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_CLAIM_LIST)
+public class CommandClaimList extends BaseCommand {
+
+    private final ClaimType forcedType;
+    private final Cache<UUID, String> lastActiveClaimTypeMap = Caffeine.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES)
+            .build();
+
+    public CommandClaimList() {
+        this.forcedType = null;
+    }
+
+    public CommandClaimList(ClaimType type) {
+        this.forcedType = type;
+    }
+
+    @CommandCompletion("@gdplayers @gdworlds @gddummy")
+    @CommandAlias("claimlist|claimslist")
+    @Syntax("[<player>|<player> <world>]")
+    @Description("List information about a player's claim blocks and claims.")
+    @Subcommand("claim list")
+    public void execute(Player src, @Optional User targetPlayer, @Optional World world) {//String[] args) {
+        final GDPermissionUser user = targetPlayer == null ? PermissionHolderCache.getInstance().getOrCreateUser(src) : PermissionHolderCache.getInstance().getOrCreateUser(targetPlayer);
+        if (user == null) {
+            GriefDefenderPlugin.sendMessage(src, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.COMMAND_INVALID_PLAYER,
+                    ImmutableMap.of(
+                    "player", targetPlayer)));
+            return;
+        }
+        if (world == null) {
+            world = src.getWorld();
+        }
+
+        showClaimList(src, user, this.forcedType, world.getUniqueId());
+    }
+
+    private void showClaimList(Player src, GDPermissionUser user, ClaimType type, UUID worldUniqueId) {
+        List<Component> claimsTextList = new ArrayList<>();
+        Set<Claim> claims = new HashSet<>();
+        final String worldName = worldUniqueId == null ? "" : Sponge.getServer().getWorld(worldUniqueId).get().getName();
+        final boolean otherUser = !src.getUniqueId().equals(user.getUniqueId());
+        for (World world : Sponge.getServer().getWorlds()) {
+            if (type != null && !world.getUniqueId().equals(worldUniqueId)) {
+                continue;
+            }
+            final GDClaimManager claimWorldManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(world.getUniqueId());
+            // load the target player's data
+            final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(world, user.getUniqueId());
+            Set<Claim> claimList = null;
+            if (type == null || otherUser) {
+                claimList = playerData.getClaims();
+            } else {
+                claimList = claimWorldManager.getWorldClaims();
+            }
+
+            for (Claim claim : claimList) {
+                if (claims.contains(claim)) {
+                    continue;
+                }
+
+                if (((GDClaim) claim).allowEdit(src) != null && !claim.isUserTrusted(src.getUniqueId(), TrustTypes.ACCESSOR)) {
+                    continue;
+                }
+                if (type == null) {
+                    claims.add(claim);
+                } else {
+                    if (claim.getType() == type) {
+                        claims.add(claim);
+                    } else if (type == ClaimTypes.SUBDIVISION) {
+                        for (Claim child : claim.getChildren(true)) {
+                            if (child.getType() == type) {
+                                claims.add(child);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        if (src instanceof Player) {
+            final Player player = (Player) src;
+            final String lastClaimType = this.lastActiveClaimTypeMap.getIfPresent(player.getUniqueId());
+            String currentType = type == null ? "OWN" : type.toString();
+            if (lastClaimType != null && !lastClaimType.equals(currentType.toString())) {
+                PaginationUtil.getInstance().resetActivePage(player.getUniqueId());
+            }
+        }
+        claimsTextList = CommandHelper.generateClaimTextListCommand(claimsTextList, claims, worldName, user, src, createClaimListConsumer(src, user, type, worldUniqueId), false);
+
+        final Component whiteOpenBracket = TextComponent.of("[");
+        final Component whiteCloseBracket = TextComponent.of("]");
+        Component ownedShowText = MessageCache.getInstance().CLAIMLIST_UI_CLICK_VIEW_CLAIMS;
+        Component adminShowText = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.UI_CLICK_FILTER_TYPE,
+                ImmutableMap.of("type", TextComponent.of("ADMIN", TextColor.RED)));
+        Component basicShowText = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.UI_CLICK_FILTER_TYPE,
+                ImmutableMap.of("type", TextComponent.of("BASIC", TextColor.YELLOW)));
+        Component subdivisionShowText = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.UI_CLICK_FILTER_TYPE,
+                ImmutableMap.of("type", TextComponent.of("SUBDIVISION", TextColor.AQUA)));
+        Component townShowText = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.UI_CLICK_FILTER_TYPE,
+                ImmutableMap.of("type", TextComponent.of("TOWN", TextColor.GREEN)));
+        Component ownedTypeText = TextComponent.builder("")
+                .append(type == null ? 
+                        TextComponent.builder("")
+                        .append(whiteOpenBracket)
+                        .append(otherUser ? TextComponent.of(user.getFriendlyName()).color(TextColor.GOLD) : MessageCache.getInstance().TITLE_OWN.color(TextColor.GOLD))
+                        .append(whiteCloseBracket).build() : otherUser ? TextComponent.of(user.getFriendlyName()).color(TextColor.GRAY) : MessageCache.getInstance().TITLE_OWN.color(TextColor.GRAY))
+                .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createClaimListConsumer(src, user, null, worldUniqueId))))
+                .hoverEvent(HoverEvent.showText(ownedShowText)).build();
+        Component adminTypeText = TextComponent.builder("")
+                .append(type == ClaimTypes.ADMIN ? TextComponent.builder("")
+                        .append(whiteOpenBracket)
+                        .append("ADMIN", TextColor.RED)
+                        .append(whiteCloseBracket).build() : TextComponent.of("ADMIN", TextColor.GRAY))
+                .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createClaimListConsumer(src, user, ClaimTypes.ADMIN, worldUniqueId))))
+                .hoverEvent(HoverEvent.showText(adminShowText)).build();
+        Component basicTypeText = TextComponent.builder("")
+                .append(type == ClaimTypes.BASIC ? TextComponent.builder("")
+                        .append(whiteOpenBracket)
+                        .append("BASIC", TextColor.YELLOW)
+                        .append(whiteCloseBracket).build() : TextComponent.of("BASIC", TextColor.GRAY))
+                .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createClaimListConsumer(src, user, ClaimTypes.BASIC, worldUniqueId))))
+                .hoverEvent(HoverEvent.showText(basicShowText)).build();
+        Component subTypeText = TextComponent.builder("")
+                .append(type == ClaimTypes.SUBDIVISION ? TextComponent.builder("")
+                        .append(whiteOpenBracket)
+                        .append("SUBDIVISION", TextColor.AQUA)
+                        .append(whiteCloseBracket).build() : TextComponent.of("SUBDIVISION", TextColor.GRAY))
+                .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createClaimListConsumer(src, user, ClaimTypes.SUBDIVISION, worldUniqueId))))
+                .hoverEvent(HoverEvent.showText(subdivisionShowText)).build();
+        Component townTypeText = TextComponent.builder("")
+                .append(type == ClaimTypes.TOWN ? TextComponent.builder("")
+                        .append(whiteOpenBracket)
+                        .append("TOWN", TextColor.GREEN)
+                        .append(whiteCloseBracket).build() : TextComponent.of("TOWN", TextColor.GRAY))
+                .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createClaimListConsumer(src, user, ClaimTypes.TOWN, worldUniqueId))))
+                .hoverEvent(HoverEvent.showText(townShowText)).build();
+        Component claimListHead = TextComponent.builder("")
+                .append(" ")
+                .append(MessageCache.getInstance().LABEL_DISPLAYING.color(TextColor.AQUA))
+                .append(" : ", TextColor.AQUA)
+                .append(ownedTypeText)
+                .append(" ")
+                .append(otherUser ? TextComponent.of("") : adminTypeText)
+                .append(otherUser ? "" : " ")
+                .append(basicTypeText)
+                .append(" ")
+                .append(subTypeText)
+                .append(" ")
+                .append(townTypeText).build();
+        final int fillSize = 20 - (claimsTextList.size() + 2);
+        for (int i = 0; i < fillSize; i++) {
+            claimsTextList.add(TextComponent.of(" "));
+        }
+
+        PaginationList paginationList = PaginationList.builder()
+                .title(claimListHead).padding(TextComponent.of(" ").decoration(TextDecoration.STRIKETHROUGH, true)).contents(claimsTextList).build();
+        Integer activePage = 1;
+        if (src instanceof Player) {
+            final Player player = (Player) src;
+            activePage = PaginationUtil.getInstance().getActivePage(player.getUniqueId());
+            if (activePage == null) {
+                activePage = 1;
+            }
+            this.lastActiveClaimTypeMap.put(player.getUniqueId(), type == null ? "OWN" : type.toString());
+        }
+        paginationList.sendTo(src, activePage);
+    }
+
+    private Consumer<CommandSource> createClaimListConsumer(Player src, GDPermissionUser user, ClaimType type, UUID worldUniqueId) {
+        return consumer -> {
+            showClaimList(src, user, type, worldUniqueId);
+        };
+    }
+}
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimMode.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimMode.java
new file mode 100644
index 0000000..64daa22
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimMode.java
@@ -0,0 +1,78 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import net.kyori.text.Component;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+
+import org.spongepowered.api.entity.living.player.Player;
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.claim.ClaimBlockSystem;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.permission.GDPermissions;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_CLAIM_MODE)
+public class CommandClaimMode extends BaseCommand {
+
+    @CommandAlias("claim|claimmode")
+    @Description("Toggles claim mode creation. Note: This will default to basic claim mode.")
+    @Subcommand("mode claim")
+    public void execute(Player player) {
+        final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        playerData.claimMode = !playerData.claimMode;
+        playerData.claimSubdividing = null;
+        if (!playerData.claimMode) {
+            playerData.revertActiveVisual(player);
+            // check for any active WECUI visuals
+            if (GriefDefenderPlugin.getInstance().getWorldEditProvider() != null) {
+                GriefDefenderPlugin.getInstance().getWorldEditProvider().revertVisuals(player, playerData, null);
+            }
+            TextAdapter.sendComponent(player, MessageCache.getInstance().COMMAND_CLAIMMODE_DISABLED);
+        } else {
+            TextAdapter.sendComponent(player, MessageCache.getInstance().COMMAND_CLAIMMODE_ENABLED);
+            if (GriefDefenderPlugin.CLAIM_BLOCK_SYSTEM == ClaimBlockSystem.VOLUME) {
+                final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.PLAYER_REMAINING_BLOCKS_3D,
+                        ImmutableMap.of(
+                        "block-amount", playerData.getRemainingClaimBlocks(),
+                        "chunk-amount", playerData.getRemainingChunks()));
+                GriefDefenderPlugin.sendMessage(player, message);
+            } else {
+                final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.PLAYER_REMAINING_BLOCKS_2D,
+                       ImmutableMap.of(
+                        "block-amount", playerData.getRemainingClaimBlocks()));
+                GriefDefenderPlugin.sendMessage(player, message);
+            }
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimName.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimName.java
new file mode 100644
index 0000000..f3b0ed8
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimName.java
@@ -0,0 +1,74 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.permission.GDPermissions;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.serializer.legacy.LegacyComponentSerializer;
+import org.spongepowered.api.entity.living.player.Player;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_SET_CLAIM_NAME)
+public class CommandClaimName extends BaseCommand {
+
+    @CommandAlias("claimname")
+    @Syntax("<name>")
+    @Description("Sets the name of your claim.")
+    @Subcommand("claim name")
+    public void execute(Player player, String name) {
+        final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        final GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(playerData, player.getLocation());
+        final Component result = claim.allowEdit(player);
+        if (result != null) {
+            GriefDefenderPlugin.sendMessage(player, result);
+            return;
+        }
+
+        final Component text = LegacyComponentSerializer.legacy().deserialize(name, '&');
+        if (text == TextComponent.empty()) {
+            claim.getInternalClaimData().setName(null);
+        } else {
+            claim.getInternalClaimData().setName(text);
+        }
+        claim.getInternalClaimData().setRequiresSave(true);
+        claim.getInternalClaimData().save();
+        final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.CLAIM_NAME,
+                ImmutableMap.of(
+                "name", text));
+        GriefDefenderPlugin.sendMessage(player, message);
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimOption.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimOption.java
new file mode 100644
index 0000000..cf5cc6b
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimOption.java
@@ -0,0 +1,85 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.InvalidCommandArgument;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandCompletion;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Optional;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.claim.ClaimContexts;
+import com.griefdefender.api.permission.Context;
+import com.griefdefender.api.permission.option.Option;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.internal.pagination.PaginationList;
+import com.griefdefender.permission.GDPermissionHolder;
+import com.griefdefender.permission.GDPermissionManager;
+import com.griefdefender.permission.GDPermissions;
+import com.griefdefender.registry.OptionRegistryModule;
+import com.griefdefender.util.CauseContextHelper;
+import com.griefdefender.util.PermissionUtil;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import net.kyori.text.format.TextColor;
+import net.kyori.text.format.TextDecoration;
+import org.spongepowered.api.command.CommandException;
+import org.spongepowered.api.entity.living.player.Player;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_OPTIONS_CLAIM)
+public class CommandClaimOption extends ClaimOptionBase {
+
+    public CommandClaimOption() {
+        super(ClaimSubjectType.GLOBAL);
+    }
+
+    @CommandCompletion("@gdoptions @gddummy")
+    @CommandAlias("co|claimoption")
+    @Description("Gets/Sets claim options in the claim you are standing in.")
+    @Syntax("[<option> <value> [context[key=value]]")
+    @Subcommand("option claim")
+    public void execute(Player player, @Optional String[] args) throws InvalidCommandArgument {
+        this.subject = GriefDefenderPlugin.DEFAULT_HOLDER;
+        this.friendlySubjectName = "ALL";
+        super.execute(player, args);
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimOptionGroup.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimOptionGroup.java
new file mode 100644
index 0000000..a4c8832
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimOptionGroup.java
@@ -0,0 +1,74 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.InvalidCommandArgument;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandCompletion;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Optional;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.permission.GDPermissions;
+import com.griefdefender.util.PermissionUtil;
+import net.kyori.text.Component;
+import org.spongepowered.api.entity.living.player.Player;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_OPTIONS_GROUP)
+public class CommandClaimOptionGroup extends ClaimOptionBase {
+
+    public CommandClaimOptionGroup() {
+        super(ClaimSubjectType.GROUP);
+    }
+
+    @CommandCompletion("@gdgroups @gdoptions @gddummy")
+    @CommandAlias("cog")
+    @Description("Gets/Sets option for a group in claim you are standing in.")
+    @Syntax("<group> <option> <value> [context[key=value]]")
+    @Subcommand("option group")
+    public void execute(Player player, String group, @Optional String[] args) throws InvalidCommandArgument {
+        if (args.length < 2 || args.length > 3) {
+            throw new InvalidCommandArgument();
+        }
+
+        if (!PermissionUtil.getInstance().hasGroupSubject(group)) {
+            final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.COMMAND_INVALID_GROUP, ImmutableMap.of(
+                    "group", group));
+            GriefDefenderPlugin.sendMessage(player, message);
+            return;
+        }
+
+        this.subject = PermissionHolderCache.getInstance().getOrCreateHolder(group);
+        this.friendlySubjectName = group;
+
+        super.execute(player, args);
+    }
+}
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimOptionPlayer.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimOptionPlayer.java
new file mode 100644
index 0000000..5b42da4
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimOptionPlayer.java
@@ -0,0 +1,58 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.InvalidCommandArgument;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandCompletion;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Optional;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.permission.GDPermissions;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.entity.living.player.User;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_OPTIONS_PLAYER)
+public class CommandClaimOptionPlayer extends ClaimOptionBase {
+
+    public CommandClaimOptionPlayer() {
+        super(ClaimSubjectType.PLAYER);
+    }
+
+    @CommandCompletion("@gdplayers @gdoptions @gddummy")
+    @CommandAlias("cop")
+    @Description("Gets/Sets option for a player in claim you are standing in.")
+    @Syntax("<player> <option> <value> [context[key=value]]")
+    @Subcommand("option player")
+    public void execute(Player src, User player, @Optional String[] args) throws InvalidCommandArgument {
+        this.subject = PermissionHolderCache.getInstance().getOrCreateUser(player);
+        this.friendlySubjectName = player.getName();
+        super.execute(src, args);
+    }
+}
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimPermissionGroup.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimPermissionGroup.java
new file mode 100644
index 0000000..0c8ff67
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimPermissionGroup.java
@@ -0,0 +1,147 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.InvalidCommandArgument;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandCompletion;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Optional;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.Tristate;
+import com.griefdefender.api.permission.Context;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.internal.pagination.PaginationList;
+import com.griefdefender.permission.GDPermissionHolder;
+import com.griefdefender.permission.GDPermissions;
+import com.griefdefender.permission.ui.UIHelper;
+import com.griefdefender.util.PermissionUtil;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import net.kyori.text.format.TextColor;
+import net.kyori.text.format.TextDecoration;
+import org.spongepowered.api.command.CommandException;
+import org.spongepowered.api.entity.living.player.Player;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_CLAIM_PERMISSION_GROUP)
+public class CommandClaimPermissionGroup extends BaseCommand {
+
+    @CommandCompletion("@gdgroups @gddummy")
+    @CommandAlias("cpg")
+    @Description("Sets a permission on a group with a claim context.")
+    @Syntax("<group> [<permission> <value>]")
+    @Subcommand("permission group")
+    public void execute(Player player, String group, @Optional String[] args) throws CommandException, InvalidCommandArgument {
+        String permission = null;
+        String value = null;
+        final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        if (args.length > 0) {
+            if (args.length < 2) {
+                throw new InvalidCommandArgument();
+            }
+            permission = args[0];
+            if (!playerData.ignoreClaims && permission != null && !player.hasPermission(permission)) {
+                GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().PERMISSION_ASSIGN_WITHOUT_HAVING);
+                return;
+            }
+    
+            value = args[1];
+        }
+
+        if (!PermissionUtil.getInstance().hasGroupSubject(group)) {
+            GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.COMMAND_INVALID_GROUP));
+            return;
+        }
+
+        final GDPermissionHolder subj = PermissionHolderCache.getInstance().getOrCreateHolder(group);
+        GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(playerData, player.getLocation());
+        final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.PERMISSION_CLAIM_MANAGE,
+                ImmutableMap.of(
+                "type", claim.getType().getName()));
+        if (claim.isWilderness() && !playerData.canManageWilderness) {
+            GriefDefenderPlugin.sendMessage(player, message);
+            return;
+        } else if (claim.isAdminClaim() && !playerData.canManageAdminClaims) {
+            GriefDefenderPlugin.sendMessage(player, message);
+            return;
+        }
+
+        Set<Context> contexts = new HashSet<>();
+        contexts.add(claim.getContext());
+        if (permission == null || value == null) {
+            List<Component> permList = Lists.newArrayList();
+            Map<String, Boolean> permissions = PermissionUtil.getInstance().getPermissions(subj, contexts);
+            for (Map.Entry<String, Boolean> permissionEntry : permissions.entrySet()) {
+                Boolean permValue = permissionEntry.getValue();
+                Component permText = TextComponent.builder("")
+                        .append(permissionEntry.getKey(), TextColor.GREEN)
+                        .append("  ")
+                        .append(permValue.toString(), TextColor.GOLD).build();
+                permList.add(permText);
+            }
+
+            List<Component> finalTexts = UIHelper.stripeText(permList);
+
+            PaginationList.Builder paginationBuilder = PaginationList.builder()
+                    .title(TextComponent.of(subj.getFriendlyName() + " Permissions", TextColor.AQUA)).padding(TextComponent.of(" ").decoration(TextDecoration.STRIKETHROUGH, true)).contents(finalTexts);
+            paginationBuilder.sendTo(player);
+            return;
+        }
+
+        Tristate tristateValue = PermissionUtil.getInstance().getTristateFromString(value);
+        if (tristateValue == null) {
+            TextAdapter.sendComponent(player, TextComponent.of("Invalid value entered. '" + value + "' is not a valid value. Valid values are : true, false, undefined, 1, -1, or 0.", TextColor.RED));
+            return;
+        }
+
+        PermissionUtil.getInstance().setPermissionValue(subj, permission, tristateValue, contexts);
+        TextAdapter.sendComponent(player, TextComponent.builder("")
+                .append("Set permission ")
+                .append(permission, TextColor.AQUA)
+                .append(" to ")
+                .append(value, TextColor.GREEN)
+                .append(" on group ")
+                .append(subj.getFriendlyName(), TextColor.GOLD)
+                .append(".").build());
+    }
+
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimPermissionPlayer.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimPermissionPlayer.java
new file mode 100644
index 0000000..d10f950
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimPermissionPlayer.java
@@ -0,0 +1,144 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.InvalidCommandArgument;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandCompletion;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Optional;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.Tristate;
+import com.griefdefender.api.permission.Context;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.internal.pagination.PaginationList;
+import com.griefdefender.permission.GDPermissionHolder;
+import com.griefdefender.permission.GDPermissions;
+import com.griefdefender.permission.ui.UIHelper;
+import com.griefdefender.util.PermissionUtil;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import net.kyori.text.format.TextColor;
+import net.kyori.text.format.TextDecoration;
+import org.spongepowered.api.command.CommandException;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.entity.living.player.User;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_CLAIM_PERMISSION_PLAYER)
+public class CommandClaimPermissionPlayer extends BaseCommand {
+
+    @CommandCompletion("@gdplayers @gddummy")
+    @CommandAlias("cpp")
+    @Description("Sets a permission on a player with a claim context.")
+    @Syntax("<player> [<permission> <value>]")
+    @Subcommand("permission player")
+    public void execute(Player player, User user, @Optional String[] args) throws CommandException, InvalidCommandArgument {
+        String permission = null;
+        String value = null;
+        final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        if (args.length > 0) {
+            if (args.length < 2) {
+                throw new InvalidCommandArgument();
+            }
+            permission = args[0];
+            if (!playerData.ignoreClaims && permission != null && !player.hasPermission(permission)) {
+                GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().PERMISSION_ASSIGN_WITHOUT_HAVING);
+                return;
+            }
+    
+            value = args[1];
+        }
+
+        final GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(playerData, player.getLocation());
+        final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.PERMISSION_CLAIM_MANAGE,
+                ImmutableMap.of(
+                "type", claim.getType().getName()));
+        if (claim.isWilderness() && !playerData.canManageWilderness) {
+            GriefDefenderPlugin.sendMessage(player, message);
+            return;
+        } else if (claim.isAdminClaim() && !playerData.canManageAdminClaims) {
+            GriefDefenderPlugin.sendMessage(player, message);
+            return;
+        }
+
+        final GDPermissionHolder holder = PermissionHolderCache.getInstance().getOrCreateUser(player);
+        Set<Context> contexts = new HashSet<>();
+        contexts.add(claim.getContext());
+        if (permission == null || value == null) {
+            // display current permissions for user
+            List<Component> permList = Lists.newArrayList();
+            Map<String, Boolean> permissions = PermissionUtil.getInstance().getPermissions(holder, contexts);
+            for (Map.Entry<String, Boolean> permissionEntry : permissions.entrySet()) {
+                Boolean permValue = permissionEntry.getValue();
+                Component permText = TextComponent.builder("")
+                        .append(permissionEntry.getKey(), TextColor.GREEN)
+                        .append("  ")
+                        .append(permValue.toString(), TextColor.GOLD).build();
+                permList.add(permText);
+            }
+
+            List<Component> finalTexts = UIHelper.stripeText(permList);
+
+            PaginationList.Builder paginationBuilder = PaginationList.builder()
+                    .title(TextComponent.of(user.getName() + " Permissions", TextColor.AQUA)).padding(TextComponent.of(" ").decoration(TextDecoration.STRIKETHROUGH, true)).contents(finalTexts);
+            paginationBuilder.sendTo(player);
+            return;
+        }
+
+        Tristate tristateValue = PermissionUtil.getInstance().getTristateFromString(value);
+        if (tristateValue == null) {
+            TextAdapter.sendComponent(player, TextComponent.of("Invalid value entered. '" + value + "' is not a valid value. Valid values are : true, false, undefined, 1, -1, or 0.", TextColor.RED));
+            return;
+        }
+
+        PermissionUtil.getInstance().setPermissionValue(holder, permission, tristateValue, contexts);
+        TextAdapter.sendComponent(player, TextComponent.builder("")
+                .append("Set permission ")
+                .append(permission, TextColor.AQUA)
+                .append(" to ")
+                .append(value, TextColor.GREEN)
+                .append(" on user ")
+                .append(user.getName(), TextColor.GOLD)
+                .append(".").build());
+    }
+
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimSchematic.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimSchematic.java
new file mode 100644
index 0000000..59f7151
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimSchematic.java
@@ -0,0 +1,154 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.InvalidCommandArgument;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Optional;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.ClaimSchematic;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.internal.pagination.PaginationList;
+import com.griefdefender.permission.GDPermissions;
+import com.griefdefender.text.action.GDCallbackHolder;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import net.kyori.text.event.ClickEvent;
+import net.kyori.text.event.HoverEvent;
+import net.kyori.text.format.TextColor;
+import net.kyori.text.format.TextDecoration;
+import org.spongepowered.api.command.CommandException;
+import org.spongepowered.api.command.CommandSource;
+import org.spongepowered.api.entity.living.player.Player;
+
+import java.sql.Date;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_CLAIM_SCHEMATIC)
+public class CommandClaimSchematic extends BaseCommand {
+
+    @CommandAlias("claimschematic")
+    @Description("Manages claim schematics. Use '/claimschematic create <name>' to create a live backup of claim.")
+    @Syntax("<create|delete> <name>")
+    @Subcommand("schematic")
+    public void execute(Player player, @Optional String[] args) throws CommandException, InvalidCommandArgument {
+        if (GriefDefenderPlugin.getInstance().getWorldEditProvider() == null) {
+            TextAdapter.sendComponent(player, MessageCache.getInstance().COMMAND_WORLDEDIT_MISSING);
+            return;
+        }
+
+        String action = null;
+        String name = null;
+        if (args.length > 0) {
+            action = args[0];
+            if (args.length < 2) {
+                throw new InvalidCommandArgument();
+            }
+            name = args[1];
+        }
+
+        GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(playerData, player.getLocation());
+        final Component denyMessage = claim.allowEdit(player);
+        if (denyMessage != null) {
+            GriefDefenderPlugin.sendMessage(player, denyMessage);
+            return;
+        }
+
+        if (action == null) {
+            if (claim.schematics.isEmpty()) {
+                TextAdapter.sendComponent(player, GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.SCHEMATIC_NONE));
+                return;
+            }
+
+            List<Component> schematicTextList = new ArrayList<>();
+            for (ClaimSchematic schematic : claim.schematics.values()) {
+                final String schematicName = schematic.getName();
+                final Instant schematicDate = schematic.getDateCreated();
+                schematicTextList.add(
+                        TextComponent.builder("")
+                        .append(schematicName, TextColor.GREEN)
+                        .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(displayConfirmationConsumer(player, claim, schematic))))
+                        .hoverEvent(HoverEvent.showText(GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.SCHEMATIC_RESTORE_CLICK, 
+                        ImmutableMap.of(
+                            "name", TextComponent.of(schematicName, TextColor.GREEN),
+                            "date", TextComponent.of(Date.from(schematicDate).toString(), TextColor.AQUA)))))
+                        .build());
+            }
+
+            PaginationList.Builder paginationBuilder = PaginationList.builder()
+                    .title(TextComponent.of("Schematics", TextColor.AQUA)).padding(TextComponent.of(" ").decoration(TextDecoration.STRIKETHROUGH, true)).contents(schematicTextList);
+            paginationBuilder.sendTo(player);
+        } else if (action.equalsIgnoreCase("create")) {
+            TextAdapter.sendComponent(player, TextComponent.of("Creating schematic backup...", TextColor.GREEN));
+            final ClaimSchematic schematic = ClaimSchematic.builder().claim(claim).name(name).build().orElse(null);
+            if (schematic != null) {
+                TextAdapter.sendComponent(player, TextComponent.of("Schematic backup complete.", TextColor.GREEN));
+            } else {
+                TextAdapter.sendComponent(player, TextComponent.of("Schematic could not be created.", TextColor.GREEN));
+            }
+        } else if (action.equalsIgnoreCase("delete")) {
+            claim.deleteSchematic(name);
+            TextAdapter.sendComponent(player, GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.SCHEMATIC_DELETED, 
+                ImmutableMap.of("name", name)));
+        }
+    }
+
+    private static Consumer<CommandSource> displayConfirmationConsumer(CommandSource src, Claim claim, ClaimSchematic schematic) {
+        return confirm -> {
+            final Component schematicConfirmationText = TextComponent.builder("")
+                    .append("\n[")
+                    .append("Confirm", TextColor.GREEN)
+                    .append("]\n")
+                    .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createConfirmationConsumer(src, claim, schematic))))
+                    .hoverEvent(HoverEvent.showText(GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.SCHEMATIC_RESTORE_CONFIRMATION))).build();
+            TextAdapter.sendComponent(src, schematicConfirmationText);
+        };
+    }
+
+    private static Consumer<CommandSource> createConfirmationConsumer(CommandSource src, Claim claim, ClaimSchematic schematic) {
+        return confirm -> {
+            schematic.apply();
+            final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.SCHEMATIC_RESTORE_CONFIRMED, 
+                ImmutableMap.of("name", schematic.getName()));
+            GriefDefenderPlugin.sendMessage(src, message);
+        };
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimSell.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimSell.java
new file mode 100644
index 0000000..f9ec8a2
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimSell.java
@@ -0,0 +1,129 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.InvalidCommandArgument;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.permission.GDPermissions;
+import com.griefdefender.text.action.GDCallbackHolder;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.event.ClickEvent;
+import net.kyori.text.format.TextColor;
+import org.spongepowered.api.command.CommandSource;
+import org.spongepowered.api.entity.living.player.Player;
+
+import java.util.function.Consumer;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_CLAIM_SELL)
+public class CommandClaimSell extends BaseCommand {
+
+    @CommandAlias("claimsell")
+    @Description("Puts your claim up for sale. Use /claimsell amount.\nNote: Requires economy plugin.")
+    @Syntax("<amount> | cancel")
+    @Subcommand("sell claim")
+    public void execute(Player player, String arg) throws InvalidCommandArgument {
+        if (!GriefDefenderPlugin.getInstance().economyService.isPresent()) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().ECONOMY_NOT_INSTALLED);
+            return;
+        }
+
+        final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        final GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAt(player.getLocation());
+
+        if (claim.isAdminClaim() || claim.isWilderness()) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().ECONOMY_CLAIM_NOT_FOR_SALE);
+            return;
+        }
+
+        if (!playerData.canIgnoreClaim(claim) && !player.getUniqueId().equals(claim.getOwnerUniqueId())) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().PERMISSION_CLAIM_SALE);
+            return;
+        }
+
+        Double salePrice = null;
+        if (!claim.getEconomyData().isForSale()) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().ECONOMY_CLAIM_NOT_FOR_SALE);
+            return;
+        }
+        if (arg.equalsIgnoreCase("cancel")) {
+            claim.getEconomyData().setForSale(false);
+            claim.getEconomyData().setSalePrice(-1);
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().ECONOMY_CLAIM_SALE_CANCELLED);
+            return;
+        } else {
+            try {
+                salePrice = Double.parseDouble(arg);
+            } catch (NumberFormatException e) {
+                throw new InvalidCommandArgument();
+            }
+        }
+
+        if (salePrice == null || salePrice < 0) {
+            final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.ECONOMY_CLAIM_SALE_INVALID_PRICE,
+                    ImmutableMap.of(
+                    "amount", salePrice));
+            GriefDefenderPlugin.sendMessage(player, message);
+            return;
+        }
+
+        final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.ECONOMY_CLAIM_SALE_CONFIRMATION,
+                ImmutableMap.of(
+                "amount", salePrice));
+        GriefDefenderPlugin.sendMessage(player, message);
+
+        final Component saleConfirmationText = TextComponent.builder("")
+                .append("\n[")
+                .append("Confirm", TextColor.GREEN)
+                .append("]\n")
+                .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createSaleConfirmationConsumer(player, claim, salePrice))))
+                .build();
+        GriefDefenderPlugin.sendMessage(player, saleConfirmationText);
+    }
+
+    private static Consumer<CommandSource> createSaleConfirmationConsumer(CommandSource src, Claim claim, double price) {
+        return confirm -> {
+            claim.getEconomyData().setSalePrice(price);
+            claim.getEconomyData().setForSale(true);
+            claim.getData().save();
+            final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.ECONOMY_CLAIM_SALE_CONFIRMED,
+                    ImmutableMap.of("amount", price));
+            GriefDefenderPlugin.sendMessage(src, message);
+        };
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimSellBlocks.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimSellBlocks.java
new file mode 100644
index 0000000..6df996d
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimSellBlocks.java
@@ -0,0 +1,154 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Optional;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.claim.ClaimResultType;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.claim.GDClaimResult;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.permission.GDPermissions;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import net.kyori.text.format.TextColor;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.event.CauseStackManager;
+import org.spongepowered.api.service.economy.Currency;
+import org.spongepowered.api.service.economy.account.Account;
+import org.spongepowered.api.service.economy.transaction.ResultType;
+import org.spongepowered.api.service.economy.transaction.TransactionResult;
+
+import java.math.BigDecimal;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_SELL_CLAIM_BLOCKS)
+public class CommandClaimSellBlocks extends BaseCommand {
+
+    @CommandAlias("sellclaim|sellclaimblocks|sellblocks")
+    @Description("Sell your claim blocks for server money.\nNote: Requires economy plugin.")
+    @Syntax("[<amount>]")
+    @Subcommand("sell blocks")
+    public void execute(Player player, @Optional Integer blockCount) {
+        final boolean economyMode = GriefDefenderPlugin.getInstance().isEconomyModeEnabled();
+        // if economy is disabled, don't do anything
+        if (!GriefDefenderPlugin.getInstance().economyService.isPresent()) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().ECONOMY_NOT_INSTALLED);
+            return;
+        }
+
+        final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        if (playerData.getEconomyClaimBlockReturn() <= 0) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().ECONOMY_BLOCK_BUY_SELL_DISABLED);
+            return;
+        }
+
+        // if selling disabled, send error message
+        if (playerData.getEconomyClaimBlockReturn() == 0) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().ECONOMY_BLOCK_ONLY_BUY);
+            return;
+        }
+
+        final Account playerAccount = GriefDefenderPlugin.getInstance().economyService.get().getOrCreateAccount(player.getUniqueId()).orElse(null);
+        if (playerAccount == null) {
+            final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.ECONOMY_PLAYER_NOT_FOUND, ImmutableMap.of(
+                    "player", player.getName()));
+            GriefDefenderPlugin.sendMessage(player, message);
+            return;
+        }
+
+        int availableBlocks = economyMode ? playerData.getAccruedClaimBlocks() + playerData.getBonusClaimBlocks() : playerData.getInternalRemainingClaimBlocks();
+        if (blockCount == null) {
+            final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.ECONOMY_BLOCK_PURCHASE_COST,
+                    ImmutableMap.of(
+                    "amount", playerData.getEconomyClaimBlockReturn(),
+                    "balance", availableBlocks));
+            GriefDefenderPlugin.sendMessage(player, message);
+            return;
+        } else {
+            // try to parse number of blocks
+            if (blockCount <= 0) {
+                GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().ECONOMY_BLOCK_BUY_INVALID);
+                return;
+            } else if (blockCount > availableBlocks) {
+                GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().ECONOMY_BLOCK_NOT_AVAILABLE);
+                return;
+            }
+
+            // attempt to compute value and deposit it
+            double economyTotalValue = blockCount * playerData.getEconomyClaimBlockReturn();
+            final Currency defaultCurrency = GriefDefenderPlugin.getInstance().economyService.get().getDefaultCurrency();
+            final TransactionResult result = playerAccount.deposit(defaultCurrency, BigDecimal.valueOf(economyTotalValue), Sponge.getCauseStackManager().getCurrentCause());
+            if (result.getResult() != ResultType.SUCCESS) {
+                final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.ECONOMY_BLOCK_SELL_ERROR, ImmutableMap.of(
+                        "reason", result.getResult()));
+                GriefDefenderPlugin.sendMessage(player, message);
+                return;
+            }
+
+            final BigDecimal currentFunds = playerAccount.getBalance(defaultCurrency);
+            Component message = null;
+            if (economyMode) {
+                message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.ECONOMY_MODE_BLOCK_SALE_CONFIRMATION,
+                        ImmutableMap.of(
+                        "deposit", economyTotalValue,
+                        "balance", String.valueOf("$" + currentFunds),
+                        "amount", playerData.getRemainingClaimBlocks()));
+            } else {
+                message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.ECONOMY_BLOCK_SALE_CONFIRMATION,
+                    ImmutableMap.of(
+                    "deposit", economyTotalValue,
+                    "amount", playerData.getRemainingClaimBlocks()));
+            }
+            int bonusBlocks = playerData.getBonusClaimBlocks();
+            int accruedBlocks = playerData.getAccruedClaimBlocks();
+            if (bonusBlocks > 0) {
+                if (bonusBlocks >= blockCount) {
+                    bonusBlocks = (int) (bonusBlocks - blockCount);
+                    playerData.setBonusClaimBlocks(bonusBlocks);
+                } else {
+                    int remaining = (int) (blockCount - bonusBlocks);
+                    playerData.setBonusClaimBlocks(0);
+                    playerData.setAccruedClaimBlocks(playerData.getAccruedClaimBlocks() - remaining);
+                }
+            } else {
+                accruedBlocks = (int) (accruedBlocks - blockCount);
+                playerData.setAccruedClaimBlocks(accruedBlocks);
+            }
+            playerData.saveAllData();
+            GriefDefenderPlugin.sendMessage(player, message);
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimSetSpawn.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimSetSpawn.java
new file mode 100644
index 0000000..78b3a41
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimSetSpawn.java
@@ -0,0 +1,66 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import com.flowpowered.math.vector.Vector3i;
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.internal.util.VecHelper;
+import com.griefdefender.permission.GDPermissions;
+import net.kyori.text.Component;
+import org.spongepowered.api.entity.living.player.Player;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_CLAIM_SET_SPAWN)
+public class CommandClaimSetSpawn extends BaseCommand {
+
+    @CommandAlias("claimsetspawn")
+    @Description("Sets the spawn of claim.")
+    @Subcommand("claim setspawn")
+    public void execute(Player player) {
+        final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        final GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(playerData, player.getLocation());
+        final Component result = claim.allowEdit(player);
+        if (result != null) {
+            GriefDefenderPlugin.sendMessage(player, result);
+            return;
+        }
+
+        final Vector3i pos = VecHelper.toVector3i(player.getLocation());
+        claim.getInternalClaimData().setSpawnPos(pos);
+        final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.SPAWN_SET_SUCCESS,
+                ImmutableMap.of(
+                "location", pos));
+        GriefDefenderPlugin.sendMessage(player, message);
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimSpawn.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimSpawn.java
new file mode 100644
index 0000000..36a5a8f
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimSpawn.java
@@ -0,0 +1,120 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Optional;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+
+import com.flowpowered.math.vector.Vector3i;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.reflect.TypeToken;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.TrustTypes;
+import com.griefdefender.api.permission.option.Options;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.permission.GDPermissionManager;
+import com.griefdefender.permission.GDPermissions;
+import net.kyori.text.Component;
+import net.kyori.text.serializer.plain.PlainComponentSerializer;
+
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.entity.living.player.User;
+import org.spongepowered.api.world.Location;
+import org.spongepowered.api.world.World;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_CLAIM_SPAWN)
+public class CommandClaimSpawn extends BaseCommand {
+
+    @CommandAlias("claimspawn")
+    @Description("Teleports you to claim spawn if available.")
+    @Syntax("[name] [user]")
+    @Subcommand("claim spawn")
+    public void execute(Player player, @Optional String claimName, @Optional User targetPlayer) {
+        final GDPlayerData srcPlayerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        GDPlayerData targetPlayerData = null;
+        if (targetPlayer != null) {
+            targetPlayerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), targetPlayer.getUniqueId());
+        } else {
+            targetPlayerData = srcPlayerData;
+        }
+
+        GDClaim claim = null;
+        if (claimName != null) {
+            for (Claim playerClaim : targetPlayerData.getInternalClaims()) {
+                String name = null;
+                Component component = playerClaim.getName().orElse(null);
+                if (component != null) {
+                    name = PlainComponentSerializer.INSTANCE.serialize(component);
+                    if (claimName.equalsIgnoreCase(name)) {
+                        claim = (GDClaim) playerClaim;
+                        break;
+                    }
+                }
+            }
+            if (claim == null) {
+                GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.COMMAND_CLAIMNAME_NOT_FOUND,
+                        ImmutableMap.of("name", claimName)));
+                return;
+            }
+        } else {
+            claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(targetPlayerData, player.getLocation());
+        }
+
+        if (!srcPlayerData.canIgnoreClaim(claim) && !claim.isUserTrusted(player, TrustTypes.ACCESSOR) && !player.hasPermission(GDPermissions.COMMAND_DELETE_CLAIMS)) {
+            GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.PERMISSION_ACCESS,
+                    ImmutableMap.of("player", claim.getOwnerName())));
+            return;
+        }
+
+        final Vector3i spawnPos = claim.getData().getSpawnPos().orElse(null);
+        if (spawnPos == null) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().SPAWN_NOT_SET);
+            return;
+        }
+
+        final Location<World> spawnLocation = new Location<>(claim.getWorld(), spawnPos.getX(), spawnPos.getY(), spawnPos.getZ());
+        int teleportDelay = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), player, Options.PLAYER_TELEPORT_DELAY, claim);
+        if (teleportDelay > 0) {
+            srcPlayerData.teleportDelay = teleportDelay + 1;
+            srcPlayerData.teleportLocation = spawnLocation;
+            return;
+        }
+        player.setLocation(spawnLocation);
+        final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.SPAWN_TELEPORT,
+                ImmutableMap.of(
+                "location", spawnPos));
+        GriefDefenderPlugin.sendMessage(player, message);
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimSubdivision.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimSubdivision.java
new file mode 100644
index 0000000..ddff33e
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimSubdivision.java
@@ -0,0 +1,53 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.claim.ShovelTypes;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.permission.GDPermissions;
+import org.spongepowered.api.entity.living.player.Player;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_SUBDIVIDE_CLAIMS)
+public class CommandClaimSubdivision extends BaseCommand {
+
+    @CommandAlias("modesubdivide|subdivideclaims|sc")
+    @Description("Switches the shovel tool to subdivision mode, used to subdivide your claims.")
+    @Subcommand("mode subdivide")
+    public void execute(Player player) {
+
+        final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        playerData.shovelMode = ShovelTypes.SUBDIVISION;
+        playerData.claimSubdividing = null;
+        GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().MODE_SUBDIVISION);
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimTown.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimTown.java
new file mode 100644
index 0000000..01a1d5d
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimTown.java
@@ -0,0 +1,53 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.claim.ShovelTypes;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.permission.GDPermissions;
+import org.spongepowered.api.entity.living.player.Player;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_TOWN_MODE)
+public class CommandClaimTown extends BaseCommand {
+
+    @CommandAlias("modetown")
+    @Description("Switches the shovel tool to town claims mode.")
+    @Subcommand("mode town")
+    public void execute(Player player) {
+
+        final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        playerData.shovelMode = ShovelTypes.TOWN;
+        playerData.claimSubdividing = null;
+        GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().MODE_TOWN);
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimTransfer.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimTransfer.java
new file mode 100644
index 0000000..bdd287c
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimTransfer.java
@@ -0,0 +1,86 @@
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandCompletion;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.claim.ClaimResult;
+import com.griefdefender.api.claim.ClaimResultType;
+import com.griefdefender.api.claim.TrustTypes;
+import com.griefdefender.api.data.PlayerData;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.event.GDCauseStackManager;
+import com.griefdefender.permission.GDPermissions;
+import net.kyori.text.TextComponent;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import net.kyori.text.format.TextColor;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.entity.living.player.User;
+
+import java.util.UUID;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_TRANSFER_CLAIM)
+public class CommandClaimTransfer extends BaseCommand {
+
+    @CommandCompletion("@gdplayers @gddummy")
+    @CommandAlias("claimtransfer|transferclaim")
+    @Description("Transfers a basic or admin claim to another player.")
+    @Syntax("<player>")
+    @Subcommand("claim transfer")
+    public void execute(Player player, User targetPlayer) {
+        final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        final GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(playerData, player.getLocation());
+
+        if (claim == null || claim.isWilderness()) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().CLAIM_NOT_FOUND);
+            return;
+        }
+
+        final UUID ownerId = claim.getOwnerUniqueId();
+        final boolean isAdmin = playerData.canIgnoreClaim(claim);
+        // check permission
+        if (!isAdmin && claim.isAdminClaim() && !player.hasPermission(GDPermissions.COMMAND_ADMIN_CLAIMS)) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().PERMISSION_CLAIM_TRANSFER_ADMIN);
+            return;
+        } else if (!isAdmin && !player.getUniqueId().equals(ownerId) && claim.isUserTrusted(player, TrustTypes.MANAGER)) {
+            if (claim.parent == null) {
+                // Managers can only transfer child claims
+                GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().CLAIM_NOT_YOURS);
+                return;
+            }
+        } else if (!isAdmin && !claim.isAdminClaim() && !player.getUniqueId().equals(ownerId)) {
+            // verify ownership
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().CLAIM_NOT_YOURS);
+            return;
+        }
+
+        // change ownership
+        GDCauseStackManager.getInstance().pushCause(player);
+        final ClaimResult claimResult = claim.transferOwner(targetPlayer.getUniqueId());
+        if (!claimResult.successful()) {
+            PlayerData targetPlayerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), targetPlayer.getUniqueId());
+            if (claimResult.getResultType() == ClaimResultType.INSUFFICIENT_CLAIM_BLOCKS) {
+                TextAdapter.sendComponent(player, TextComponent.of("Could not transfer claim to player with UUID " + targetPlayer.getUniqueId() + "."
+                    + " Player only has " + targetPlayerData.getRemainingClaimBlocks() + " claim blocks remaining." 
+                    + " The claim requires a total of " + claim.getClaimBlocks() + " claim blocks to own.", TextColor.RED));
+            } else if (claimResult.getResultType() == ClaimResultType.WRONG_CLAIM_TYPE) {
+                TextAdapter.sendComponent(player, TextComponent.of("The wilderness claim cannot be transferred.", TextColor.RED));
+            } else if (claimResult.getResultType() == ClaimResultType.CLAIM_EVENT_CANCELLED) {
+                TextAdapter.sendComponent(player, TextComponent.of("Could not transfer the claim. A plugin has cancelled the TransferClaimEvent.", TextColor.RED));
+            } else {
+                TextAdapter.sendComponent(player, TextComponent.of("Could not transfer the claim. " + claimResult.getResultType().name(), TextColor.RED));
+            }
+        } else {
+            GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.CLAIM_TRANSFER_SUCCESS));
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimUnban.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimUnban.java
new file mode 100644
index 0000000..5f1b17e
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimUnban.java
@@ -0,0 +1,107 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandCompletion;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Optional;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+import net.kyori.text.TextComponent;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import net.kyori.text.format.TextColor;
+import org.spongepowered.api.data.type.HandTypes;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.item.inventory.ItemStack;
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.internal.registry.BlockTypeRegistryModule;
+import com.griefdefender.internal.registry.EntityTypeRegistryModule;
+import com.griefdefender.internal.registry.ItemTypeRegistryModule;
+import com.griefdefender.internal.util.NMSUtil;
+import com.griefdefender.permission.GDPermissions;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_CLAIM_BAN)
+public class CommandClaimUnban extends BaseCommand {
+
+    @CommandCompletion("@gdbantypes @gdmcids @gddummy")
+    @CommandAlias("claimunban")
+    @Description("Unbans target id allowing it to be used again.")
+    @Syntax("hand | <type> <target>")
+    @Subcommand("unban")
+    public void execute(Player player, String type, @Optional String id) {
+        if (type.equalsIgnoreCase("block")) {
+            if (!BlockTypeRegistryModule.getInstance().getById(id).isPresent()) {
+                TextAdapter.sendComponent(player, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.REGISTRY_BLOCK_NOT_FOUND,
+                        ImmutableMap.of("id", TextComponent.of(id, TextColor.LIGHT_PURPLE))));
+                return;
+            }
+            GriefDefenderPlugin.getGlobalConfig().getConfig().bans.removeBlockBan(id);
+            GriefDefenderPlugin.getGlobalConfig().save();
+            TextAdapter.sendComponent(player, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.COMMAND_CLAIMUNBAN_SUCCESS_BLOCK,
+                    ImmutableMap.of("id", id)));
+        } else if (type.equalsIgnoreCase("entity")) {
+            if (!EntityTypeRegistryModule.getInstance().getById(id).isPresent()) {
+                TextAdapter.sendComponent(player, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.REGISTRY_ENTITY_NOT_FOUND,
+                        ImmutableMap.of("id", TextComponent.of(id, TextColor.LIGHT_PURPLE))));
+                return;
+            }
+
+            GriefDefenderPlugin.getGlobalConfig().getConfig().bans.removeEntityBan(id);
+            GriefDefenderPlugin.getGlobalConfig().save();
+            TextAdapter.sendComponent(player, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.COMMAND_CLAIMUNBAN_SUCCESS_ENTITY,
+                    ImmutableMap.of("id", TextComponent.of(id, TextColor.LIGHT_PURPLE))));
+        } else if (type.equalsIgnoreCase("item")) {
+            if (!ItemTypeRegistryModule.getInstance().getById(id).isPresent()) {
+                TextAdapter.sendComponent(player, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.REGISTRY_ITEM_NOT_FOUND,
+                        ImmutableMap.of("id", TextComponent.of(id, TextColor.LIGHT_PURPLE))));
+                return;
+            }
+
+            GriefDefenderPlugin.getGlobalConfig().getConfig().bans.removeItemBan(id);
+            GriefDefenderPlugin.getGlobalConfig().save();
+            TextAdapter.sendComponent(player, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.COMMAND_CLAIMUNBAN_SUCCESS_ITEM,
+                    ImmutableMap.of("id", TextComponent.of(id, TextColor.LIGHT_PURPLE))));
+        } else if (type.equalsIgnoreCase("hand")) {
+            final ItemStack itemInHand = player.getItemInHand(HandTypes.MAIN_HAND).orElse(null);
+            if (itemInHand == null) {
+                return;
+            }
+            final String handItemId = itemInHand.getType().getId();
+            GriefDefenderPlugin.getGlobalConfig().getConfig().bans.removeItemBan(handItemId);
+            GriefDefenderPlugin.getGlobalConfig().save();
+            TextAdapter.sendComponent(player, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.COMMAND_CLAIMUNBAN_SUCCESS_ITEM,
+                    ImmutableMap.of("id", TextComponent.of(handItemId, TextColor.LIGHT_PURPLE))));
+        } else {
+            TextAdapter.sendComponent(player, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.COMMAND_INVALID_TYPE,
+                    ImmutableMap.of("type", type)));
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandClaimWorldEdit.java b/sponge/src/main/java/com/griefdefender/command/CommandClaimWorldEdit.java
new file mode 100644
index 0000000..a68bf03
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandClaimWorldEdit.java
@@ -0,0 +1,52 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.permission.GDPermissions;
+import org.spongepowered.api.entity.living.player.Player;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_CLAIM_WORLDEDIT)
+public class CommandClaimWorldEdit extends BaseCommand {
+
+    @CommandAlias("claimwe|claimworldedit")
+    @Description("Uses the worldedit selection to create a claim.")
+    @Subcommand("claim worldedit|claim we")
+    public void execute(Player player) {
+        if (GriefDefenderPlugin.getInstance().getWorldEditProvider() == null) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().COMMAND_WORLDEDIT_MISSING);
+            return;
+        }
+
+        GriefDefenderPlugin.getInstance().getWorldEditProvider().createClaim(player);
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandDebug.java b/sponge/src/main/java/com/griefdefender/command/CommandDebug.java
new file mode 100644
index 0000000..e25f804
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandDebug.java
@@ -0,0 +1,116 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Optional;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+import com.griefdefender.GDDebugData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.permission.GDPermissions;
+import net.kyori.text.TextComponent;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import net.kyori.text.format.TextColor;
+import org.spongepowered.api.command.CommandSource;
+import org.spongepowered.api.entity.living.player.User;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_ADMIN_DEBUG)
+public class CommandDebug extends BaseCommand {
+
+    @CommandAlias("gddebug")
+    @Description("Captures all GD actions for debugging purposes.")
+    @Syntax("<record|paste|on|off> [filter]")
+    @Subcommand("debug")
+    public void execute(CommandSource src, String target, @Optional User user) {
+        GDDebugData debugData = null;
+        boolean paste = false;
+        if (target.equalsIgnoreCase("on")) {
+            debugData = getOrCreateDebugUser(src, user, true);
+        } else if (target.equalsIgnoreCase("record")) {
+            debugData = getOrCreateDebugUser(src, user, false);
+        } else if (target.equalsIgnoreCase("paste")) {
+            paste = true;
+        } else if (target.equalsIgnoreCase("off")) {
+            GriefDefenderPlugin.getInstance().getDebugUserMap().remove(src.getIdentifier());
+            if (GriefDefenderPlugin.getInstance().getDebugUserMap().isEmpty()) {
+                GriefDefenderPlugin.debugActive = false;
+            }
+        }
+
+        if (debugData == null) {
+            if (paste) {
+                debugData = GriefDefenderPlugin.getInstance().getDebugUserMap().get(src.getIdentifier());
+                if (debugData == null) {
+                    TextAdapter.sendComponent(src, TextComponent.of("Nothing to paste!", TextColor.RED));
+                } else {
+                    debugData.pasteRecords();
+                }
+            }
+            TextAdapter.sendComponent(src, TextComponent.builder("")
+                    .append(GriefDefenderPlugin.GD_TEXT)
+                    .append("Debug ", TextColor.GRAY)
+                    .append("OFF", TextColor.RED)
+                    .build());
+            GriefDefenderPlugin.getInstance().getDebugUserMap().remove(src.getIdentifier());
+            if (GriefDefenderPlugin.getInstance().getDebugUserMap().isEmpty()) {
+                GriefDefenderPlugin.debugActive = false;
+            }
+        } else {
+            TextAdapter.sendComponent(src, TextComponent.builder("")
+                    .append(GriefDefenderPlugin.GD_TEXT)
+                    .append("Debug: ", TextColor.GRAY)
+                    .append("ON", TextColor.GREEN)
+                    .append(" | ")
+                    .append("Verbose: ", TextColor.GRAY)
+                    .append(!debugData.isRecording() ? TextComponent.of("ON", TextColor.GREEN) : TextComponent.of("OFF", TextColor.RED))
+                    .append(" | ")
+                    .append("Record: ", TextColor.GRAY)
+                    .append(debugData.isRecording() ? TextComponent.of("ON", TextColor.GREEN) : TextComponent.of("OFF", TextColor.RED))
+                    .append(" | ")
+                    .append("User: ", TextColor.GRAY)
+                    .append(user == null ? "ALL" : user.getName(), TextColor.GOLD)
+                    .build());
+            GriefDefenderPlugin.getInstance().getDebugUserMap().put(src.getIdentifier(), debugData);
+        }
+    }
+
+    private GDDebugData getOrCreateDebugUser(CommandSource src, User user, boolean verbose) {
+        GDDebugData debugData = GriefDefenderPlugin.getInstance().getDebugUserMap().get(src.getIdentifier());
+        if (debugData == null) {
+            debugData = new GDDebugData(src, user, verbose);
+            GriefDefenderPlugin.getInstance().getDebugUserMap().put(src.getIdentifier(), debugData);
+        } else {
+            debugData.setTarget(user);
+            debugData.setVerbose(verbose);
+        }
+        GriefDefenderPlugin.debugActive = true;
+        return debugData;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandException.java b/sponge/src/main/java/com/griefdefender/command/CommandException.java
new file mode 100644
index 0000000..aca9170
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandException.java
@@ -0,0 +1,92 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) SpongePowered <https://www.spongepowered.org>
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import net.kyori.text.Component;
+
+/**
+ * Thrown when an executed command raises an error or when execution of
+ * the command failed.
+ */
+public class CommandException extends ComponentMessageException {
+
+    private static final long serialVersionUID = 4626722485860074825L;
+
+    private final boolean includeUsage;
+
+    /**
+     * Constructs a new {@link CommandException} with the given message.
+     *
+     * @param message The detail message
+     */
+    public CommandException(Component message) {
+        this(message, false);
+    }
+
+    /**
+     * Constructs a new {@link CommandException} with the given message and
+     * the given cause.
+     *
+     * @param message The detail message
+     * @param cause The cause
+     */
+    public CommandException(Component message, Throwable cause) {
+        this(message, cause, false);
+    }
+
+    /**
+     * Constructs a new {@link CommandException} with the given message.
+     *
+     * @param message The detail message
+     * @param includeUsage Whether to include usage in the exception
+     */
+    public CommandException(Component message, boolean includeUsage) {
+        super(message);
+        this.includeUsage = includeUsage;
+    }
+
+    /**
+     * Constructs a new {@link CommandException} with the given message and
+     * the given cause.
+     *
+     * @param message The detail message
+     * @param cause The cause
+     * @param includeUsage Whether to include the usage in the exception
+     */
+    public CommandException(Component message, Throwable cause, boolean includeUsage) {
+        super(message, cause);
+        this.includeUsage = includeUsage;
+    }
+
+    /**
+     * Gets whether the exception should include usage in
+     * the presentation of the exception/stack-trace.
+     *
+     * @return Whether to include usage in the exception
+     */
+    public boolean shouldIncludeUsage() {
+        return this.includeUsage;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandGDReload.java b/sponge/src/main/java/com/griefdefender/command/CommandGDReload.java
new file mode 100644
index 0000000..b0f68c9
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandGDReload.java
@@ -0,0 +1,48 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.permission.GDPermissions;
+import org.spongepowered.api.command.CommandSource;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_RELOAD)
+public class CommandGDReload extends BaseCommand {
+
+    @CommandAlias("gdreload")
+    @Description("Reloads GriefDefender's configuration settings.")
+    @Subcommand("reload")
+    public void execute(CommandSource src) {
+        GriefDefenderPlugin.getInstance().loadConfig();
+        GriefDefenderPlugin.sendMessage(src, MessageCache.getInstance().PLUGIN_RELOAD);
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandGDVersion.java b/sponge/src/main/java/com/griefdefender/command/CommandGDVersion.java
new file mode 100644
index 0000000..1f81d2b
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandGDVersion.java
@@ -0,0 +1,77 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.permission.GDPermissions;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import net.kyori.text.format.TextColor;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.command.CommandSource;
+import org.spongepowered.api.service.permission.PermissionService;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_VERSION)
+public class CommandGDVersion extends BaseCommand {
+
+    @CommandAlias("gdversion")
+    @Description("Displays GriefDefender's version information.")
+    @Subcommand("version")
+    public void execute(CommandSource src) {
+
+        final String spongePlatform = Sponge.getPlatform().getContainer(org.spongepowered.api.Platform.Component.IMPLEMENTATION).getName();
+        Component gpVersion = TextComponent.builder("")
+                .append(GriefDefenderPlugin.GD_TEXT)
+                .append("Running ")
+                .append("GriefDefender " + GriefDefenderPlugin.IMPLEMENTATION_VERSION, TextColor.AQUA)
+                .build();
+        Component spongeVersion = TextComponent.builder("")
+                .append(GriefDefenderPlugin.GD_TEXT)
+                .append("Running ")
+                .append(spongePlatform + " " + GriefDefenderPlugin.SPONGE_VERSION, TextColor.YELLOW)
+                .build();
+        String permissionPlugin = Sponge.getServiceManager().getRegistration(PermissionService.class).get().getPlugin().getId();
+        String permissionVersion = Sponge.getServiceManager().getRegistration(PermissionService.class).get().getPlugin().getVersion().orElse("unknown");
+        Component permVersion = TextComponent.builder("")
+                .append(GriefDefenderPlugin.GD_TEXT)
+                .append("Running ")
+                .append(permissionPlugin + " " + permissionVersion, TextColor.GREEN)
+                .build();
+        TextAdapter.sendComponent(src, TextComponent.builder("")
+                .append(gpVersion)
+                .append("\n")
+                .append(spongeVersion)
+                .append("\n")
+                .append(permVersion)
+                .build());
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandGiveBlocks.java b/sponge/src/main/java/com/griefdefender/command/CommandGiveBlocks.java
new file mode 100644
index 0000000..80e211f
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandGiveBlocks.java
@@ -0,0 +1,92 @@
+package com.griefdefender.command;
+
+import java.util.function.Consumer;
+
+import org.spongepowered.api.command.CommandSource;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.entity.living.player.User;
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.permission.GDPermissions;
+import com.griefdefender.text.action.GDCallbackHolder;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandCompletion;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import net.kyori.text.event.ClickEvent;
+import net.kyori.text.event.HoverEvent;
+import net.kyori.text.format.TextColor;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_GIVE_BLOCKS)
+public class CommandGiveBlocks extends BaseCommand {
+
+    @CommandCompletion("@gdplayers @gddummy")
+    @CommandAlias("giveblocks")
+    @Description("Gives claim blocks to another player.")
+    @Syntax("<player> <amount>")
+    @Subcommand("giveblocks")
+    public void execute(Player src, User targetPlayer, int amount) {
+        final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(src.getWorld(), src.getUniqueId());
+        int availableBlocks = playerData.getAccruedClaimBlocks() + playerData.getBonusClaimBlocks();
+        if (amount > availableBlocks) {
+            TextAdapter.sendComponent(src, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.COMMAND_GIVEBLOCKS_NOT_ENOUGH, 
+                    ImmutableMap.of("amount", TextComponent.of(availableBlocks, TextColor.GOLD))));
+            return;
+        }
+
+        final Component confirmationText = TextComponent.builder()
+                .append(MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.COMMAND_GIVEBLOCKS_CONFIRMATION, 
+                        ImmutableMap.of("player", TextComponent.of(targetPlayer.getName(), TextColor.AQUA),
+                                        "amount", TextComponent.of(amount, TextColor.GREEN))))
+                    .append(TextComponent.builder()
+                    .append("\n[")
+                    .append("Confirm", TextColor.GREEN)
+                    .append("]\n")
+                    .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createConfirmationConsumer(src, targetPlayer, amount))))
+                    .hoverEvent(HoverEvent.showText(MessageCache.getInstance().UI_CLICK_CONFIRM)).build())
+                .build();
+        TextAdapter.sendComponent(src, confirmationText);
+    }
+
+    private static Consumer<CommandSource> createConfirmationConsumer(Player src, User targetPlayer, int amount) {
+        return confirm -> {
+            final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(src.getWorld(), src.getUniqueId());
+            final int accruedTotal = playerData.getAccruedClaimBlocks();
+            final int bonusTotal = playerData.getBonusClaimBlocks();
+            if (bonusTotal >= amount) {
+                playerData.setBonusClaimBlocks(bonusTotal - amount);
+            } else if (accruedTotal >= amount) {
+                playerData.setAccruedClaimBlocks(accruedTotal- amount);
+            } else {
+                int remaining = amount - bonusTotal;
+                playerData.setBonusClaimBlocks(0);
+                int newAccrued = accruedTotal - remaining;
+                playerData.setAccruedClaimBlocks(newAccrued);
+            }
+            final GDPlayerData targetPlayerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(src.getWorld(), targetPlayer.getUniqueId());
+            targetPlayerData.setBonusClaimBlocks(targetPlayerData.getBonusClaimBlocks() + amount);
+            playerData.getStorageData().save();
+            targetPlayerData.getStorageData().save();
+            final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.COMMAND_GIVEBLOCKS_CONFIRMED);
+            TextAdapter.sendComponent(src, message);
+
+            if (targetPlayer.isOnline()) {
+                final Component targetMessage = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.COMMAND_GIVEBLOCKS_RECEIVED, 
+                        ImmutableMap.of("amount", TextComponent.of(amount, TextColor.GOLD),
+                                        "player", TextComponent.of(src.getName(), TextColor.AQUA)));
+                TextAdapter.sendComponent((Player) targetPlayer, targetMessage);
+            }
+        };
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandGivePet.java b/sponge/src/main/java/com/griefdefender/command/CommandGivePet.java
new file mode 100644
index 0000000..594be29
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandGivePet.java
@@ -0,0 +1,31 @@
+package com.griefdefender.command;
+
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.permission.GDPermissions;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.entity.living.player.User;
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandCompletion;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_GIVE_PET)
+public class CommandGivePet extends BaseCommand {
+
+    @CommandCompletion("@gdplayers @gddummy")
+    @CommandAlias("givepet")
+    @Description("Transfers a pet to a new owner.")
+    @Syntax("<player>")
+    @Subcommand("givepet")
+    public void execute(Player player, User newOwner) {
+        final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        playerData.petRecipientUniqueId = newOwner.getUniqueId();
+        GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().COMMAND_PET_TRANSFER_READY);
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandHelp.java b/sponge/src/main/java/com/griefdefender/command/CommandHelp.java
new file mode 100644
index 0000000..36973e2
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandHelp.java
@@ -0,0 +1,51 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.HelpCommand;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.internal.pagination.PaginationList;
+import com.griefdefender.permission.GDPermissions;
+import net.kyori.text.TextComponent;
+import net.kyori.text.format.TextColor;
+import net.kyori.text.format.TextDecoration;
+import org.spongepowered.api.command.CommandSource;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_HELP)
+public class CommandHelp extends BaseCommand {
+
+    @HelpCommand
+    @Description("Displays GriefDefender command help.")
+    public void execute(CommandSource src) {
+        PaginationList.Builder paginationBuilder =
+                PaginationList.builder().title(TextComponent.of("Showing GriefDefender Help", TextColor.AQUA)).padding(TextComponent.of(" ").decoration(TextDecoration.STRIKETHROUGH, true)).contents(GriefDefenderPlugin.helpComponents);
+        paginationBuilder.sendTo(src);
+    }
+}
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandHelper.java b/sponge/src/main/java/com/griefdefender/command/CommandHelper.java
new file mode 100644
index 0000000..3243de2
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandHelper.java
@@ -0,0 +1,1352 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import com.flowpowered.math.vector.Vector3i;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.reflect.TypeToken;
+import com.google.gson.Gson;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.GriefDefender;
+import com.griefdefender.api.Subject;
+import com.griefdefender.api.Tristate;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.ClaimContexts;
+import com.griefdefender.api.claim.ClaimResult;
+import com.griefdefender.api.claim.TrustType;
+import com.griefdefender.api.claim.TrustTypes;
+import com.griefdefender.api.economy.BankTransactionType;
+import com.griefdefender.api.permission.Context;
+import com.griefdefender.api.permission.PermissionResult;
+import com.griefdefender.api.permission.ResultTypes;
+import com.griefdefender.api.permission.flag.Flag;
+import com.griefdefender.api.permission.flag.Flags;
+import com.griefdefender.api.permission.option.Option;
+import com.griefdefender.api.permission.option.Options;
+import com.griefdefender.api.permission.option.type.CreateModeTypes;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.GriefDefenderConfig;
+import com.griefdefender.configuration.MessageDataConfig;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.economy.GDBankTransaction;
+import com.griefdefender.internal.pagination.PaginationList;
+import com.griefdefender.internal.util.NMSUtil;
+import com.griefdefender.internal.util.VecHelper;
+import com.griefdefender.internal.visual.ClaimVisual;
+import com.griefdefender.permission.GDPermissionHolder;
+import com.griefdefender.permission.GDPermissionManager;
+import com.griefdefender.permission.GDPermissionResult;
+import com.griefdefender.permission.GDPermissionUser;
+import com.griefdefender.permission.GDPermissions;
+import com.griefdefender.permission.flag.GDFlag;
+import com.griefdefender.permission.ui.MenuType;
+import com.griefdefender.permission.ui.UIHelper;
+import com.griefdefender.registry.FlagRegistryModule;
+import com.griefdefender.text.action.GDCallbackHolder;
+import com.griefdefender.util.PermissionUtil;
+import com.griefdefender.util.TaskUtil;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import net.kyori.text.event.ClickEvent;
+import net.kyori.text.event.HoverEvent;
+import net.kyori.text.format.TextColor;
+import net.kyori.text.format.TextDecoration;
+import net.kyori.text.serializer.legacy.LegacyComponentSerializer;
+import net.kyori.text.serializer.plain.PlainComponentSerializer;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.block.BlockState;
+import org.spongepowered.api.block.BlockType;
+import org.spongepowered.api.command.CommandException;
+import org.spongepowered.api.command.CommandMapping;
+import org.spongepowered.api.command.CommandSource;
+import org.spongepowered.api.data.property.entity.EyeLocationProperty;
+import org.spongepowered.api.entity.EntityType;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.entity.living.player.User;
+import org.spongepowered.api.event.CauseStackManager;
+import org.spongepowered.api.item.ItemType;
+import org.spongepowered.api.plugin.PluginContainer;
+import org.spongepowered.api.service.economy.Currency;
+import org.spongepowered.api.service.economy.EconomyService;
+import org.spongepowered.api.service.economy.account.Account;
+import org.spongepowered.api.service.economy.account.UniqueAccount;
+import org.spongepowered.api.service.economy.transaction.ResultType;
+import org.spongepowered.api.service.economy.transaction.TransactionResult;
+import org.spongepowered.api.world.DimensionTypes;
+import org.spongepowered.api.world.Location;
+import org.spongepowered.api.world.World;
+
+import java.math.BigDecimal;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.ZonedDateTime;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.UUID;
+import java.util.function.Consumer;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class CommandHelper {
+
+    public static Comparator<Component> PLAIN_COMPARATOR = (text1, text2) -> PlainComponentSerializer.INSTANCE.serialize(text1).compareTo(PlainComponentSerializer.INSTANCE.serialize(text2));
+
+    public static Player checkPlayer(CommandSource source) {
+        if (source instanceof Player) {
+            return ((Player) source);
+        } else {
+            return null;
+        }
+    }
+
+    public static boolean validateFlagTarget(Flag flag, String target) {
+        if (!(flag instanceof GDFlag)) {
+            return true;
+        }
+
+        if (flag.getName().equals("block-break") || flag.getName().equals("block-place") || flag.getName().equals("collide-block")) {
+            if (validateBlockTarget(target) ||
+                validateItemTarget(target)) {
+                return true;
+            }
+            return false;
+        }
+        if (flag.getName().equals("enter-claim") || flag.getName().equals("exit-claim") || flag.getName().equals("entity-riding") ||
+                flag.getName().equals("entity-damage") || flag.getName().equals("portal-use")) {
+            if (validateEntityTarget(target) ||
+                validateBlockTarget(target) ||
+                validateItemTarget(target)) {
+                return true;
+            }
+
+            return false;
+        }
+        if (flag.getName().equals("interact-inventory")) {
+            if (validateEntityTarget(target) || validateBlockTarget(target)) {
+                return true;
+            }
+
+            return false;
+        }
+        if (flag.getName().equals("liquid-flow") || flag.getName().equals("interact-block-primary") 
+                || flag.getName().equals("interact-block-secondary")) {
+            return validateBlockTarget(target);
+        }
+        if (flag.getName().equals("entity-chunk-spawn") || flag.getName().equals("entity-spawn") ||
+                flag.getName().equals("interact-entity-primary") || flag.getName().equals("interact-entity-secondary")) {
+            return validateEntityTarget(target);
+        }
+        if (flag.getName().equals("item-drop") || flag.getName().equals("item-pickup") ||
+                flag.getName().equals("item-spawn") || flag.getName().equals("item-use")) {
+            return validateItemTarget(target);
+        }
+
+        return true;
+    }
+
+    private static boolean validateEntityTarget(String target) {
+        Optional<EntityType> entityType = Sponge.getRegistry().getType(EntityType.class, target);
+        if (entityType.isPresent()) {
+            return true;
+        }
+
+        return false;
+    }
+
+    private static boolean validateItemTarget(String target) {
+        Optional<ItemType> itemType = Sponge.getRegistry().getType(ItemType.class, target);
+        if (itemType.isPresent()) {
+            return true;
+        }
+        // target could be an item block, so validate blockstate
+        Optional<BlockState> blockState = Sponge.getRegistry().getType(BlockState.class, target);
+        if (blockState.isPresent()) {
+            return true;
+        }
+
+        return false;
+    }
+
+    private static boolean validateBlockTarget(String target) {
+        Optional<BlockType> blockType = Sponge.getRegistry().getType(BlockType.class, target);
+        if (blockType.isPresent()) {
+            return true;
+        }
+
+        Optional<BlockState> blockState = Sponge.getRegistry().getType(BlockState.class, target);
+        if (blockState.isPresent()) {
+            return true;
+        }
+        return false;
+    }
+
+    public static PermissionResult addFlagPermission(CommandSource src, GDPermissionHolder subject, Claim claim, Flag claimFlag, String target, Tristate value, Set<Context> contexts) {
+        if (src instanceof Player) {
+            Component denyReason = ((GDClaim) claim).allowEdit((Player) src);
+            if (denyReason != null) {
+                GriefDefenderPlugin.sendMessage(src, denyReason);
+                return new GDPermissionResult(ResultTypes.NO_PERMISSION);
+            }
+        }
+
+        final String baseFlag = claimFlag.toString().toLowerCase();
+        String flagPermission = GDPermissions.FLAG_BASE + "." + baseFlag;
+        // special handling for commands
+        target = adjustTargetForTypes(target, claimFlag);
+        if (baseFlag.equals(Flags.COMMAND_EXECUTE.getName()) || baseFlag.equals(Flags.COMMAND_EXECUTE_PVP.getName())) {
+            target = handleCommandFlag(src, target);
+            if (target == null) {
+                // failed
+                return new GDPermissionResult(ResultTypes.TARGET_NOT_VALID);
+            }
+        } else {
+            if (!target.equalsIgnoreCase("any")) {
+                if (!target.startsWith("#") && !target.contains(":")) {
+                    // assume vanilla
+                    target = "minecraft:" + target;
+                }
+    
+                String[] parts = target.split(":");
+                if (parts.length > 1 && parts[1].equalsIgnoreCase("any")) {
+                    target = baseFlag + "." + parts[0];
+                } else {
+                    // check for meta
+                    parts = target.split("\\.");
+                    String targetFlag = parts[0];
+                    if (parts.length > 1) {
+                        try {
+                            Integer.parseInt(parts[1]);
+                        } catch (NumberFormatException e) {
+                            final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.PERMISSION_CLAIM_MANAGE, ImmutableMap.of(
+                                    "meta", parts[1],
+                                    "flag", baseFlag));
+                            GriefDefenderPlugin.sendMessage(src, message);
+                            return new GDPermissionResult(ResultTypes.TARGET_NOT_VALID);
+                        }
+                    }
+                    addFlagContexts(contexts, claimFlag, targetFlag);
+                    if (!targetFlag.startsWith("#") && !CommandHelper.validateFlagTarget(claimFlag, targetFlag)) {
+                        //TODO
+                        /*final Text message = GriefDefenderPlugin.getInstance().messageData.permissionClaimManage
+                                .apply(ImmutableMap.of(
+                                "target", targetFlag,
+                                "flag", baseFlag)).build();*/
+                        GriefDefenderPlugin.sendMessage(src,TextComponent.of("Invalid flag " + targetFlag, TextColor.RED));
+                        return new GDPermissionResult(ResultTypes.TARGET_NOT_VALID);
+                    }
+                }
+            } else {
+                target = "";
+            }
+        }
+
+        return applyFlagPermission(src, subject, claim, flagPermission, target, value, contexts, null, false);
+    }
+
+    public static PermissionResult applyFlagPermission(CommandSource src, GDPermissionHolder subject, Claim claim, String flagPermission, String target, Tristate value, Set<Context> contexts, MenuType flagType) {
+        return applyFlagPermission(src, subject, claim, flagPermission, target, value, contexts, flagType, false);
+    }
+
+    public static PermissionResult applyFlagPermission(CommandSource src, GDPermissionHolder subject, Claim claim, String flagPermission, String target, Tristate value, Set<Context> contexts, MenuType flagType, boolean clicked) {
+        // Check if player can manage flag
+        if (src instanceof Player) {
+            final String basePermission = flagPermission.replace(GDPermissions.FLAG_BASE + ".", "");
+            Tristate result = Tristate.fromBoolean(src.hasPermission(GDPermissions.USER_CLAIM_FLAGS + "." + basePermission));
+
+            if (result != Tristate.TRUE) {
+                GriefDefenderPlugin.sendMessage(src, MessageCache.getInstance().PERMISSION_FLAG_USE);
+                return new GDPermissionResult(ResultTypes.NO_PERMISSION);
+            }
+        }
+
+        boolean hasDefaultContext = false;
+        boolean hasOverrideContext = false;
+        Component reason = null;
+        Iterator<Context> iterator = contexts.iterator();
+        while (iterator.hasNext()) {
+            final Context context = iterator.next();
+                // validate perms
+            if (context.getKey().equalsIgnoreCase("reason")) {
+                reason = LegacyComponentSerializer.legacy().deserialize(context.getValue(), '&');
+                iterator.remove();
+                continue;
+            }
+            if (hasDefaultContext || hasOverrideContext) {
+                continue;
+            }
+            if (context.getKey().contains("gd_claim_default")) {
+                hasDefaultContext = true;
+            } else if (context.getKey().contains("gd_claim_override")) {
+                hasOverrideContext = true;
+            }
+        }
+        // Add target context
+        if (target != null && !target.isEmpty() && !target.equalsIgnoreCase("any")) {
+            contexts.add(new Context("target", target));
+        }
+
+        if (flagType == null) {
+            if (hasDefaultContext) {
+                flagType = MenuType.DEFAULT;
+            } else if (hasOverrideContext) {
+                flagType = MenuType.OVERRIDE;
+            } else {
+                flagType = MenuType.CLAIM;
+            }
+        }
+
+        TextComponent.Builder builder = null;
+        if (flagType == MenuType.OVERRIDE) {
+            builder = TextComponent.builder("OVERRIDE").color(TextColor.RED);
+        } else if (flagType == MenuType.DEFAULT) {
+            builder = TextComponent.builder("DEFAULT").color(TextColor.LIGHT_PURPLE);
+        } else if (flagType == MenuType.CLAIM) {
+            builder = TextComponent.builder("CLAIM").color(TextColor.GOLD);
+            if (contexts instanceof HashSet) {
+                contexts.add(claim.getContext());
+            }
+        }
+        Component flagTypeText = builder.build();
+
+        if (contexts.isEmpty()) {
+            // default to claim
+            contexts.add(claim.getContext());
+        }
+        // wilderness overrides affect all worlds
+        if (!contexts.contains(ClaimContexts.WILDERNESS_OVERRIDE_CONTEXT) && !contexts.contains(claim.getContext())) {
+            if (contexts instanceof HashSet) {
+                //contexts.add(claim.getWorld().getContext());
+            }
+        }
+
+        if (subject == GriefDefenderPlugin.DEFAULT_HOLDER) {
+            PermissionUtil.getInstance().setPermissionValue(GriefDefenderPlugin.DEFAULT_HOLDER, flagPermission, value, contexts);
+            if (!clicked && src instanceof Player) {
+                TextAdapter.sendComponent(src, TextComponent.builder("")
+                    .append(TextComponent.builder("\n[").append(MessageCache.getInstance().FLAG_UI_RETURN_FLAGS.color(TextColor.AQUA)).append("]\n")
+                    .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createCommandConsumer(src, "claimflag", "")))).build())
+                        .append(MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.FLAG_SET_PERMISSION_TARGET,
+                                ImmutableMap.of(
+                                    "type", flagTypeText,
+                                    "permission", flagPermission.replace(GDPermissions.FLAG_BASE + ".", ""),
+                                    "contexts", getFriendlyContextString(claim, contexts),
+                                    "value", getClickableText(src, (GDClaim) claim, subject, contexts, flagPermission, value, flagType).color(TextColor.LIGHT_PURPLE),
+                                    "target", "ALL")))
+                        .build());
+            }
+        } else {
+            PermissionUtil.getInstance().setPermissionValue(subject, flagPermission, value, contexts);
+            if (!clicked && src instanceof Player) {
+                TextAdapter.sendComponent(src, TextComponent.builder("")
+                        .append(TextComponent.builder("")
+                                .append("\n[")
+                                .append(MessageCache.getInstance().FLAG_UI_RETURN_FLAGS.color(TextColor.AQUA))
+                                .append("]\n", TextColor.WHITE)
+                                .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createCommandConsumer(src, subject instanceof GDPermissionUser ? "claimflagplayer" : "claimflaggroup", subject.getFriendlyName())))).build())
+                        .append(MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.FLAG_SET_PERMISSION_TARGET,
+                                ImmutableMap.of(
+                                        "type", flagTypeText,
+                                        "permission", flagPermission.replace(GDPermissions.FLAG_BASE + ".", ""),
+                                        "contexts", getFriendlyContextString(claim, contexts),
+                                        "value", getClickableText(src, (GDClaim) claim, subject, contexts, flagPermission, value, flagType).color(TextColor.LIGHT_PURPLE),
+                                        "target", subject.getFriendlyName())))
+                        .build());
+            }
+        }
+
+        return new GDPermissionResult(ResultTypes.SUCCESS);
+    }
+
+    public static String adjustTargetForTypes(String target, Flag flag) {
+        if (target.equals("player") || target.equals("minecraft:player") || target.equalsIgnoreCase("any")) {
+            return target;
+        }
+
+        if (flag.getName().contains("entity") || flag == Flags.ITEM_SPAWN) {
+            final String contextKey = "target";
+            String[] parts = target.split(":");
+            String targetId = "";
+            if (parts.length == 1) {
+                targetId = parts[0];
+            } else {
+                targetId = parts[1];
+            }
+
+            if (targetId.equalsIgnoreCase("animal")) {
+                return "#animal";
+            } else if (targetId.equalsIgnoreCase("aquatic")) {
+                return "#aquatic";
+            } else if (targetId.equalsIgnoreCase("monster")) {
+                return "#monster";
+            } else if (targetId.equalsIgnoreCase("ambient")) {
+                return "#ambient";
+            }
+            return target;
+        } else {
+            if ((target.equals("food") || target.endsWith(":food")) && !target.startsWith("#")) {
+                target = "#" + target;
+            }
+        }
+        return target;
+    }
+
+    public static void addFlagContexts(Set<Context> contexts, Flag flag, String target) {
+        if (target.equals("player") || target.equals("minecraft:player") || target.equalsIgnoreCase("any")) {
+            return;
+        }
+
+        if (flag.getName().contains("entity") || flag == Flags.ITEM_SPAWN) {
+            final String contextKey = "target";
+            String[] parts = target.split(":");
+            if (parts.length == 1) {
+                contexts.add(new Context(contextKey, target));
+                return;
+            }
+
+            if (parts[1].equalsIgnoreCase("animal")) {
+                contexts.add(new Context(contextKey, "#animal"));
+            } else if (parts[1].equalsIgnoreCase("aquatic")) {
+                contexts.add(new Context(contextKey, "#aquatic"));
+            } else if (parts[1].equalsIgnoreCase("monster")) {
+                contexts.add(new Context(contextKey, "#monster"));
+            } else if (parts[1].equalsIgnoreCase("ambient")) {
+                contexts.add(new Context(contextKey, "#ambient"));
+            } else {
+                contexts.add(new Context(contextKey, target));
+            }
+        }
+    }
+
+    public static Component getFriendlyContextString(Claim claim, Set<Context> contexts) {
+        if (contexts.isEmpty()) {
+            return TextComponent.of("[]", TextColor.WHITE);
+        }
+
+        TextComponent.Builder builder = TextComponent.builder();
+        final Iterator<Context> iterator = contexts.iterator();
+        while (iterator.hasNext()) {
+            final Context context = iterator.next();
+            builder.append("\n[", TextColor.WHITE)
+                .append(context.getKey(), TextColor.GREEN)
+                .append("=", TextColor.GRAY)
+                .append(context.getValue(), TextColor.WHITE);
+
+            if (iterator.hasNext()) {
+                builder.append("], ");
+            } else {
+                builder.append("]");
+            }
+        }
+        return builder.build();
+    }
+
+    public static TextColor getPermissionMenuTypeColor(MenuType type) {
+        TextColor color = TextColor.LIGHT_PURPLE;
+        if (type == MenuType.CLAIM) {
+            color = TextColor.GOLD;
+        } else if (type == MenuType.OVERRIDE) {
+            color = TextColor.RED;
+        }
+
+        return color;
+    }
+
+   public static Consumer<CommandSource> createFlagConsumer(CommandSource src, GDClaim claim, Subject subject, Set<Context> contexts, String flagPermission, Tristate flagValue, MenuType flagType) {
+        return consumer -> {
+            Tristate newValue = Tristate.UNDEFINED;
+            if (flagValue == Tristate.TRUE) {
+                newValue = Tristate.FALSE;
+            } else if (flagValue == Tristate.UNDEFINED) {
+                newValue = Tristate.TRUE;
+            }
+
+            Component flagTypeText = TextComponent.empty();
+            if (flagType == MenuType.OVERRIDE) {
+                flagTypeText = TextComponent.of("OVERRIDE", TextColor.RED);
+            } else if (flagType == MenuType.DEFAULT) {
+                flagTypeText = TextComponent.of("DEFAULT", TextColor.LIGHT_PURPLE);
+            } else if (flagType == MenuType.CLAIM) {
+                flagTypeText = TextComponent.of("CLAIM", TextColor.GOLD);
+            }
+            String target = flagPermission.replace(GDPermissions.FLAG_BASE + ".",  "");
+            Set<Context> newContexts = new HashSet<>(contexts);
+            PermissionUtil.getInstance().setPermissionValue(GriefDefenderPlugin.DEFAULT_HOLDER, flagPermission, newValue, newContexts);
+            TextAdapter.sendComponent(src, TextComponent.builder("")
+                    .append("Set ", TextColor.GREEN)
+                    .append(flagTypeText)
+                    .append(" permission ")
+                    .append(target, TextColor.AQUA)
+                    .append("\n to ", TextColor.GREEN)
+                    .append(getClickableText(src, (GDClaim) claim, subject, newContexts, flagPermission, newValue, flagType).color(TextColor.LIGHT_PURPLE))
+                    .append(" for ", TextColor.GREEN)
+                    .append(subject.getFriendlyName(), TextColor.GOLD).build());
+        };
+    }
+
+    public static Consumer<CommandSource> createCommandConsumer(CommandSource src, String command, String arguments) {
+        return createCommandConsumer(src, command, arguments, null);
+    }
+
+    public static Consumer<CommandSource> createCommandConsumer(CommandSource src, String command, String arguments, Consumer<CommandSource> postConsumerTask) {
+        return consumer -> {
+            try {
+                Sponge.getCommandManager().get(command).get().getCallable().process(src, arguments);
+            } catch (CommandException e) {
+                src.sendMessage(e.getText());
+            }
+            if (postConsumerTask != null) {
+                postConsumerTask.accept(src);
+            }
+        };
+    }
+
+    public static void executeCommand(CommandSource src, String command, String arguments) {
+        try {
+            Sponge.getCommandManager().get(command).get().getCallable().process(src, arguments);
+        } catch (CommandException e) {
+            src.sendMessage(e.getText());
+        }
+    }
+
+    public static void showClaims(CommandSource src, Set<Claim> claims) {
+        if (claims.isEmpty()) {
+            // do nothing
+            return;
+        }
+        showClaims(src, claims, 0, false);
+    }
+
+    public static void showOverlapClaims(CommandSource src, Set<Claim> claims, int height) {
+        showClaims(src, claims, height, true, true);
+    }
+
+    public static void showClaims(CommandSource src, Set<Claim> claims, int height, boolean visualizeClaims) {
+        showClaims(src, claims, height, visualizeClaims, false);
+    }
+
+    public static void showClaims(CommandSource src, Set<Claim> claims, int height, boolean visualizeClaims, boolean overlap) {
+        final String worldName = src instanceof Player ? ((Player) src).getWorld().getName() : Sponge.getServer().getDefaultWorldName();
+        final boolean canListOthers = src.hasPermission(GDPermissions.LIST_OTHER_CLAIMS);
+        List<Component> claimsTextList = generateClaimTextList(new ArrayList<Component>(), claims, worldName, null, src, createShowClaimsConsumer(src, claims, height, visualizeClaims), canListOthers, false, overlap);
+
+        if (visualizeClaims && src instanceof Player) {
+            Player player = (Player) src;
+            final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+            if (claims.size() > 1) {
+                if (height != 0) {
+                    height = playerData.lastValidInspectLocation != null ? playerData.lastValidInspectLocation.getBlockY() : player.getProperty(EyeLocationProperty.class).get().getValue().getFloorY();
+                }
+                ClaimVisual visualization = ClaimVisual.fromClaims(claims, playerData.getClaimCreateMode() == CreateModeTypes.VOLUME ? height : player.getProperty(EyeLocationProperty.class).get().getValue().getFloorY(), player.getLocation(), playerData, null);
+                visualization.apply(player);
+            } else {
+                for (Claim claim : claims) {
+                    GDClaim gpClaim = (GDClaim) claim;
+                    gpClaim.getVisualizer().createClaimBlockVisuals(height, player.getLocation(), playerData);
+                    gpClaim.getVisualizer().apply(player);
+                }
+            }
+        }
+
+        PaginationList.Builder builder = PaginationList.builder().title(MessageCache.getInstance().CLAIMLIST_UI_TITLE.color(TextColor.RED)).padding(TextComponent.builder(" ").decoration(TextDecoration.STRIKETHROUGH, true).build()).contents(claimsTextList);
+        builder.sendTo(src);
+    }
+
+    private static Consumer<CommandSource> createShowClaimsConsumer(CommandSource src, Set<Claim> claims, int height, boolean visualizeClaims) {
+        return consumer -> {
+            showClaims(src, claims, height, visualizeClaims);
+        };
+    }
+
+    public static List<Component> generateClaimTextListCommand(List<Component> claimsTextList, Set<Claim> claimList, String worldName, GDPermissionUser user, CommandSource src, Consumer<CommandSource> returnCommand, boolean listChildren) {
+        return generateClaimTextList(claimsTextList, claimList, worldName, user, src, returnCommand, listChildren, false, true);
+    }
+
+    public static List<Component> generateClaimTextList(List<Component> claimsTextList, Set<Claim> claimList, String worldName, GDPermissionUser user, CommandSource src, Consumer<CommandSource> returnCommand, boolean listChildren) {
+        return generateClaimTextList(claimsTextList, claimList, worldName, user, src, returnCommand, listChildren, false, false);
+    }
+
+    public static List<Component> generateClaimTextList(List<Component> claimsTextList, Set<Claim> claimList, String worldName, GDPermissionUser user, CommandSource src, Consumer<CommandSource> returnCommand, boolean listChildren, boolean overlap, boolean listCommand) {
+        if (claimList.size() > 0) {
+            for (Claim playerClaim : claimList) {
+                GDClaim claim = (GDClaim) playerClaim;
+                if (!listCommand && !overlap && !listChildren && claim.isSubdivision() && !claim.getData().getEconomyData().isForSale()) {
+                    continue;
+                }
+
+                double teleportHeight = claim.getOwnerPlayerData() == null ? 65.0D : (claim.getOwnerMinClaimLevel() > 65.0D ? claim.getOwnerMinClaimLevel() : 65);
+                Vector3i lesserPos = claim.lesserBoundaryCorner;
+                Vector3i greaterPos = claim.greaterBoundaryCorner;
+                Vector3i center = claim.lesserBoundaryCorner.add(lesserPos.getX(), lesserPos.getY(), lesserPos.getZ()).div(2);
+                Vector3i newCenter = new Vector3i(center.getX(), teleportHeight, center.getZ());
+                Vector3i southWest = new Vector3i(newCenter.getX(), newCenter.getY(), newCenter.getZ());
+                //final double teleportHeight = claim.getOwnerPlayerData() == null ? 65.0D : (claim.getOwnerPlayerData().getMinClaimLevel() > 65.0D ? claim.getOwnerPlayerData().getMinClaimLevel() : 65);
+                //Location<World> southWest = claim.lesserBoundaryCorner.setPosition(new Vector3d(claim.lesserBoundaryCorner.getPosition().getX(), teleportHeight, claim.greaterBoundaryCorner.getPosition().getZ()));
+                Component claimName = claim.getData().getName().orElse(TextComponent.empty());
+                Component teleportName = claim.getData().getName().orElse(claim.getFriendlyNameType());
+                Component ownerLine = TextComponent.builder()
+                        .append(MessageCache.getInstance().LABEL_OWNER.color(TextColor.YELLOW))
+                        .append(" : ", TextColor.WHITE)
+                        .append(claim.getOwnerName().color(TextColor.GOLD))
+                        .append("\n").build();
+                Component claimTypeInfo = TextComponent.builder("Type").color(TextColor.YELLOW)
+                        .append(" : ", TextColor.WHITE)
+                        .append(claim.getFriendlyNameType())
+                        .append(" ")
+                        .append(claim.isCuboid() ? "3D " : "2D ", TextColor.GRAY)
+                        .append(" (")
+                        .append(MessageCache.getInstance().LABEL_AREA)
+                        .append(": ", TextColor.WHITE)
+                        .append(String.valueOf(claim.getClaimBlocks()), TextColor.GRAY)
+                        .append(" blocks)\n", TextColor.WHITE).build();
+                Component clickInfo = MessageCache.getInstance().CLAIMLIST_UI_CLICK_INFO;
+                Component basicInfo = TextComponent.builder("")
+                        .append(ownerLine)
+                        .append(claimTypeInfo)
+                        .append(clickInfo).build();
+
+                Component claimInfoCommandClick = TextComponent.builder("")
+                        .append(claim.getFriendlyNameType())
+                        .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(CommandHelper.createCommandConsumer(src, "claiminfo", claim.getUniqueId().toString(), createReturnClaimListConsumer(src, returnCommand)))))
+                        .hoverEvent(HoverEvent.showText(basicInfo)).build();
+
+                Component claimCoordsTPClick = TextComponent.builder("")
+                        .append("[")
+                        .append("TP", TextColor.LIGHT_PURPLE)
+                        .append("]")
+                        .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(CommandHelper.createTeleportConsumer(src, VecHelper.toLocation(claim.getWorld(), southWest), claim))))
+                        .hoverEvent(HoverEvent.showText(MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.CLAIMLIST_UI_CLICK_TELEPORT_TARGET,
+                                ImmutableMap.of(
+                                    "name", teleportName,
+                                    "target", southWest.toString(),
+                                    "world", claim.getWorld().getName()))))
+                        .build();
+
+                Component claimSpawn = null;
+                if (claim.getData().getSpawnPos().isPresent()) {
+                    Vector3i spawnPos = claim.getData().getSpawnPos().get();
+                    Location<World> spawnLoc = new Location<>(claim.getWorld(), spawnPos.getX(), spawnPos.getY(), spawnPos.getZ());
+                    claimSpawn = TextComponent.builder("")
+                            .append("[")
+                            .append("TP", TextColor.LIGHT_PURPLE)
+                            .append("]")
+                            .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(CommandHelper.createTeleportConsumer(src, spawnLoc, claim, true))))
+                            .hoverEvent(HoverEvent.showText(MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.CLAIMLIST_UI_CLICK_TELEPORT_TARGET,
+                                    ImmutableMap.of(
+                                            "name", teleportName,
+                                            "target", "'s spawn @ " + spawnPos.toString(),
+                                            "world", claim.getWorld().getName()))))
+                            .build();
+                } else {
+                    claimSpawn = claimCoordsTPClick;
+                }
+
+                List<Component> childrenTextList = new ArrayList<>();
+                if (!listChildren) {
+                    childrenTextList = generateClaimTextList(new ArrayList<Component>(), claim.getChildren(true), worldName, user, src, returnCommand, true);
+                }
+                final Player player = src instanceof Player ? (Player) src : null;
+                Component buyClaim = TextComponent.empty();
+                if (player != null && claim.getEconomyData().isForSale() && claim.getEconomyData().getSalePrice() > -1) {
+                    Component buyInfo = TextComponent.builder()
+                            .append(MessageCache.getInstance().LABEL_PRICE.color(TextColor.AQUA))
+                            .append(" : ", TextColor.WHITE)
+                            .append(String.valueOf(claim.getEconomyData().getSalePrice()), TextColor.GOLD)
+                            .append("\n")
+                            .append(MessageCache.getInstance().CLAIMLIST_UI_CLICK_PURCHASE).build();
+                    buyClaim = TextComponent.builder()
+                        .append(claim.getEconomyData().isForSale() ? TextComponent.builder(" [").append(MessageCache.getInstance().LABEL_BUY.color(TextColor.GREEN)).append("]", TextColor.WHITE).build() : TextComponent.empty())
+                        .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(buyClaimConsumerConfirmation(src, claim))))
+                        .hoverEvent(HoverEvent.showText(player.getUniqueId().equals(claim.getOwnerUniqueId()) ? MessageCache.getInstance().CLAIM_OWNER_ALREADY : buyInfo)).build();
+                }
+                if (!childrenTextList.isEmpty()) {
+                    Component children = TextComponent.builder("[")
+                            .append(MessageCache.getInstance().LABEL_CHILDREN.color(TextColor.AQUA))
+                            .append("]", TextColor.WHITE)
+                            .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(showChildrenList(childrenTextList, src, returnCommand, claim))))
+                            .hoverEvent(HoverEvent.showText(MessageCache.getInstance().CLAIMLIST_UI_CLICK_VIEW_CHILDREN)).build();
+                    claimsTextList.add(TextComponent.builder("")
+                            .append(claimSpawn)
+                            .append(" ")
+                            .append(claimInfoCommandClick)
+                            .append(" : ", TextColor.WHITE)
+                            .append(claim.getOwnerName().color(TextColor.GOLD))
+                            .append(" ")
+                            .append(claimName == TextComponent.empty() ? TextComponent.of("") : claimName)
+                            .append(" ")
+                            .append(children)
+                            .append(" ")
+                            .append(buyClaim)
+                            .build());
+                } else {
+                   claimsTextList.add(TextComponent.builder("")
+                           .append(claimSpawn)
+                           .append(" ")
+                           .append(claimInfoCommandClick)
+                           .append(" : ", TextColor.WHITE)
+                           .append(claim.getOwnerName().color(TextColor.GOLD))
+                           .append(" ")
+                           .append(claimName == TextComponent.empty() ? TextComponent.of("") : claimName)
+                           .append(buyClaim)
+                           .build());
+                }
+            }
+            if (claimsTextList.size() == 0) {
+                claimsTextList.add(MessageCache.getInstance().CLAIMLIST_UI_NO_CLAIMS_FOUND.color(TextColor.RED));
+            }
+        }
+        return claimsTextList;
+    }
+
+    public static Consumer<CommandSource> buyClaimConsumerConfirmation(CommandSource src, Claim claim) {
+        return confirm -> {
+            final Player player = (Player) src;
+            if (player.getUniqueId().equals(claim.getOwnerUniqueId())) {
+                return;
+            }
+            Account playerAccount = GriefDefenderPlugin.getInstance().economyService.get().getOrCreateAccount(player.getUniqueId()).orElse(null);
+            if (playerAccount == null) {
+                final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.ECONOMY_PLAYER_NOT_FOUND, ImmutableMap.of(
+                        "player", player.getName()));
+                GriefDefenderPlugin.sendMessage(player, message);
+                return;
+            }
+
+            final double balance = playerAccount.getBalance(GriefDefenderPlugin.getInstance().economyService.get().getDefaultCurrency()).doubleValue();
+            if (balance < claim.getEconomyData().getSalePrice()) {
+                Map<String, Object> params = ImmutableMap.of(
+                        "amount", claim.getEconomyData().getSalePrice(),
+                        "balance", balance,
+                        "amount_required", claim.getEconomyData().getSalePrice() -  balance);
+                GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.ECONOMY_CLAIM_BUY_NOT_ENOUGH_FUNDS, params));
+                return;
+            }
+            final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.ECONOMY_CLAIM_BUY_CONFIRMATION,
+                    ImmutableMap.of("amount", "$" + claim.getEconomyData().getSalePrice()));
+            final Component buyConfirmationText = TextComponent.builder()
+                    .append(message)
+                    .append(TextComponent.builder()
+                        .append("\n[")
+                        .append(MessageCache.getInstance().LABEL_CONFIRM.color(TextColor.GREEN))
+                        .append("]\n")
+                        .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createBuyConsumerConfirmed(src, claim)))).build())
+                    .build();
+            GriefDefenderPlugin.sendMessage(player, buyConfirmationText);
+        };
+    }
+
+    private static Consumer<CommandSource> createBuyConsumerConfirmed(CommandSource src, Claim claim) {
+        return confirm -> {
+            final Player player = (Player) src;
+            final GDPermissionUser owner = PermissionHolderCache.getInstance().getOrCreateUser(claim.getOwnerUniqueId());
+            final Account ownerAccount = GriefDefenderPlugin.getInstance().economyService.get().getOrCreateAccount(claim.getOwnerUniqueId()).orElse(null);
+            if (ownerAccount == null) {
+                final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.ECONOMY_PLAYER_NOT_FOUND, ImmutableMap.of(
+                        "player", player.getName()));
+                GriefDefenderPlugin.sendMessage(player, message);
+                return;
+            }
+
+            final ClaimResult result = claim.transferOwner(player.getUniqueId());
+            if (!result.successful()) {
+                final Component defaultMessage = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.ECONOMY_CLAIM_BUY_TRANSFER_CANCELLED,
+                        ImmutableMap.of(
+                            "owner", owner.getName(),
+                            "player", player.getName()));
+                TextAdapter.sendComponent(src, result.getMessage().orElse(defaultMessage));
+                return;
+            }
+
+            try (CauseStackManager.StackFrame frame = Sponge.getCauseStackManager().pushCauseFrame()) {
+                final Currency defaultCurrency = GriefDefenderPlugin.getInstance().economyService.get().getDefaultCurrency();
+                final double salePrice = claim.getEconomyData().getSalePrice();
+                Sponge.getCauseStackManager().pushCause(src);
+                final TransactionResult ownerResult = ownerAccount.deposit(defaultCurrency, BigDecimal.valueOf(salePrice), Sponge.getCauseStackManager().getCurrentCause());
+                Account playerAccount = GriefDefenderPlugin.getInstance().economyService.get().getOrCreateAccount(player.getUniqueId()).orElse(null);
+                final TransactionResult
+                    transactionResult =
+                    playerAccount.withdraw(defaultCurrency, BigDecimal.valueOf(salePrice), Sponge.getCauseStackManager().getCurrentCause());
+                final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.ECONOMY_CLAIM_BUY_CONFIRMED,
+                    ImmutableMap.of(
+                        "amount", String.valueOf("$" +salePrice)));
+                final Component saleMessage = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.ECONOMY_CLAIM_SOLD,
+                    ImmutableMap.of(
+                        "amount", String.valueOf("$" +salePrice),
+                        "balance",String.valueOf("$" + playerAccount.getBalance(defaultCurrency))));
+                if (owner.getOnlinePlayer() != null) {
+                    TextAdapter.sendComponent(owner.getOnlinePlayer(), saleMessage);
+                }
+                claim.getEconomyData().setForSale(false);
+                claim.getEconomyData().setSalePrice(0);
+                claim.getData().save();
+                GriefDefenderPlugin.sendMessage(src, message);
+            }
+        };
+    }
+
+    public static Consumer<CommandSource> showChildrenList(List<Component> childrenTextList, CommandSource src, Consumer<CommandSource> returnCommand, GDClaim parent) {
+        return consumer -> {
+            Component claimListReturnCommand = TextComponent.builder("")
+                    .append("\n[")
+                    .append(MessageCache.getInstance().CLAIMLIST_UI_RETURN_CLAIMSLIST.color(TextColor.AQUA))
+                    .append("]\n", TextColor.WHITE)
+                .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(returnCommand))).build();
+    
+            List<Component> textList = new ArrayList<>();
+            textList.add(claimListReturnCommand);
+            textList.addAll(childrenTextList);
+            PaginationList.Builder builder = PaginationList.builder()
+                    .title(parent.getName().orElse(parent.getFriendlyNameType())
+                            .append(TextComponent.of(" ").append(MessageCache.getInstance().CLAIMLIST_UI_TITLE_CHILD_CLAIMS))).padding(TextComponent.builder(" ").decoration(TextDecoration.STRIKETHROUGH, true).build()).contents(textList);
+            builder.sendTo(src);
+        };
+    }
+
+    public static Consumer<CommandSource> createReturnClaimListConsumer(CommandSource src, Consumer<CommandSource> returnCommand) {
+        return consumer -> {
+            Component claimListReturnCommand = TextComponent.builder("")
+                    .append("\n[")
+                    .append(MessageCache.getInstance().CLAIMLIST_UI_RETURN_CLAIMSLIST.color(TextColor.AQUA))
+                    .append("]\n", TextColor.WHITE)
+                .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(returnCommand))).build();
+            TextAdapter.sendComponent(src, claimListReturnCommand);
+        };
+    }
+
+    public static Consumer<CommandSource> createReturnClaimListConsumer(CommandSource src, String arguments) {
+        return consumer -> {
+            Component claimListReturnCommand = TextComponent.builder("")
+                    .append("\n[")
+                    .append(MessageCache.getInstance().CLAIMLIST_UI_RETURN_CLAIMSLIST.color(TextColor.AQUA))
+                    .append("]\n", TextColor.WHITE)
+                .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(CommandHelper.createCommandConsumer(src, "/claimslist", arguments)))).build();
+            TextAdapter.sendComponent(src, claimListReturnCommand);
+        };
+    }
+
+    public static Consumer<CommandSource> createFlagConsumer(CommandSource src, GDPermissionHolder subject, String subjectName, Set<Context> contexts, GDClaim claim, String flagPermission, Tristate flagValue, String source) {
+        return consumer -> {
+            String target = flagPermission.replace(GDPermissions.FLAG_BASE + ".", "");
+            if (target.isEmpty()) {
+                target = "any";
+            }
+            Tristate newValue = Tristate.UNDEFINED;
+            if (flagValue == Tristate.TRUE) {
+                newValue = Tristate.FALSE;
+            } else if (flagValue == Tristate.UNDEFINED) {
+                newValue = Tristate.TRUE;
+            }
+
+            CommandHelper.applyFlagPermission(src, subject, claim, flagPermission, target, newValue, null, MenuType.GROUP);
+        };
+    }
+
+    public static Component getClickableText(CommandSource src, GDClaim claim, Subject subject, Set<Context> contexts, String flagPermission, Tristate flagValue, MenuType type) {
+        TextComponent.Builder textBuilder = TextComponent.builder(flagValue.toString().toLowerCase())
+                .hoverEvent(HoverEvent.showText(TextComponent.builder("")
+                        .append(MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.CLAIMLIST_UI_CLICK_TOGGLE_VALUE,
+                                ImmutableMap.of("type", type.name().toLowerCase())))
+                        .append("\n")
+                        .append(UIHelper.getPermissionMenuTypeHoverText(type)).build()))
+                .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createFlagConsumer(src, claim, subject, contexts, flagPermission, flagValue, type))));
+        return textBuilder.build();
+    }
+
+    public static Component getClickableText(CommandSource src, GDPermissionHolder subject, String subjectName, Set<Context> contexts, GDClaim claim, String flagPermission, Tristate flagValue, String source, MenuType type) {
+        Component onClickText = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.CLAIMLIST_UI_CLICK_TOGGLE_VALUE,
+                ImmutableMap.of("type", "flag"));
+        boolean hasPermission = true;
+        if (type == MenuType.INHERIT) {
+            onClickText = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.FLAG_UI_INHERIT_PARENT,
+                    ImmutableMap.of("name", claim.getFriendlyNameType()));
+            hasPermission = false;
+        } else if (src instanceof Player) {
+            Component denyReason = claim.allowEdit((Player) src);
+            if (denyReason != null) {
+                onClickText = denyReason;
+                hasPermission = false;
+            }
+        }
+
+        TextComponent.Builder textBuilder = TextComponent.builder(flagValue.toString().toLowerCase())
+                .hoverEvent(HoverEvent.showText(TextComponent.builder("")
+                        .append(onClickText)
+                        .append("\n")
+                        .append(UIHelper.getPermissionMenuTypeHoverText(type)).build()));
+        if (hasPermission) {
+            textBuilder.clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createFlagConsumer(src, subject, subjectName, contexts, claim, flagPermission, flagValue, source))));
+        }
+        return textBuilder.build();
+    }
+
+    public static String handleCommandFlag(CommandSource src, String target) {
+        String pluginId = "minecraft";
+        String args = "";
+        String command = "";
+        int argsIndex = target.indexOf("[");
+        if (argsIndex != -1) {
+            if (argsIndex == 0) {
+                // invalid
+                TextAdapter.sendComponent(src, MessageCache.getInstance().COMMAND_INVALID);
+                return null;
+            }
+            command = target.substring(0, argsIndex);
+            String[] parts = command.split(":");
+            if (parts.length > 1) {
+                pluginId = parts[0];
+                command = parts[1];
+            }
+            if (!validateCommandMapping(src, command, pluginId)) {
+                return null;
+            }
+            if (!pluginId.equals("minecraft")) {
+                PluginContainer pluginContainer = Sponge.getPluginManager().getPlugin(pluginId).orElse(null);
+                if (pluginContainer == null) {
+                    TextAdapter.sendComponent(src, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.PLUGIN_NOT_FOUND, 
+                            ImmutableMap.of("id", pluginId)));
+                    return null;
+                }
+            }
+            args = target.substring(argsIndex, target.length());
+            Pattern p = Pattern.compile("\\[([^\\]]+)\\]");
+            Matcher m = p.matcher(args);
+            if (!m.find()) {
+                // invalid
+                TextAdapter.sendComponent(src, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.COMMAND_EXECUTE_FAILED,
+                        ImmutableMap.of(
+                                "command", command,
+                                "args", args)));
+                return null;
+            }
+            args = m.group(1);
+            target = pluginId + ":" + command + "." + args.replace(":", ".");
+        } else {
+            String[] parts = target.split(":");
+            if (parts.length > 1) {
+                pluginId = parts[0];
+                command = parts[1];
+            } else {
+                command = target;
+            }
+            target = pluginId + ":" + command;
+        }
+
+        // validate command
+        if (!validateCommandMapping(src, command, pluginId)) {
+            return null;
+        }
+
+        return target;
+    }
+
+    private static boolean validateCommandMapping(CommandSource src, String command, String pluginId) {
+        CommandMapping commandMapping = Sponge.getCommandManager().get(command).orElse(null);
+        if (commandMapping == null) {
+            TextAdapter.sendComponent(src, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.PLUGIN_COMMAND_NOT_FOUND,
+                    ImmutableMap.of(
+                        "command", command,
+                        "id", pluginId)));
+            return false;
+        }
+        return true;
+    }
+
+    public static String getTrustPermission(TrustType trustType) {
+        if (trustType == TrustTypes.ACCESSOR) {
+            return GDPermissions.TRUST_ACCESSOR;
+        } else if (trustType == TrustTypes.CONTAINER) {
+            return GDPermissions.TRUST_CONTAINER;
+        } else if (trustType == TrustTypes.BUILDER) {
+            return GDPermissions.TRUST_BUILDER;
+        } else {
+            return GDPermissions.TRUST_MANAGER;
+        }
+    }
+
+    public static Consumer<CommandSource> createTeleportConsumer(CommandSource src, Location<World> location, Claim claim) {
+        return createTeleportConsumer(src, location, claim, false);
+    }
+
+    public static Consumer<CommandSource> createTeleportConsumer(CommandSource src, Location<World> location, Claim claim, boolean isClaimSpawn) {
+        return teleport -> {
+            if (!(src instanceof Player)) {
+                // ignore
+                return;
+            }
+            Player player = (Player) src;
+            GDClaim gpClaim = (GDClaim) claim;
+            GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getPlayerData(player.getWorld(), player.getUniqueId());
+            if (!playerData.canIgnoreClaim(gpClaim) && !playerData.canManageAdminClaims) {
+                // if not owner of claim, validate perms
+                if (!player.getUniqueId().equals(claim.getOwnerUniqueId())) {
+                    if (!player.hasPermission(GDPermissions.COMMAND_CLAIM_INFO_TELEPORT_OTHERS) && !gpClaim.isUserTrusted(player, TrustTypes.ACCESSOR)) {
+                        TextAdapter.sendComponent(player, MessageCache.getInstance().CLAIMINFO_UI_TELEPORT_FEATURE.color(TextColor.RED)); 
+                        return;
+                    }
+                } else if (!player.hasPermission(GDPermissions.COMMAND_CLAIM_INFO_TELEPORT_BASE)) {
+                    TextAdapter.sendComponent(player, MessageCache.getInstance().CLAIMINFO_UI_TELEPORT_FEATURE.color(TextColor.RED)); 
+                    return;
+                }
+            }
+
+            final int teleportDelay = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), player, Options.PLAYER_TELEPORT_DELAY, claim);
+            if (isClaimSpawn) {
+                if (teleportDelay > 0) {
+                    playerData.teleportDelay = teleportDelay + 1;
+                    playerData.teleportSourceLocation = player.getLocation();
+                    playerData.teleportLocation = location;
+                    return;
+                }
+
+                player.setLocation(location);
+                return;
+            }
+
+            Location<World> safeLocation = Sponge.getGame().getTeleportHelper().getSafeLocation(location, 64, 16).orElse(null);
+            if (safeLocation == null) {
+                TextAdapter.sendComponent(player, TextComponent.builder("")
+                        .append("Location is not safe. ", TextColor.RED)
+                        .append(TextComponent.builder("")
+                                .append("Are you sure you want to teleport here?", TextColor.GREEN)
+                                .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createForceTeleportConsumer(player, location)))).decoration(TextDecoration.UNDERLINED, true).build()).build());
+            } else {
+                player.setLocation(safeLocation);
+            }
+
+           // TextAdapter.sendComponent(player, MessageCache.getInstance().TELEPORT_NO_SAFE_LOCATION);
+        };
+    }
+
+    public static Consumer<CommandSource> createForceTeleportConsumer(Player player, Location<World> location) {
+        return teleport -> {
+            player.setLocation(location);
+        };
+    }
+
+    public static void handleBankTransaction(CommandSource src, String[] args, GDClaim claim) {
+        final EconomyService economyService = GriefDefenderPlugin.getInstance().economyService.orElse(null);
+        if (economyService == null) {
+            GriefDefenderPlugin.sendMessage(src, MessageCache.getInstance().ECONOMY_NOT_INSTALLED);
+            return;
+        }
+
+        if (claim.isSubdivision() || claim.isAdminClaim()) {
+            return;
+        }
+
+        Account bankAccount = null;//claim.getEconomyAccount().orElse(null);
+        if (bankAccount == null) {
+            GriefDefenderPlugin.sendMessage(src, MessageCache.getInstance().ECONOMY_VIRTUAL_NOT_SUPPORTED);
+            return;
+        }
+
+        final String command = args[0];
+        double amount = 0;
+        try {
+            amount = Double.valueOf(args[1]);
+        } catch (NumberFormatException e) {
+            
+        }
+
+        final UUID playerSource = ((Player) src).getUniqueId();
+        final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getPlayerData(claim.getWorld(), claim.getOwnerUniqueId());
+        if (playerData.canIgnoreClaim(claim) || claim.getOwnerUniqueId().equals(playerSource) || claim.getUserTrusts(TrustTypes.MANAGER).contains(playerData.playerID)) {
+            final UniqueAccount playerAccount = economyService.getOrCreateAccount(playerData.playerID).get();
+            try (final CauseStackManager.StackFrame frame = Sponge.getCauseStackManager().pushCauseFrame()) {
+                Sponge.getCauseStackManager().pushCause(src);
+                Sponge.getCauseStackManager().addContext(GriefDefenderPlugin.PLUGIN_CONTEXT, GriefDefenderPlugin.getInstance());
+                if (command.equalsIgnoreCase("withdraw")) {
+                    TransactionResult
+                        result =
+                        bankAccount.withdraw(economyService.getDefaultCurrency(), BigDecimal.valueOf(amount), Sponge.getCauseStackManager().getCurrentCause());
+                    if (result.getResult() == ResultType.SUCCESS) {
+                        final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.BANK_WITHDRAW,
+                            ImmutableMap.of(
+                                "amount", amount));
+                        GriefDefenderPlugin.sendMessage(src, message);
+                        playerAccount.deposit(economyService.getDefaultCurrency(), BigDecimal.valueOf(amount), Sponge.getCauseStackManager().getCurrentCause());
+                        claim.getData().getEconomyData().addBankTransaction(
+                            new GDBankTransaction(BankTransactionType.WITHDRAW_SUCCESS, playerData.playerID, Instant.now(), amount));
+                    } else {
+                        final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.BANK_WITHDRAW_NO_FUNDS,
+                            ImmutableMap.of(
+                                "balance", bankAccount.getBalance(economyService.getDefaultCurrency()),
+                                "amount", amount));
+                        GriefDefenderPlugin.sendMessage(src, message);
+                        claim.getData().getEconomyData()
+                            .addBankTransaction(new GDBankTransaction(BankTransactionType.WITHDRAW_FAIL, playerData.playerID, Instant.now(), amount));
+                        return;
+                    }
+                } else if (command.equalsIgnoreCase("deposit")) {
+                    TransactionResult
+                        result =
+                        playerAccount.withdraw(economyService.getDefaultCurrency(), BigDecimal.valueOf(amount), Sponge.getCauseStackManager().getCurrentCause());
+                    if (result.getResult() == ResultType.SUCCESS) {
+                        double depositAmount = amount;
+                        if (claim.getData().isExpired()) {
+                            final double taxBalance = claim.getEconomyData().getTaxBalance();
+                            depositAmount -= claim.getEconomyData().getTaxBalance();
+                            if (depositAmount >= 0) {
+                                claim.getEconomyData().addBankTransaction(new GDBankTransaction(BankTransactionType.TAX_SUCCESS, Instant.now(), taxBalance));
+                                claim.getEconomyData().setTaxPastDueDate(null);
+                                claim.getEconomyData().setTaxBalance(0);
+                                claim.getInternalClaimData().setExpired(false);
+                                final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.TAX_CLAIM_PAID_BALANCE,
+                                        ImmutableMap.of(
+                                            "amount", taxBalance));
+                                GriefDefenderPlugin.sendMessage(src, message);
+                                if (depositAmount == 0) {
+                                    return;
+                                }
+                            } else {
+                                final double newTaxBalance = Math.abs(depositAmount);
+                                claim.getEconomyData().setTaxBalance(newTaxBalance);
+                                final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.TAX_CLAIM_PAID_PARTIAL,
+                                       ImmutableMap.of(
+                                            "amount", depositAmount,
+                                            "balance", newTaxBalance));
+                                GriefDefenderPlugin.sendMessage(src, message);
+                                return;
+                            }
+                        }
+                        final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.BANK_DEPOSIT, ImmutableMap.of(
+                                "amount", depositAmount));
+                        GriefDefenderPlugin.sendMessage(src, message);
+                        bankAccount.deposit(economyService.getDefaultCurrency(), BigDecimal.valueOf(depositAmount), Sponge.getCauseStackManager().getCurrentCause());
+                        claim.getData().getEconomyData().addBankTransaction(
+                            new GDBankTransaction(BankTransactionType.DEPOSIT_SUCCESS, playerData.playerID, Instant.now(), depositAmount));
+                    } else {
+                        GriefDefenderPlugin.sendMessage(src, GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.BANK_WITHDRAW_NO_FUNDS));
+                        claim.getData().getEconomyData()
+                            .addBankTransaction(new GDBankTransaction(BankTransactionType.DEPOSIT_FAIL, playerData.playerID, Instant.now(), amount));
+                        return;
+                    }
+                }
+            }
+        } else {
+            final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.BANK_NO_PERMISSION,
+                    ImmutableMap.of(
+                            "player", claim.getOwnerName()));
+            GriefDefenderPlugin.sendMessage(src, message);
+        }
+    }
+
+    public static void displayClaimBankInfo(CommandSource src, GDClaim claim) {
+        displayClaimBankInfo(src, claim, false, false);
+    }
+
+    public static void displayClaimBankInfo(CommandSource src, GDClaim claim, boolean checkTown, boolean returnToClaimInfo) {
+        final EconomyService economyService = GriefDefenderPlugin.getInstance().economyService.orElse(null);
+        if (economyService == null) {
+            GriefDefenderPlugin.sendMessage(src, MessageCache.getInstance().ECONOMY_NOT_INSTALLED);
+            return;
+        }
+
+        if (checkTown && !claim.isInTown()) {
+            GriefDefenderPlugin.sendMessage(src, MessageCache.getInstance().TOWN_NOT_IN);
+            return;
+        }
+
+        if (!checkTown && (claim.isSubdivision() || claim.isAdminClaim())) {
+            return;
+        }
+
+        final GDClaim town = claim.getTownClaim();
+        Account bankAccount = checkTown ? town.getEconomyAccount() : claim.getEconomyAccount();
+        if (bankAccount == null) {
+            GriefDefenderPlugin.sendMessage(src, MessageCache.getInstance().ECONOMY_VIRTUAL_NOT_SUPPORTED);
+            return;
+        }
+
+        final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getPlayerData(claim.getWorld(), claim.getOwnerUniqueId());
+        final double claimBalance = bankAccount.getBalance(economyService.getDefaultCurrency()).doubleValue();
+        double taxOwed = -1;
+        final double playerTaxRate = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Double.class), (Player) src, Options.TAX_RATE, claim);
+        if (checkTown) {
+            if (!town.getOwnerUniqueId().equals(playerData.playerID)) {
+                for (Claim playerClaim : playerData.getInternalClaims()) {
+                    GDClaim playerTown = (GDClaim) playerClaim.getTown().orElse(null);
+                    if (!playerClaim.isTown() && playerTown != null && playerTown.getUniqueId().equals(claim.getUniqueId())) {
+                        taxOwed += playerTown.getClaimBlocks() * playerTaxRate;
+                    }
+                }
+            } else {
+                taxOwed = town.getClaimBlocks() * playerTaxRate;
+            }
+        } else {
+            taxOwed = claim.getClaimBlocks() * playerTaxRate;
+        }
+
+        final GriefDefenderConfig<?> activeConfig = GriefDefenderPlugin.getActiveConfig(claim.getWorld().getProperties());
+        final ZonedDateTime withdrawDate = TaskUtil.getNextTargetZoneDate(activeConfig.getConfig().claim.taxApplyHour, 0, 0);
+        Duration duration = Duration.between(Instant.now().truncatedTo(ChronoUnit.SECONDS), withdrawDate.toInstant()) ;
+        final long s = duration.getSeconds();
+        final String timeLeft = String.format("%d:%02d:%02d", s / 3600, (s % 3600) / 60, (s % 60));
+        final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.BANK_INFO,
+                ImmutableMap.of(
+                "balance", claimBalance,
+                "tax-amount", taxOwed,
+                "time-remaining", timeLeft,
+                "tax-balance", claim.getData().getEconomyData().getTaxBalance()));
+        Component transactions = TextComponent.builder("")
+                .append(MessageCache.getInstance().BANK_TITLE_TRANSACTIONS.color(TextColor.AQUA).decoration(TextDecoration.ITALIC, true))
+                .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createBankTransactionsConsumer(src, claim, checkTown, returnToClaimInfo))))
+                .hoverEvent(HoverEvent.showText(MessageCache.getInstance().BANK_CLICK_VIEW_TRANSACTIONS))
+                .build();
+        List<Component> textList = new ArrayList<>();
+        if (returnToClaimInfo) {
+            textList.add(TextComponent.builder("")
+                    .append("\n[")
+                    .append(MessageCache.getInstance().CLAIMINFO_UI_RETURN_CLAIMINFO.color(TextColor.AQUA))
+                    .append("]\n")
+                    .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(CommandHelper.createCommandConsumer(src, "claiminfo", "")))).build());
+        }
+        textList.add(message);
+        textList.add(transactions);
+        PaginationList.Builder builder = PaginationList.builder()
+                .title(TextComponent.of("Bank Info", TextColor.AQUA)).padding(TextComponent.builder(" ").decoration(TextDecoration.STRIKETHROUGH, true).build()).contents(textList);
+        builder.sendTo(src);
+    }
+
+    public static Consumer<CommandSource> createBankTransactionsConsumer(CommandSource src, GDClaim claim, boolean checkTown, boolean returnToClaimInfo) {
+        return settings -> {
+            List<String> bankTransactions = new ArrayList<>(claim.getData().getEconomyData().getBankTransactionLog());
+            Collections.reverse(bankTransactions);
+            List<Component> textList = new ArrayList<>();
+            textList.add(TextComponent.builder("")
+                    .append("\n[")
+                    .append(MessageCache.getInstance().CLAIMINFO_UI_RETURN_BANKINFO.color(TextColor.AQUA))
+                    .append("]\n")
+                    .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(consumer -> { displayClaimBankInfo(src, claim, checkTown, returnToClaimInfo); }))).build());
+            Gson gson = new Gson();
+            for (String transaction : bankTransactions) {
+                GDBankTransaction bankTransaction = gson.fromJson(transaction, GDBankTransaction.class);
+                final Duration duration = Duration.between(bankTransaction.timestamp, Instant.now().truncatedTo(ChronoUnit.SECONDS)) ;
+                final long s = duration.getSeconds();
+                final GDPermissionUser user = PermissionHolderCache.getInstance().getOrCreateUser(bankTransaction.source);
+                final String timeLeft = String.format("%dh %02dm %02ds", s / 3600, (s % 3600) / 60, (s % 60)) + " ago";
+                textList.add(TextComponent.builder("")
+                        .append(bankTransaction.type.name(), getTransactionColor(bankTransaction.type))
+                        .append(" | ", TextColor.BLUE)
+                        .append(TextComponent.of(String.valueOf(bankTransaction.amount)))
+                        .append(" | ", TextColor.BLUE)
+                        .append(timeLeft, TextColor.GRAY)
+                        .append(user == null ? TextComponent.empty() : TextComponent.builder("")
+                                .append(" | ", TextColor.BLUE)
+                                .append(user.getName(), TextColor.LIGHT_PURPLE)
+                                .build())
+                        .build());
+            }
+            textList.add(TextComponent.builder("")
+                    .append("\n[")
+                    .append(MessageCache.getInstance().CLAIMINFO_UI_RETURN_BANKINFO.color(TextColor.AQUA))
+                    .append("]\n")
+                    .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(CommandHelper.createCommandConsumer(src, "claimbank", "")))).build());
+            PaginationList.Builder builder = PaginationList.builder()
+                    .title(MessageCache.getInstance().BANK_TITLE_TRANSACTIONS.color(TextColor.AQUA)).padding(TextComponent.builder(" ").decoration(TextDecoration.STRIKETHROUGH, true).build()).contents(textList);
+            builder.sendTo(src);
+        };
+    }
+
+    public static TextColor getTransactionColor(BankTransactionType type) {
+        switch (type) {
+            case DEPOSIT_SUCCESS :
+            case TAX_SUCCESS :
+            case WITHDRAW_SUCCESS :
+                return TextColor.GREEN;
+            case DEPOSIT_FAIL :
+            case TAX_FAIL :
+            case WITHDRAW_FAIL :
+                return TextColor.RED;
+            default :
+                return TextColor.GREEN;
+        }
+    }
+
+    public static Component getBaseOptionOverlayText(String option) {
+        String baseFlag = option.replace(GDPermissions.OPTION_BASE + ".", "");
+        int endIndex = baseFlag.indexOf(".");
+        if (endIndex != -1) {
+            baseFlag = baseFlag.substring(0, endIndex);
+        }
+
+        final Option flag = GriefDefender.getRegistry().getType(Option.class, baseFlag).orElse(null);
+        if (flag == null) {
+            return MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.OPTION_NOT_FOUND, ImmutableMap.of(
+                    "option", baseFlag));
+        }
+
+        return flag.getDescription();
+    }
+
+    public static TrustType getTrustType(String type) {
+        switch (type.toLowerCase()) {
+            case "accessor" :
+                return TrustTypes.ACCESSOR;
+            case "builder" :
+                return TrustTypes.BUILDER;
+            case "container" :
+                return TrustTypes.CONTAINER;
+            case "manager" :
+                return TrustTypes.MANAGER;
+            case "none" :
+                return TrustTypes.NONE;
+            default :
+                return null;
+        }
+    }
+
+    public static boolean checkTrustPermission(Player player, TrustType type) {
+        if (type == TrustTypes.ACCESSOR) {
+            return player.hasPermission(GDPermissions.GIVE_ACCESS_TRUST);
+        }
+        if (type == TrustTypes.BUILDER) {
+            return player.hasPermission(GDPermissions.GIVE_BUILDER_TRUST);
+        }
+        if (type == TrustTypes.CONTAINER) {
+            return player.hasPermission(GDPermissions.GIVE_CONTAINER_TRUST);
+        }
+        if (type == TrustTypes.MANAGER) {
+            return player.hasPermission(GDPermissions.GIVE_MANAGER_TRUST);
+        }
+        if (type == TrustTypes.NONE) {
+            return player.hasPermission(GDPermissions.REMOVE_TRUST);
+        }
+
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandPagination.java b/sponge/src/main/java/com/griefdefender/command/CommandPagination.java
new file mode 100644
index 0000000..fb5ed29
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandPagination.java
@@ -0,0 +1,36 @@
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.Description;
+import com.griefdefender.internal.pagination.ActivePagination;
+import com.griefdefender.internal.pagination.GDPaginationHolder;
+import net.kyori.text.TextComponent;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import net.kyori.text.format.TextColor;
+import org.spongepowered.api.command.CommandSource;
+
+public class CommandPagination extends BaseCommand {
+
+    @CommandAlias("gd:pagination")
+    @Description("Used internally by GD for pagination purposes.")
+    public void execute(CommandSource src, String[] args) throws CommandException {
+        String id = args[0];
+        final ActivePagination activePagination = GDPaginationHolder.getInstance().getActivePagination(src, id);
+        if (activePagination == null) {
+            TextAdapter.sendComponent(src, TextComponent.of("Source " + src.getName() + " has no paginations!", TextColor.RED));
+            return;
+        }
+        String action = args[1];
+        if (action.equals("page")) {
+            activePagination.currentPage();
+        } else if (action.equals("next")) {
+            activePagination.nextPage();
+        } else if (action.equals("prev")) {
+            activePagination.previousPage();
+        } else {
+            int page = Integer.parseInt(action);
+            activePagination.specificPage(page);
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandPlayerInfo.java b/sponge/src/main/java/com/griefdefender/command/CommandPlayerInfo.java
new file mode 100644
index 0000000..543840c
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandPlayerInfo.java
@@ -0,0 +1,273 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.InvalidCommandArgument;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandCompletion;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Optional;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.google.common.reflect.TypeToken;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.ClaimBlockSystem;
+import com.griefdefender.api.claim.ClaimTypes;
+import com.griefdefender.api.permission.option.Options;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.internal.pagination.PaginationList;
+import com.griefdefender.permission.GDPermissionManager;
+import com.griefdefender.permission.GDPermissionUser;
+import com.griefdefender.permission.GDPermissions;
+import com.griefdefender.storage.BaseStorage;
+import com.griefdefender.util.PlayerUtil;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import net.kyori.text.format.TextColor;
+import net.kyori.text.format.TextDecoration;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.command.CommandSource;
+import org.spongepowered.api.data.manipulator.mutable.entity.JoinData;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.entity.living.player.User;
+import org.spongepowered.api.world.storage.WorldProperties;
+
+import java.time.format.DateTimeParseException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_PLAYER_INFO_BASE)
+public class CommandPlayerInfo extends BaseCommand {
+
+    @CommandCompletion("@gdplayers @gddummy")
+    @CommandAlias("playerinfo")
+    @Description("Gets information about a player.")
+    @Syntax("[<player>|<player> <world>]")
+    @Subcommand("player info")
+    public void execute(CommandSource src, @Optional String[] args) throws InvalidCommandArgument {
+        GDPermissionUser user = null;
+        WorldProperties worldProperties = null;
+        if (args.length > 0) {
+            user = PermissionHolderCache.getInstance().getOrCreateUser(args[0]);
+            if (user == null) {
+                TextAdapter.sendComponent(src, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.COMMAND_PLAYER_NOT_FOUND,
+                        ImmutableMap.of("player", args[0])));
+                return;
+            }
+            if (args.length > 1) {
+                worldProperties = Sponge.getServer().getWorldProperties(args[1]).orElse(null);
+                if (worldProperties == null) {
+                    TextAdapter.sendComponent(src, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.COMMAND_WORLD_NOT_FOUND,
+                            ImmutableMap.of("world", args[1])));
+                    return;
+                }
+            }
+        }
+
+        if (user == null) {
+            if (!(src instanceof Player)) {
+                GriefDefenderPlugin.sendMessage(src, GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.COMMAND_INVALID_PLAYER));
+                return;
+            }
+
+            user = PermissionHolderCache.getInstance().getOrCreateUser((User) src);
+            if (worldProperties == null) {
+                worldProperties = ((Player) src).getWorld().getProperties();
+            }
+        }
+        if (worldProperties == null) {
+            worldProperties = Sponge.getServer().getDefaultWorld().get();
+        }
+
+        // otherwise if no permission to delve into another player's claims data or self
+        if ((user != null && user != src && !src.hasPermission(GDPermissions.COMMAND_PLAYER_INFO_OTHERS))) {
+           TextAdapter.sendComponent(src, MessageCache.getInstance().PERMISSION_PLAYER_VIEW_OTHERS);
+        }
+
+
+        GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(worldProperties.getUniqueId(), user.getUniqueId());
+        boolean useGlobalData = BaseStorage.USE_GLOBAL_PLAYER_STORAGE;
+        List<Claim> claimList = new ArrayList<>();
+        for (Claim claim : playerData.getInternalClaims()) {
+            if (useGlobalData) {
+                claimList.add(claim);
+            } else {
+                if (claim.getWorldUniqueId().equals(worldProperties.getUniqueId())) {
+                    claimList.add(claim);
+                }
+            }
+        }
+        Component claimSizeLimit = TextComponent.of("none", TextColor.GRAY);
+        if (playerData.getMaxClaimX(ClaimTypes.BASIC) != 0 || playerData.getMaxClaimY(ClaimTypes.BASIC) != 0 || playerData.getMaxClaimZ(ClaimTypes.BASIC) != 0) {
+            claimSizeLimit = TextComponent.of(playerData.getMaxClaimX(ClaimTypes.BASIC) + "," + playerData.getMaxClaimY(ClaimTypes.BASIC) + "," + playerData.getMaxClaimZ(ClaimTypes.BASIC), TextColor.GRAY);
+        }
+
+        final double claimableChunks = GriefDefenderPlugin.CLAIM_BLOCK_SYSTEM == ClaimBlockSystem.VOLUME ? (playerData.getRemainingClaimBlocks() / 65536.0) : (playerData.getRemainingClaimBlocks() / 256.0);
+        final Component uuidText = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.PLAYERINFO_UI_UUID, 
+                ImmutableMap.of("id", user.getUniqueId().toString()));
+        final Component worldText = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.PLAYERINFO_UI_WORLD, 
+                ImmutableMap.of("name", worldProperties.getWorldName()));
+        final Component sizeLimitText = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.PLAYERINFO_UI_CLAIM_SIZE_LIMIT, 
+                ImmutableMap.of("limit", claimSizeLimit));
+        final Component initialBlockText = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.PLAYERINFO_UI_BLOCK_INITIAL, 
+                ImmutableMap.of("amount", String.valueOf(playerData.getInitialClaimBlocks())));
+        final Component accruedBlockText = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.PLAYERINFO_UI_BLOCK_ACCRUED, 
+                ImmutableMap.of(
+                    "amount", String.valueOf(playerData.getAccruedClaimBlocks()),
+                    "block_amount", String.valueOf(playerData.getBlocksAccruedPerHour())));
+        final Component maxAccruedBlockText = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.PLAYERINFO_UI_BLOCK_MAX_ACCRUED, 
+                ImmutableMap.of("amount", String.valueOf(playerData.getMaxAccruedClaimBlocks())));
+        final Component bonusBlockText = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.PLAYERINFO_UI_BLOCK_BONUS, 
+                ImmutableMap.of("amount", String.valueOf(playerData.getBonusClaimBlocks())));
+        final Component remainingBlockText = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.PLAYERINFO_UI_BLOCK_REMAINING, 
+                ImmutableMap.of("amount", String.valueOf(playerData.getRemainingClaimBlocks())));
+        final Component economyBlockAvailablePurchaseText = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.PLAYERINFO_UI_ECONOMY_BLOCK_AVAILABLE_PURCHASE, 
+                ImmutableMap.of("amount", String.valueOf(playerData.getRemainingClaimBlocks())));
+        final Component economyBlockCostText = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.PLAYERINFO_UI_ECONOMY_BLOCK_COST, 
+                ImmutableMap.of("amount", String.valueOf("$" + playerData.getInternalEconomyBlockCost())));
+        final Component economyBlockSellReturnText = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.PLAYERINFO_UI_ECONOMY_BLOCK_SELL_RETURN, 
+                ImmutableMap.of("amount", String.valueOf("$" + playerData.getEconomyClaimBlockReturn())));
+        final Component minMaxLevelText = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.PLAYERINFO_UI_CLAIM_LEVEL, 
+                ImmutableMap.of("level", String.valueOf(playerData.getMinClaimLevel() + "-" + playerData.getMaxClaimLevel())));
+        final Component abandonRatioText = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.PLAYERINFO_UI_ABANDON_RETURN_RATIO, 
+                ImmutableMap.of("ratio", String.valueOf(playerData.getAbandonedReturnRatio(ClaimTypes.BASIC))));
+        final Component totalTaxText = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.PLAYERINFO_UI_TAX_TOTAL, 
+                ImmutableMap.of("amount", String.valueOf(playerData.getInitialClaimBlocks())));
+        final Component totalBlockText = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.PLAYERINFO_UI_BLOCK_TOTAL, 
+                ImmutableMap.of("amount", String.valueOf(playerData.getInitialClaimBlocks())));
+        final Component totalClaimableChunkText = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.PLAYERINFO_UI_CHUNK_TOTAL, 
+                ImmutableMap.of("amount", String.valueOf(Math.round(claimableChunks * 100.0)/100.0)));
+        final Component totalClaimText = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.PLAYERINFO_UI_CLAIM_TOTAL, 
+                ImmutableMap.of("amount", String.valueOf(claimList.size())));
+
+        List<Component> claimsTextList = Lists.newArrayList();
+        claimsTextList.add(uuidText);
+        claimsTextList.add(worldText);
+        claimsTextList.add(sizeLimitText);
+        claimsTextList.add(initialBlockText);
+        claimsTextList.add(accruedBlockText);
+        claimsTextList.add(maxAccruedBlockText);
+        claimsTextList.add(bonusBlockText);
+        if (GriefDefenderPlugin.getInstance().isEconomyModeEnabled()) {
+            claimsTextList.add(economyBlockAvailablePurchaseText);
+            claimsTextList.add(economyBlockCostText);
+            claimsTextList.add(economyBlockSellReturnText);
+        } else {
+            claimsTextList.add(remainingBlockText);
+        }
+        claimsTextList.add(minMaxLevelText);
+        claimsTextList.add(abandonRatioText);
+        final int townLimit = playerData.getCreateClaimLimit(ClaimTypes.TOWN);
+        final int basicLimit = playerData.getCreateClaimLimit(ClaimTypes.BASIC);
+        final int subLimit = playerData.getCreateClaimLimit(ClaimTypes.SUBDIVISION);
+        String townLimitText = townLimit < 0 ? "∞" : String.valueOf(townLimit);
+        String basicLimitText = basicLimit < 0 ? "∞" : String.valueOf(basicLimit);
+        String subLimitText = subLimit < 0 ? "∞" : String.valueOf(subLimit);
+
+        Component claimCreateLimits = TextComponent.builder("")
+                .append("TOWN", TextColor.GRAY)
+                .append(" : ")
+                .append(townLimitText, TextColor.GREEN)
+                .append(" BASIC", TextColor.GRAY)
+                .append(" : ")
+                .append(basicLimitText, TextColor.GREEN)
+                .append(" SUB", TextColor.GRAY)
+                .append(" : ")
+                .append(subLimitText, TextColor.GREEN)
+                .build();
+        claimsTextList.add(claimCreateLimits);
+        if (GriefDefenderPlugin.getGlobalConfig().getConfig().claim.bankTaxSystem) {
+            Component townTaxRate = TextComponent.builder("")
+                    .append("TOWN", TextColor.GRAY)
+                    .append(" : ")
+                    .append(String.valueOf(playerData.getTaxRate(ClaimTypes.TOWN)), TextColor.GREEN)
+                    .build();
+                    // TODO
+                    //TextColors.GRAY, " BASIC", TextColors.WHITE, " : ", TextColors.GREEN, playerData.optionTaxRateTownBasic, 
+                    //TextColors.GRAY, " SUB", TextColors.WHITE, " : ", TextColors.GREEN, playerData.getTaxRate(type));
+            Component claimTaxRate = TextComponent.builder("")
+                    .append("BASIC", TextColor.GRAY)
+                    .append(" : ")
+                    .append(String.valueOf(playerData.getTaxRate(ClaimTypes.BASIC)), TextColor.GREEN)
+                    .append(" SUB", TextColor.GRAY)
+                    .append(" : ")
+                    .append(String.valueOf(playerData.getTaxRate(ClaimTypes.SUBDIVISION)), TextColor.GREEN)
+                    .build();
+            String taxRate = "N/A";
+            if (src instanceof Player) {
+                Player player = (Player) src;
+                if (player.getUniqueId().equals(user.getUniqueId())) {
+                    final GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAt(player.getLocation());
+                    if (claim != null && !claim.isWilderness()) {
+                        final double playerTaxRate = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Double.class), user, Options.TAX_RATE, claim);
+                        taxRate = String.valueOf(playerTaxRate);
+                    }
+                }
+            }
+            final Component currentTaxRateText = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.PLAYERINFO_UI_TAX_CURRENT_RATE, 
+                    ImmutableMap.of("rate", taxRate));
+            final Component globalTownTaxText = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.PLAYERINFO_UI_TAX_GLOBAL_TOWN_RATE, 
+                    ImmutableMap.of("rate", townTaxRate));
+            final Component globalClaimTaxText = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.PLAYERINFO_UI_TAX_GLOBAL_CLAIM_RATE, 
+                    ImmutableMap.of("rate", claimTaxRate));
+            claimsTextList.add(currentTaxRateText);
+            claimsTextList.add(globalTownTaxText);
+            claimsTextList.add(globalClaimTaxText);
+            claimsTextList.add(totalTaxText);
+        }
+        claimsTextList.add(totalBlockText);
+        claimsTextList.add(totalClaimableChunkText);
+        claimsTextList.add(totalClaimText);
+        JoinData joinData = user.getOfflinePlayer().getOrCreate(JoinData.class).orElse(null);
+        if (joinData != null && joinData.lastPlayed().exists()) {
+            Date lastActive = null;
+            try {
+                lastActive = Date.from(joinData.lastPlayed().get());
+            } catch(DateTimeParseException ex) {
+                // ignore
+            }
+            if (lastActive != null) {
+                claimsTextList.add(MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.PLAYERINFO_UI_LAST_ACTIVE, 
+                        ImmutableMap.of("date", lastActive)));
+            }
+        }
+
+        PaginationList.Builder paginationBuilder = PaginationList.builder()
+                .title(MessageCache.getInstance().PLAYERINFO_UI_TITLE).padding(TextComponent.of(" ").decoration(TextDecoration.STRIKETHROUGH, true)).contents(claimsTextList);
+        paginationBuilder.sendTo(src);
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandRestoreClaim.java b/sponge/src/main/java/com/griefdefender/command/CommandRestoreClaim.java
new file mode 100644
index 0000000..3dd00f0
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandRestoreClaim.java
@@ -0,0 +1,96 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.internal.util.BlockUtil;
+import com.griefdefender.permission.GDPermissions;
+import com.griefdefender.text.action.GDCallbackHolder;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import net.kyori.text.event.ClickEvent;
+import net.kyori.text.event.HoverEvent;
+import net.kyori.text.format.TextColor;
+import org.spongepowered.api.command.CommandSource;
+import org.spongepowered.api.entity.living.player.Player;
+
+import java.util.function.Consumer;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_RESTORE_CLAIM)
+public class CommandRestoreClaim extends BaseCommand {
+
+    @CommandAlias("restoreclaim|claimrestore")
+    @Description("Restores claim to its natural state. Use with caution.")
+    @Subcommand("claim restore")
+    public void execute(Player player) {
+        final GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAt(player.getLocation());
+
+        if (claim.isWilderness()) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().CLAIM_NOT_FOUND);
+            return;
+        }
+
+        final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.PERMISSION_CLAIM_DELETE,
+                ImmutableMap.of(
+                "type", claim.getType().getName()));
+
+        if (!player.hasPermission(GDPermissions.DELETE_CLAIM_ADMIN)) {
+            GriefDefenderPlugin.sendMessage(player, message);
+            return;
+        }
+
+        displayConfirmationConsumer(player, claim);
+    }
+
+    private static void displayConfirmationConsumer(CommandSource src, GDClaim claim) {
+        final Component schematicConfirmationText = TextComponent.builder("")
+                .append("Are you sure you want to restore this claim?")
+                .append("\n[")
+                .append("Confirm", TextColor.GREEN)
+                .append("]\n")
+                .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createConfirmationConsumer(src, claim))))
+                .hoverEvent(HoverEvent.showText(TextComponent.of("Clicking confirm will restore ENTIRE claim back to default world gen state. Use cautiously!"))).build();
+        TextAdapter.sendComponent(src, schematicConfirmationText);
+    }
+
+    private static Consumer<CommandSource> createConfirmationConsumer(CommandSource src, GDClaim claim) {
+        return confirm -> {
+            BlockUtil.getInstance().restoreClaim(claim);
+            final Component message = MessageCache.getInstance().CLAIM_RESTORE_SUCCESS;
+            GriefDefenderPlugin.sendMessage(src, message);
+        };
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandRestoreNature.java b/sponge/src/main/java/com/griefdefender/command/CommandRestoreNature.java
new file mode 100644
index 0000000..1e3613f
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandRestoreNature.java
@@ -0,0 +1,56 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.claim.ShovelTypes;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.permission.GDPermissions;
+import org.spongepowered.api.entity.living.player.Player;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_RESTORE_NATURE)
+public class CommandRestoreNature extends BaseCommand {
+
+    @CommandAlias("modenature")
+    @Description("Switches the shovel tool to restoration mode.")
+    @Subcommand("mode nature")
+    public void execute(Player player) {
+        if (true) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().FEATURE_NOT_AVAILABLE);
+            return;
+        }
+
+        final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        playerData.shovelMode = ShovelTypes.RESTORE;
+        GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().MODE_NATURE);
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandSetAccruedClaimBlocks.java b/sponge/src/main/java/com/griefdefender/command/CommandSetAccruedClaimBlocks.java
new file mode 100644
index 0000000..1a73cec
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandSetAccruedClaimBlocks.java
@@ -0,0 +1,115 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.InvalidCommandArgument;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandCompletion;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Optional;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.configuration.MessageDataConfig;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.permission.GDPermissionUser;
+import com.griefdefender.permission.GDPermissions;
+import net.kyori.text.TextComponent;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import net.kyori.text.format.TextColor;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.command.CommandSource;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.world.World;
+import org.spongepowered.api.world.storage.WorldProperties;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_SET_ACCRUED_CLAIM_BLOCKS)
+public class CommandSetAccruedClaimBlocks extends BaseCommand {
+
+    @CommandCompletion("@gdplayers @gddummy")
+    @CommandAlias("scb|setaccruedblocks")
+    @Description("Updates a player's accrued claim block total.")
+    @Syntax("<player> <amount> [world]")
+    @Subcommand("player setaccruedblocks")
+    public void execute(CommandSource src, String player, int amount, @Optional String worldName) throws InvalidCommandArgument {
+        GDPermissionUser user = PermissionHolderCache.getInstance().getOrCreateUser(player);
+        if (user == null) {
+            TextAdapter.sendComponent(src, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.COMMAND_PLAYER_NOT_FOUND,
+                    ImmutableMap.of("player", player)));
+            return;
+        }
+        World world = null;
+        if (worldName != null) {
+            world = Sponge.getServer().getWorld(worldName).orElse(null);
+            if (world == null) {
+                TextAdapter.sendComponent(src, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.COMMAND_WORLD_NOT_FOUND,
+                        ImmutableMap.of("world", worldName)));
+                return;
+            }
+        } else {
+            if (src instanceof Player) {
+                world = ((Player) src).getWorld();
+            } else {
+                // use default
+                world = Sponge.getServer().getWorlds().iterator().next();
+            }
+            if (world == null) {
+                TextAdapter.sendComponent(src, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.COMMAND_WORLD_NOT_FOUND,
+                        ImmutableMap.of("world", worldName)));
+                return;
+            }
+        }
+
+        if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(world.getUniqueId())) {
+            GriefDefenderPlugin.sendMessage(src, MessageCache.getInstance().CLAIM_DISABLED_WORLD);
+            return;
+        }
+
+        // set player's blocks
+        GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(world.getUniqueId(), user.getUniqueId());
+        if (!playerData.setAccruedClaimBlocks(amount)) {
+            TextAdapter.sendComponent(src, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.PLAYER_ACCRUED_BLOCKS_EXCEEDED,
+                    ImmutableMap.of(
+                        "player", user.getName(),
+                        "total", playerData.getAccruedClaimBlocks(),
+                        "amount", amount)));
+            return;
+        }
+
+        playerData.getStorageData().save();
+        GriefDefenderPlugin.sendMessage(src, GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.ADJUST_ACCRUED_BLOCKS_SUCCESS,
+                    ImmutableMap.of(
+                        "player", user.getName(),
+                        "total", playerData.getAccruedClaimBlocks(),
+                        "amount", amount)));
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandTownChat.java b/sponge/src/main/java/com/griefdefender/command/CommandTownChat.java
new file mode 100644
index 0000000..89ec4e4
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandTownChat.java
@@ -0,0 +1,63 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.permission.GDPermissions;
+import org.spongepowered.api.entity.living.player.Player;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_TOWN_CHAT)
+public class CommandTownChat extends BaseCommand {
+
+    @CommandAlias("townchat")
+    @Description("Toggles town chat.")
+    @Subcommand("town chat")
+    public void execute(Player player) {
+        final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        final GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAt(player.getLocation());
+        if (!claim.isInTown()) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().TOWN_NOT_IN);
+            return;
+        }
+
+        playerData.townChat = !playerData.townChat;
+
+        // toggle ignore claims mode on or off
+        if (!playerData.townChat) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().TOWN_CHAT_DISABLED);
+        } else {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().TOWN_CHAT_ENABLED);
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandTownTag.java b/sponge/src/main/java/com/griefdefender/command/CommandTownTag.java
new file mode 100644
index 0000000..2d256b3
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandTownTag.java
@@ -0,0 +1,88 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.permission.GDPermissions;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import net.kyori.text.serializer.legacy.LegacyComponentSerializer;
+import org.spongepowered.api.entity.living.player.Player;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_TOWN_TAG)
+public class CommandTownTag extends BaseCommand {
+
+    @CommandAlias("towntag")
+    @Description("Sets the tag of your town.")
+    @Syntax("<tag>")
+    @Subcommand("town tag")
+    public void execute(Player player, String tag) {
+        final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        final GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(playerData, player.getLocation());
+        if (claim == null || !claim.isInTown()) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().TOWN_NOT_IN);
+            return;
+        }
+
+        final GDClaim town = claim.getTownClaim();
+        final Component result = town.allowEdit(player);
+        if (result != null) {
+            GriefDefenderPlugin.sendMessage(player, result);
+            return;
+        }
+
+        TextComponent name = LegacyComponentSerializer.legacy().deserialize(tag, '&');
+        if (name == TextComponent.empty() || name.content().equals("clear")) {
+            town.getTownData().setTownTag(null);
+        } else {
+            town.getTownData().setTownTag(name);
+        }
+
+        town.getInternalClaimData().setRequiresSave(true);
+        Component resultMessage = null;
+        if (!claim.getTownData().getTownTag().isPresent()) {
+            resultMessage = MessageCache.getInstance().TOWN_TAG_CLEAR;
+        } else {
+            resultMessage = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.TOWN_TAG,
+                    ImmutableMap.of(
+                    "tag", name));
+        }
+        TextAdapter.sendComponent(player, resultMessage);
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandTrustGroup.java b/sponge/src/main/java/com/griefdefender/command/CommandTrustGroup.java
new file mode 100644
index 0000000..b0b5507
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandTrustGroup.java
@@ -0,0 +1,145 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandCompletion;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.GriefDefender;
+import com.griefdefender.api.Tristate;
+import com.griefdefender.api.claim.TrustType;
+import com.griefdefender.api.claim.TrustTypes;
+import com.griefdefender.api.permission.Context;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.MessageDataConfig;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.event.GDCauseStackManager;
+import com.griefdefender.event.GDGroupTrustClaimEvent;
+import com.griefdefender.permission.GDPermissionGroup;
+import com.griefdefender.permission.GDPermissions;
+import com.griefdefender.util.PermissionUtil;
+import net.kyori.text.Component;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import org.spongepowered.api.entity.living.player.Player;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_TRUST_GROUP)
+public class CommandTrustGroup extends BaseCommand {
+
+    @CommandCompletion("@gdgroups @gdtrusttypes @gddummy")
+    @CommandAlias("trustgroup")
+    @Description("Grants a group access to your claim."
+            + "\nAccessor: access to interact with all blocks except inventory."
+            + "\nContainer: access to interact with all blocks including inventory."
+            + "\nBuilder: access to everything above including ability to place and break blocks."
+            + "\nManager: access to everything above including ability to manage claim settings.")
+    @Syntax("<group> <accessor|builder|container|manager>")
+    @Subcommand("trust group")
+    public void execute(Player player, String groupName, String type) {
+        final TrustType trustType = CommandHelper.getTrustType(type);
+        if (trustType == null) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().TRUST_INVALID);
+            return;
+        }
+
+        final GDPermissionGroup group = PermissionHolderCache.getInstance().getOrCreateGroup(groupName);
+        if (group == null) {
+            GriefDefenderPlugin.sendMessage(player, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.COMMAND_INVALID_GROUP, ImmutableMap.of(
+                    "group", groupName)));
+            return;
+        }
+
+        if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(player.getWorld().getUniqueId())) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().CLAIM_DISABLED_WORLD);
+            return;
+        }
+
+        // determine which claim the player is standing in
+        GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(playerData, player.getLocation());
+        if (!playerData.canIgnoreClaim(claim) && claim.allowEdit(player) != null) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().PERMISSION_COMMAND_TRUST);
+            return;
+        }
+
+        //check permission here
+        if(claim.allowGrantPermission(player) != null) {
+            final Component message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.PERMISSION_TRUST,
+                    ImmutableMap.of(
+                    "owner", claim.getOwnerName()));
+            GriefDefenderPlugin.sendMessage(player, message);
+            return;
+        }
+
+        GDCauseStackManager.getInstance().pushCause(player);
+        GDGroupTrustClaimEvent.Remove event =
+            new GDGroupTrustClaimEvent.Remove(claim, ImmutableList.of(group.getName()), TrustTypes.NONE);
+        GriefDefender.getEventManager().post(event);
+        GDCauseStackManager.getInstance().popCause();
+        if (event.cancelled()) {
+            TextAdapter.sendComponent(player, event.getMessage().orElse(MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.TRUST_PLUGIN_CANCEL,
+                    ImmutableMap.of("target", group))));
+            return;
+        }
+
+        final String permission = CommandHelper.getTrustPermission(trustType);
+        Set<Context> contexts = new HashSet<>(); 
+        contexts.add(claim.getContext());
+        final List<String> groupTrustList = claim.getGroupTrustList(trustType);
+        if (!groupTrustList.contains(group.getName())) {
+            groupTrustList.add(group.getName());
+        } else {
+            final Component message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.TRUST_ALREADY_HAS,
+                ImmutableMap.of(
+                    "target", group.getName(),
+                    "type", trustType.getName()));
+            GriefDefenderPlugin.sendMessage(player, message);
+            return;
+        }
+
+        PermissionUtil.getInstance().setPermissionValue(group, permission, Tristate.TRUE, contexts);
+        claim.getInternalClaimData().setRequiresSave(true);
+        claim.getInternalClaimData().save();
+
+        final Component message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.TRUST_GRANT, ImmutableMap.of(
+                "target", group.getName(),
+                "type", trustType.getName()));
+        GriefDefenderPlugin.sendMessage(player, message);
+    }
+}
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandTrustGroupAll.java b/sponge/src/main/java/com/griefdefender/command/CommandTrustGroupAll.java
new file mode 100644
index 0000000..47363a0
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandTrustGroupAll.java
@@ -0,0 +1,132 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandCompletion;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.GriefDefender;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.TrustType;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.MessageDataConfig;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.event.GDCauseStackManager;
+import com.griefdefender.event.GDGroupTrustClaimEvent;
+import com.griefdefender.permission.GDPermissionGroup;
+import com.griefdefender.permission.GDPermissions;
+import net.kyori.text.Component;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import org.spongepowered.api.entity.living.player.Player;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_TRUSTALL_GROUP)
+public class CommandTrustGroupAll extends BaseCommand {
+
+    @CommandCompletion("@gdgroups @gdtrusttypes @gddummy")
+    @CommandAlias("trustallgroup")
+    @Description("Grants a group access to all your claims."
+            + "\nAccessor: access to interact with all blocks except inventory."
+            + "\nContainer: access to interact with all blocks including inventory."
+            + "\nBuilder: access to everything above including ability to place and break blocks."
+            + "\nManager: access to everything above including ability to manage claim settings.")
+    @Syntax("<group> <accessor|builder|container|manager>")
+    @Subcommand("trustall group")
+    public void execute(Player player, String target, String type) {
+        final TrustType trustType = CommandHelper.getTrustType(type);
+        if (trustType == null) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().TRUST_INVALID);
+            return;
+        }
+
+        final GDPermissionGroup group = PermissionHolderCache.getInstance().getOrCreateGroup(target);
+
+        // validate player argument
+        if (group == null) {
+            GriefDefenderPlugin.sendMessage(player, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.COMMAND_INVALID_GROUP, ImmutableMap.of(
+                    "group", target)));
+            return;
+        }
+
+        GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        Set<Claim> claimList = null;
+        if (playerData != null) {
+            claimList = playerData.getInternalClaims();
+        }
+
+        if (playerData == null || claimList == null || claimList.size() == 0) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().TRUST_NO_CLAIMS);
+            return;
+        }
+
+        GDCauseStackManager.getInstance().pushCause(player);
+        GDGroupTrustClaimEvent.Add
+            event = new GDGroupTrustClaimEvent.Add(new ArrayList<>(claimList), ImmutableList.of(group.getName()), trustType);
+        GriefDefender.getEventManager().post(event);
+        GDCauseStackManager.getInstance().popCause();
+        if (event.cancelled()) {
+            TextAdapter.sendComponent(player, event.getMessage().orElse(MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.TRUST_PLUGIN_CANCEL,
+                    ImmutableMap.of("target", group))));
+            return;
+        }
+
+        for (Claim claim : claimList) {
+            this.addAllGroupTrust(claim, group, trustType);
+        }
+
+        final Component message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.TRUST_INDIVIDUAL_ALL_CLAIMS,
+                ImmutableMap.of(
+                "player", group.getName()));
+        GriefDefenderPlugin.sendMessage(player, message);
+    }
+
+    private void addAllGroupTrust(Claim claim, GDPermissionGroup holder, TrustType trustType) {
+        GDClaim gdClaim = (GDClaim) claim;
+        List<String> trustList = gdClaim.getGroupTrustList(trustType);
+        if (!trustList.contains(holder.getFriendlyName())) {
+            trustList.add(holder.getFriendlyName());
+        }
+
+        gdClaim.getInternalClaimData().setRequiresSave(true);
+        gdClaim.getInternalClaimData().save();
+        for (Claim child : gdClaim.children) {
+            this.addAllGroupTrust(child, holder, trustType);
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandTrustList.java b/sponge/src/main/java/com/griefdefender/command/CommandTrustList.java
new file mode 100644
index 0000000..77d8ec5
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandTrustList.java
@@ -0,0 +1,253 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.claim.TrustType;
+import com.griefdefender.api.claim.TrustTypes;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.MessageDataConfig;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.internal.pagination.PaginationList;
+import com.griefdefender.permission.GDPermissionUser;
+import com.griefdefender.permission.GDPermissions;
+import com.griefdefender.text.action.GDCallbackHolder;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.event.ClickEvent;
+import net.kyori.text.event.HoverEvent;
+import net.kyori.text.format.TextColor;
+import net.kyori.text.format.TextDecoration;
+import org.spongepowered.api.command.CommandSource;
+import org.spongepowered.api.entity.living.player.Player;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import java.util.function.Consumer;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_LIST_TRUST)
+public class CommandTrustList extends BaseCommand {
+
+    @CommandAlias("trustlist")
+    @Description("Lists permissions for the claim you're standing in.")
+    @Subcommand("trust list")
+    public void execute(Player player) {
+        final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        final GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(playerData, player.getLocation());
+        showTrustList(player, claim, TrustTypes.NONE);
+    }
+
+    public static void showTrustList(CommandSource src, GDClaim claim, TrustType type) {
+        final Component whiteOpenBracket = TextComponent.of("[", TextColor.AQUA);
+        final Component whiteCloseBracket = TextComponent.of("]", TextColor.AQUA);
+        final Component showAllText = MessageCache.getInstance().TRUST_CLICK_SHOW_LIST;
+        final Component showAccessorText = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.UI_CLICK_FILTER_TYPE,
+                ImmutableMap.of("type", MessageCache.getInstance().TITLE_ACCESSOR.color(TextColor.YELLOW)));
+        final Component showContainerText = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.UI_CLICK_FILTER_TYPE,
+                ImmutableMap.of("type", MessageCache.getInstance().TITLE_CONTAINER.color(TextColor.LIGHT_PURPLE)));
+        final Component showBuilderText = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.UI_CLICK_FILTER_TYPE,
+                ImmutableMap.of("type", MessageCache.getInstance().TITLE_BUILDER.color(TextColor.GREEN)));
+        final Component showManagerText = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.UI_CLICK_FILTER_TYPE,
+                ImmutableMap.of("type", MessageCache.getInstance().TITLE_MANAGER.color(TextColor.GOLD)));
+        final Component allTypeText = TextComponent.builder("")
+                .append(type == TrustTypes.NONE ? TextComponent.builder("")
+                        .append(whiteOpenBracket)
+                        .append("ALL")
+                        .append(whiteCloseBracket)
+                        .build() : TextComponent.builder("")
+                        .append("ALL",TextColor.GRAY)
+                        .build())
+                .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createTrustConsumer(src, claim, TrustTypes.NONE))))
+                .hoverEvent(HoverEvent.showText(showAllText)).build();
+        final Component accessorTrustText = TextComponent.builder("")
+                .append(type == TrustTypes.ACCESSOR ? TextComponent.builder("")
+                        .append(whiteOpenBracket)
+                        .append(MessageCache.getInstance().TITLE_ACCESSOR.color(TextColor.YELLOW))
+                        .append(whiteCloseBracket)
+                        .build() : MessageCache.getInstance().TITLE_ACCESSOR.color(TextColor.GRAY))
+                .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createTrustConsumer(src, claim, TrustTypes.ACCESSOR))))
+                .hoverEvent(HoverEvent.showText(showAccessorText)).build();
+        final Component builderTrustText = TextComponent.builder("")
+                .append(type == TrustTypes.BUILDER ? TextComponent.builder("")
+                        .append(whiteOpenBracket)
+                        .append(MessageCache.getInstance().TITLE_BUILDER.color(TextColor.GREEN))
+                        .append(whiteCloseBracket)
+                        .build() : MessageCache.getInstance().TITLE_BUILDER.color(TextColor.GRAY))
+                .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createTrustConsumer(src, claim, TrustTypes.BUILDER))))
+                .hoverEvent(HoverEvent.showText(showBuilderText)).build();
+        final Component containerTrustText = TextComponent.builder("")
+                .append(type == TrustTypes.CONTAINER ? TextComponent.builder("")
+                        .append(whiteOpenBracket)
+                        .append(MessageCache.getInstance().TITLE_CONTAINER.color(TextColor.LIGHT_PURPLE))
+                        .append(whiteCloseBracket)
+                        .build() : MessageCache.getInstance().TITLE_CONTAINER.color(TextColor.GRAY))
+                .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createTrustConsumer(src, claim, TrustTypes.CONTAINER))))
+                .hoverEvent(HoverEvent.showText(showContainerText)).build();
+        final Component managerTrustText = TextComponent.builder("")
+                .append(type == TrustTypes.MANAGER ? TextComponent.builder("")
+                        .append(whiteOpenBracket)
+                        .append("MANAGER", TextColor.GOLD)
+                        .append(whiteCloseBracket)
+                        .build() : MessageCache.getInstance().TITLE_MANAGER.color(TextColor.GRAY))
+                .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createTrustConsumer(src, claim, TrustTypes.MANAGER))))
+                .hoverEvent(HoverEvent.showText(showManagerText)).build();
+        final Component claimTrustHead = TextComponent.builder()
+                .append(" ")
+                .append(MessageCache.getInstance().LABEL_DISPLAYING.color(TextColor.AQUA))
+                .append(" ")
+                .append(allTypeText)
+                .append("  ")
+                .append(accessorTrustText)
+                .append("  ")
+                .append(builderTrustText)
+                .append("  ")
+                .append(containerTrustText)
+                .append("  ")
+                .append(managerTrustText)
+                .build();
+
+        List<UUID> userIdList = new ArrayList<>(claim.getUserTrusts());
+        List<Component> trustList = new ArrayList<>();
+        trustList.add(TextComponent.empty());
+
+        if (type == TrustTypes.NONE) {
+            // check highest trust first
+            for (UUID uuid : claim.getInternalClaimData().getManagers()) {
+                final GDPermissionUser user = PermissionHolderCache.getInstance().getOrCreateUser(uuid);
+                trustList.add(TextComponent.of(user.getName(), TextColor.GOLD));
+                userIdList.remove(user.getUniqueId());
+            }
+
+            for (UUID uuid : claim.getInternalClaimData().getBuilders()) {
+                if (!userIdList.contains(uuid)) {
+                    continue;
+                }
+
+                final GDPermissionUser user = PermissionHolderCache.getInstance().getOrCreateUser(uuid);
+                trustList.add(TextComponent.of(user.getName(), TextColor.GREEN));
+                userIdList.remove(uuid);
+            }
+    
+            /*for (String group : claim.getInternalClaimData().getManagerGroups()) {
+                permissions.append(SPACE_TEXT, Text.of(group));
+            }*/
+    
+            for (UUID uuid : claim.getInternalClaimData().getContainers()) {
+                if (!userIdList.contains(uuid)) {
+                    continue;
+                }
+
+                final GDPermissionUser user = PermissionHolderCache.getInstance().getOrCreateUser(uuid);
+                trustList.add(TextComponent.of(user.getName(), TextColor.LIGHT_PURPLE));
+                userIdList.remove(uuid);
+            }
+    
+           /* for (String group : claim.getInternalClaimData().getBuilderGroups()) {
+                permissions.append(SPACE_TEXT, Text.of(group));
+            }*/
+    
+            for (UUID uuid : claim.getInternalClaimData().getAccessors()) {
+                if (!userIdList.contains(uuid)) {
+                    continue;
+                }
+
+                final GDPermissionUser user = PermissionHolderCache.getInstance().getOrCreateUser(uuid);
+                trustList.add(TextComponent.of(user.getName(), TextColor.YELLOW));
+                userIdList.remove(uuid);
+            }
+    
+            /*for (String group : claim.getInternalClaimData().getContainerGroups()) {
+                permissions.append(SPACE_TEXT, Text.of(group));
+            }
+    
+            player.sendMessage(permissions.build());
+            permissions = Text.builder(">").color(TextColors.BLUE);
+    
+            for (UUID uuid : claim.getInternalClaimData().getAccessors()) {
+                User user = GriefDefenderPlugin.getOrCreateUser(uuid);
+                permissions.append(SPACE_TEXT, Text.of(user.getName()));
+            }
+    
+            for (String group : claim.getInternalClaimData().getAccessorGroups()) {
+                permissions.append(SPACE_TEXT, Text.of(group));
+            }*/
+    
+        } else {
+            for (UUID uuid : claim.getUserTrusts(type)) {
+                if (!userIdList.contains(uuid)) {
+                    continue;
+                }
+
+                final GDPermissionUser user = PermissionHolderCache.getInstance().getOrCreateUser(uuid);
+                trustList.add(TextComponent.of(user.getName(), getTrustColor(type)));
+                userIdList.remove(uuid);
+            }
+        }
+
+        int fillSize = 20 - (trustList.size() + 2);
+        for (int i = 0; i < fillSize; i++) {
+            trustList.add(TextComponent.of(" "));
+        }
+
+        PaginationList.Builder paginationBuilder = PaginationList.builder()
+                .title(claimTrustHead).padding(TextComponent.of(" ").decoration(TextDecoration.STRIKETHROUGH, true)).contents(trustList);
+        paginationBuilder.sendTo(src);
+        paginationBuilder.sendTo(src);
+
+    }
+
+    private static TextColor getTrustColor(TrustType type) {
+        if (type == TrustTypes.NONE) {
+            return TextColor.WHITE;
+        }
+        if (type == TrustTypes.ACCESSOR) {
+            return TextColor.YELLOW;
+        }
+        if (type == TrustTypes.BUILDER) {
+            return TextColor.GREEN;
+        }
+        if (type == TrustTypes.CONTAINER) {
+            return TextColor.LIGHT_PURPLE;
+        }
+        return TextColor.GOLD;
+    }
+
+    private static Consumer<CommandSource> createTrustConsumer(CommandSource src, GDClaim claim, TrustType type) {
+        return consumer -> {
+            showTrustList(src, claim, type);
+        };
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandTrustPlayer.java b/sponge/src/main/java/com/griefdefender/command/CommandTrustPlayer.java
new file mode 100644
index 0000000..2ba8976
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandTrustPlayer.java
@@ -0,0 +1,167 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandCompletion;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Optional;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.GriefDefender;
+import com.griefdefender.api.claim.TrustType;
+import com.griefdefender.api.claim.TrustTypes;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.MessageDataConfig;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.event.GDCauseStackManager;
+import com.griefdefender.event.GDUserTrustClaimEvent;
+import com.griefdefender.permission.GDPermissionUser;
+import com.griefdefender.permission.GDPermissions;
+import net.kyori.text.Component;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import org.spongepowered.api.entity.living.player.Player;
+
+import java.util.List;
+import java.util.UUID;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_TRUST_PLAYER)
+public class CommandTrustPlayer extends BaseCommand {
+
+    @CommandCompletion("@gdplayers @gdtrusttypes @gddummy")
+    @CommandAlias("trust")
+    @Description("Grants a player access to your claim."
+            + "\nAccessor: access to interact with all blocks except inventory."
+            + "\nContainer: access to interact with all blocks including inventory."
+            + "\nBuilder: access to everything above including ability to place and break blocks."
+            + "\nManager: access to everything above including ability to manage claim settings.")
+    @Syntax("<player> [<accessor|builder|container|manager>]")
+    @Subcommand("trust player")
+    public void execute(Player player, String target, @Optional String type) {
+        TrustType trustType = null;
+        if (type == null) {
+            trustType = TrustTypes.BUILDER;
+        } else {
+            trustType = CommandHelper.getTrustType(type);
+            if (trustType == null) {
+                GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().TRUST_INVALID);
+                return;
+            }
+        }
+
+        GDPermissionUser user = null;
+        if (target.equalsIgnoreCase("public")) {
+            user = GriefDefenderPlugin.PUBLIC_USER;
+        } else {
+            user = PermissionHolderCache.getInstance().getOrCreateUser(target);
+        }
+
+        if (user == null) {
+            GriefDefenderPlugin.sendMessage(player, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.COMMAND_INVALID_PLAYER,
+                    ImmutableMap.of(
+                    "player", target)));
+            return;
+        }
+        if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(player.getWorld().getUniqueId())) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().CLAIM_DISABLED_WORLD);
+            return;
+        }
+
+        // determine which claim the player is standing in
+        GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(playerData, player.getLocation());
+        if (!claim.getOwnerUniqueId().equals(player.getUniqueId()) && !playerData.canIgnoreClaim(claim) && claim.allowEdit(player) != null) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().PERMISSION_COMMAND_TRUST);
+            return;
+        }
+
+        if (user.getUniqueId().equals(player.getUniqueId()) && !playerData.canIgnoreClaim(claim)) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().TRUST_SELF);
+            return;
+        }
+
+        if (user != null && claim.getOwnerUniqueId().equals(user.getUniqueId())) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().CLAIM_OWNER_ALREADY);
+            return;
+        } else {
+            //check permission here
+            if(claim.allowGrantPermission(player) != null) {
+                final Component message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.PERMISSION_TRUST,
+                        ImmutableMap.of(
+                        "player", claim.getOwnerName()));
+                GriefDefenderPlugin.sendMessage(player, message);
+                return;
+            }
+
+            if(trustType == TrustTypes.MANAGER) {
+                Component denyReason = claim.allowEdit(player);
+                if(denyReason != null) {
+                    GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().PERMISSION_GRANT);
+                    return;
+                }
+            }
+        }
+
+        GDCauseStackManager.getInstance().pushCause(player);
+        GDUserTrustClaimEvent.Add
+            event =
+            new GDUserTrustClaimEvent.Add(claim, ImmutableList.of(user.getUniqueId()), trustType);
+        GriefDefender.getEventManager().post(event);
+        GDCauseStackManager.getInstance().popCause();
+        if (event.cancelled()) {
+            TextAdapter.sendComponent(player, event.getMessage().orElse(event.getMessage().orElse(MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.TRUST_PLUGIN_CANCEL,
+                    ImmutableMap.of("target", user.getName())))));
+            return;
+        }
+
+        final List<UUID> trustList = claim.getUserTrustList(trustType);
+        if (trustList.contains(user.getUniqueId())) {
+            final Component message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.TRUST_ALREADY_HAS,
+                ImmutableMap.of(
+                    "target", user.getName(),
+                    "type", trustType.getName()));
+            GriefDefenderPlugin.sendMessage(player, message);
+            return;
+        }
+
+        trustList.add(user.getUniqueId());
+        claim.getInternalClaimData().setRequiresSave(true);
+        claim.getInternalClaimData().save();
+
+        final Component message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.TRUST_GRANT, ImmutableMap.of(
+                "target", user.getName(),
+                "type", trustType.getName()));
+        GriefDefenderPlugin.sendMessage(player, message);
+    }
+}
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandTrustPlayerAll.java b/sponge/src/main/java/com/griefdefender/command/CommandTrustPlayerAll.java
new file mode 100644
index 0000000..3abe47e
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandTrustPlayerAll.java
@@ -0,0 +1,151 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandCompletion;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Optional;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.GriefDefender;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.TrustType;
+import com.griefdefender.api.claim.TrustTypes;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.MessageDataConfig;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.event.GDCauseStackManager;
+import com.griefdefender.event.GDUserTrustClaimEvent;
+import com.griefdefender.permission.GDPermissionUser;
+import com.griefdefender.permission.GDPermissions;
+import net.kyori.text.Component;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import org.spongepowered.api.entity.living.player.Player;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_TRUSTALL_PLAYER)
+public class CommandTrustPlayerAll extends BaseCommand {
+
+    @CommandCompletion("@gdplayers @gdtrusttypes @gddummy")
+    @CommandAlias("trustall")
+    @Description("Grants a player access to all your claims."
+            + "\nAccessor: access to interact with all blocks except inventory."
+            + "\nContainer: access to interact with all blocks including inventory."
+            + "\nBuilder: access to everything above including ability to place and break blocks."
+            + "\nManager: access to everything above including ability to manage claim settings.")
+    @Syntax("<player> <accessor|builder|container|manager>")
+    @Subcommand("trustall player")
+    public void execute(Player player, String target, @Optional String type) {
+        TrustType trustType = null;
+        if (type == null) {
+            trustType = TrustTypes.BUILDER;
+        } else {
+            trustType = CommandHelper.getTrustType(type);
+            if (trustType == null) {
+                GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().TRUST_INVALID);
+                return;
+            }
+        }
+
+        GDPermissionUser user;
+        if (target.equalsIgnoreCase("public")) {
+            user = GriefDefenderPlugin.PUBLIC_USER;
+        } else {
+            user = PermissionHolderCache.getInstance().getOrCreateUser(target);
+        }
+
+        // validate player argument
+        if (user == null) {
+            GriefDefenderPlugin.sendMessage(player, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.COMMAND_INVALID_PLAYER,
+                    ImmutableMap.of(
+                    "player", target)));
+            return;
+        }
+
+        if (user.getUniqueId().equals(player.getUniqueId())) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().TRUST_SELF);
+            return;
+        }
+
+        GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        Set<Claim> claimList = null;
+        if (playerData != null) {
+            claimList = playerData.getInternalClaims();
+        }
+
+        if (playerData == null || claimList == null || claimList.size() == 0) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().TRUST_NO_CLAIMS);
+            return;
+        }
+
+        GDCauseStackManager.getInstance().pushCause(player);
+        GDUserTrustClaimEvent.Add
+            event = new GDUserTrustClaimEvent.Add(new ArrayList<>(claimList), ImmutableList.of(user.getUniqueId()), trustType);
+        GriefDefender.getEventManager().post(event);
+        GDCauseStackManager.getInstance().popCause();
+        if (event.cancelled()) {
+            TextAdapter.sendComponent(player, event.getMessage().orElse(MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.TRUST_PLUGIN_CANCEL,
+                    ImmutableMap.of("target", user.getName()))));
+            return;
+        }
+
+        for (Claim claim : claimList) {
+            this.addAllUserTrust(claim, user, trustType);
+        }
+
+        final Component message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.TRUST_INDIVIDUAL_ALL_CLAIMS,
+                ImmutableMap.of(
+                "player", user.getName()));
+        GriefDefenderPlugin.sendMessage(player, message);
+    }
+
+    private void addAllUserTrust(Claim claim, GDPermissionUser user, TrustType trustType) {
+        GDClaim gdClaim = (GDClaim) claim;
+        List<UUID> trustList = gdClaim.getUserTrustList(trustType);
+        if (!trustList.contains(user.getUniqueId())) {
+            trustList.add(user.getUniqueId());
+        }
+
+        gdClaim.getInternalClaimData().setRequiresSave(true);
+        gdClaim.getInternalClaimData().save();
+        for (Claim child : gdClaim.children) {
+            this.addAllUserTrust(child, user, trustType);
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandUntrustGroup.java b/sponge/src/main/java/com/griefdefender/command/CommandUntrustGroup.java
new file mode 100644
index 0000000..64a55cc
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandUntrustGroup.java
@@ -0,0 +1,115 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandCompletion;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.GriefDefender;
+import com.griefdefender.api.claim.TrustTypes;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.MessageDataConfig;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.event.GDCauseStackManager;
+import com.griefdefender.event.GDGroupTrustClaimEvent;
+import com.griefdefender.permission.GDPermissionGroup;
+import com.griefdefender.permission.GDPermissions;
+import com.griefdefender.util.PermissionUtil;
+import net.kyori.text.Component;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.service.permission.Subject;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_UNTRUST_GROUP)
+public class CommandUntrustGroup extends BaseCommand {
+
+    @CommandCompletion("@gdgroups @gddummy")
+    @CommandAlias("untrustgroup")
+    @Description("Revokes group access to your claim.")
+    @Syntax("<group>")
+    @Subcommand("untrust group")
+    public void execute(Player player, String target) {
+        final GDPermissionGroup group = PermissionHolderCache.getInstance().getOrCreateGroup(target);
+        if (group == null) {
+            GriefDefenderPlugin.sendMessage(player, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.COMMAND_INVALID_GROUP, ImmutableMap.of(
+                    "group", target)));
+            return;
+        }
+
+        if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(player.getWorld().getUniqueId())) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().CLAIM_DISABLED_WORLD);
+            return;
+        }
+
+        // determine which claim the player is standing in
+        GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(playerData, player.getLocation());
+        if (!playerData.canIgnoreClaim(claim) && claim.allowEdit(player) != null) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().PERMISSION_COMMAND_TRUST);
+            return;
+        }
+
+        //check permission here
+        if(claim.allowGrantPermission(player) != null) {
+            final Component message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.PERMISSION_TRUST,
+                    ImmutableMap.of(
+                    "player", claim.getOwnerName()));
+            GriefDefenderPlugin.sendMessage(player, message);
+            return;
+        }
+
+        GDCauseStackManager.getInstance().pushCause(player);
+        GDGroupTrustClaimEvent.Remove event =
+            new GDGroupTrustClaimEvent.Remove(claim, ImmutableList.of(group.getName()), TrustTypes.NONE);
+        GriefDefender.getEventManager().post(event);
+        GDCauseStackManager.getInstance().popCause();
+        if (event.cancelled()) {
+            TextAdapter.sendComponent(player, event.getMessage().orElse(MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.TRUST_PLUGIN_CANCEL,
+                    ImmutableMap.of("target", group))));
+            return;
+        }
+
+        claim.removeAllTrustsFromGroup(group.getName());
+        claim.getInternalClaimData().setRequiresSave(true);
+        claim.getInternalClaimData().save();
+
+        final Component message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.UNTRUST_INDIVIDUAL_SINGLE_CLAIM,
+                ImmutableMap.of(
+                "target", group));
+        GriefDefenderPlugin.sendMessage(player, message);
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandUntrustGroupAll.java b/sponge/src/main/java/com/griefdefender/command/CommandUntrustGroupAll.java
new file mode 100644
index 0000000..97d2776
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandUntrustGroupAll.java
@@ -0,0 +1,118 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandCompletion;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.GriefDefender;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.TrustTypes;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.MessageDataConfig;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.event.GDCauseStackManager;
+import com.griefdefender.event.GDGroupTrustClaimEvent;
+import com.griefdefender.permission.GDPermissionGroup;
+import com.griefdefender.permission.GDPermissions;
+import net.kyori.text.Component;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import org.spongepowered.api.entity.living.player.Player;
+
+import java.util.ArrayList;
+import java.util.Set;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_UNTRUSTALL_GROUP)
+public class CommandUntrustGroupAll extends BaseCommand {
+
+    @CommandCompletion("@gdgroups @gdtrusttypes @gddummy")
+    @CommandAlias("untrustallgroup")
+    @Description("Revokes group access to all your claims")
+    @Syntax("<group>")
+    @Subcommand("untrustall group")
+    public void execute(Player player, String target, String type) {
+        final GDPermissionGroup group = PermissionHolderCache.getInstance().getOrCreateGroup(target);
+
+        // validate player argument
+        if (group == null) {
+            GriefDefenderPlugin.sendMessage(player, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.COMMAND_INVALID_GROUP,
+                    ImmutableMap.of(
+                    "group", target)));
+            return;
+        }
+
+        GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        Set<Claim> claimList = null;
+        if (playerData != null) {
+            claimList = playerData.getInternalClaims();
+        }
+
+        if (playerData == null || claimList == null || claimList.size() == 0) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().TRUST_NO_CLAIMS);
+            return;
+        }
+
+        GDCauseStackManager.getInstance().pushCause(player);
+        GDGroupTrustClaimEvent.Remove
+            event = new GDGroupTrustClaimEvent.Remove(new ArrayList<>(claimList), ImmutableList.of(group.getName()), TrustTypes.NONE);
+        GriefDefender.getEventManager().post(event);
+        GDCauseStackManager.getInstance().popCause();
+        if (event.cancelled()) {
+            TextAdapter.sendComponent(player, event.getMessage().orElse(MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.TRUST_PLUGIN_CANCEL,
+                    ImmutableMap.of("target", group))));
+            return;
+        }
+
+        for (Claim claim : claimList) {
+            this.removeAllGroupTrust(claim, group);
+        }
+
+        final Component message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.UNTRUST_INDIVIDUAL_ALL_CLAIMS,
+               ImmutableMap.of(
+                "player", group.getName()));
+        GriefDefenderPlugin.sendMessage(player, message);
+    }
+
+    private void removeAllGroupTrust(Claim claim, GDPermissionGroup holder) {
+        GDClaim gdClaim = (GDClaim) claim;
+        gdClaim.removeAllTrustsFromGroup(holder.getName());
+        gdClaim.getInternalClaimData().setRequiresSave(true);
+        gdClaim.getInternalClaimData().save();
+        for (Claim child : gdClaim.children) {
+            this.removeAllGroupTrust(child, holder);
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandUntrustPlayer.java b/sponge/src/main/java/com/griefdefender/command/CommandUntrustPlayer.java
new file mode 100644
index 0000000..3db8f24
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandUntrustPlayer.java
@@ -0,0 +1,121 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandCompletion;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.GriefDefender;
+import com.griefdefender.api.claim.TrustTypes;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.MessageDataConfig;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.event.GDCauseStackManager;
+import com.griefdefender.event.GDUserTrustClaimEvent;
+import com.griefdefender.permission.GDPermissionUser;
+import com.griefdefender.permission.GDPermissions;
+import net.kyori.text.Component;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import org.spongepowered.api.entity.living.player.Player;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_UNTRUST_PLAYER)
+public class CommandUntrustPlayer extends BaseCommand {
+
+    @CommandCompletion("@gdplayers @gddummy")
+    @CommandAlias("untrust|ut")
+    @Description("Revokes player access to your claim.")
+    @Syntax("<player>")
+    @Subcommand("untrust player")
+    public void execute(Player player, String target) {
+        GDPermissionUser user;
+        if (target.equalsIgnoreCase("public")) {
+            user = GriefDefenderPlugin.PUBLIC_USER;
+        } else {
+            user = PermissionHolderCache.getInstance().getOrCreateUser(target);
+        }
+
+        if (user == null) {
+            GriefDefenderPlugin.sendMessage(player, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.COMMAND_INVALID_PLAYER,
+                    ImmutableMap.of(
+                    "player", target)));
+            return;
+        }
+
+        if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(player.getWorld().getUniqueId())) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().CLAIM_DISABLED_WORLD);
+            return;
+        }
+
+        // determine which claim the player is standing in
+        GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(playerData, player.getLocation());
+        if (!claim.getOwnerUniqueId().equals(player.getUniqueId()) && !playerData.canIgnoreClaim(claim) && claim.allowEdit(player) != null) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().PERMISSION_COMMAND_TRUST);
+            return;
+        }
+
+        if (user.getUniqueId().equals(player.getUniqueId()) && !playerData.canIgnoreClaim(claim)) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().UNTRUST_SELF);
+            return;
+        }
+
+        if (claim.getOwnerUniqueId().equals(user.getUniqueId())) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().CLAIM_OWNER_ALREADY);
+            return;
+        }
+
+        GDCauseStackManager.getInstance().pushCause(player);
+        GDUserTrustClaimEvent.Remove
+            event =
+            new GDUserTrustClaimEvent.Remove(claim, ImmutableList.of(user.getUniqueId()), TrustTypes.NONE);
+        GriefDefender.getEventManager().post(event);
+        GDCauseStackManager.getInstance().popCause();
+        if (event.cancelled()) {
+            TextAdapter.sendComponent(player, event.getMessage().orElse(MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.TRUST_PLUGIN_CANCEL,
+                    ImmutableMap.of("target", user.getName()))));
+            return;
+        }
+
+        claim.removeAllTrustsFromUser(user.getUniqueId());
+        claim.getInternalClaimData().setRequiresSave(true);
+        claim.getInternalClaimData().save();
+
+        final Component message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.UNTRUST_INDIVIDUAL_SINGLE_CLAIM,
+            ImmutableMap.of(
+                "target", user.getName()));
+        GriefDefenderPlugin.sendMessage(player, message);
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/CommandUntrustPlayerAll.java b/sponge/src/main/java/com/griefdefender/command/CommandUntrustPlayerAll.java
new file mode 100644
index 0000000..12eb951
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/CommandUntrustPlayerAll.java
@@ -0,0 +1,126 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandCompletion;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.GriefDefender;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.TrustTypes;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.MessageDataConfig;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.event.GDCauseStackManager;
+import com.griefdefender.event.GDUserTrustClaimEvent;
+import com.griefdefender.permission.GDPermissionUser;
+import com.griefdefender.permission.GDPermissions;
+import net.kyori.text.Component;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import org.spongepowered.api.entity.living.player.Player;
+
+import java.util.ArrayList;
+import java.util.Set;
+
+@CommandAlias("%griefdefender")
+@CommandPermission(GDPermissions.COMMAND_UNTRUSTALL_PLAYER)
+public class CommandUntrustPlayerAll extends BaseCommand {
+
+    @CommandCompletion("@gdplayers @gddummy")
+    @CommandAlias("untrustall")
+    @Description("Revokes player access to all your claims.")
+    @Syntax("<player>")
+    @Subcommand("untrustall player")
+    public void execute(Player player, String target) {
+        GDPermissionUser user;
+        if (target.equalsIgnoreCase("public")) {
+            user = GriefDefenderPlugin.PUBLIC_USER;
+        } else {
+            user = PermissionHolderCache.getInstance().getOrCreateUser(target);
+        }
+
+        // validate player argument
+        if (user == null) {
+            GriefDefenderPlugin.sendMessage(player, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.COMMAND_INVALID_PLAYER, ImmutableMap.of(
+                    "player", target)));
+            return;
+        }
+        if (user.getUniqueId().equals(player.getUniqueId())) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().UNTRUST_SELF);
+            return;
+        }
+
+        GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        Set<Claim> claimList = null;
+        if (playerData != null) {
+            claimList = playerData.getInternalClaims();
+        }
+
+        if (playerData == null || claimList == null || claimList.size() == 0) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().UNTRUST_NO_CLAIMS);
+            return;
+        }
+
+        GDCauseStackManager.getInstance().pushCause(player);
+        GDUserTrustClaimEvent.Remove
+            event = new GDUserTrustClaimEvent.Remove(new ArrayList<>(claimList), ImmutableList.of(user.getUniqueId()), TrustTypes.NONE);
+        GriefDefender.getEventManager().post(event);
+        GDCauseStackManager.getInstance().popCause();
+        if (event.cancelled()) {
+            TextAdapter.sendComponent(player, event.getMessage().orElse(MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.TRUST_PLUGIN_CANCEL,
+                    ImmutableMap.of("target", user.getName()))));
+            return;
+        }
+
+        for (Claim claim : claimList) {
+            this.removeAllUserTrust(claim, user);
+        }
+
+        final Component message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.UNTRUST_INDIVIDUAL_ALL_CLAIMS,
+               ImmutableMap.of(
+                "player", user.getName()));
+        GriefDefenderPlugin.sendMessage(player, message);
+    }
+
+    private void removeAllUserTrust(Claim claim, GDPermissionUser user) {
+        final GDClaim gdClaim = ((GDClaim) claim);
+        gdClaim.removeAllTrustsFromUser(user.getUniqueId());
+        gdClaim.getInternalClaimData().setRequiresSave(true);
+        gdClaim.getInternalClaimData().save();
+        for (Claim child : gdClaim.children) {
+            this.removeAllUserTrust(child, user);
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/ComponentMessageException.java b/sponge/src/main/java/com/griefdefender/command/ComponentMessageException.java
new file mode 100644
index 0000000..a097300
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/ComponentMessageException.java
@@ -0,0 +1,104 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) SpongePowered <https://www.spongepowered.org>
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command;
+
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+import net.kyori.text.Component;
+import net.kyori.text.serializer.plain.PlainComponentSerializer;
+
+/**
+ * A subclass of Exception that contains a rich message that is an instance of
+ * {@link Text} rather than a String. This allows formatted and localized
+ * exception messages.
+ */
+public class ComponentMessageException extends Exception {
+
+    private static final long serialVersionUID = -5281221645176698853L;
+
+    @Nullable private final Component message;
+
+    /**
+     * Constructs a new {@link ComponentMessageException}.
+     */
+    public ComponentMessageException() {
+        this.message = null;
+    }
+
+    /**
+     * Constructs a new {@link ComponentMessageException} with the given message.
+     *
+     * @param message The detail message
+     */
+    public ComponentMessageException(Component message) {
+        this.message = message;
+    }
+
+    /**
+     * Constructs a new {@link ComponentMessageException} with the given message and
+     * cause.
+     *
+     * @param message The detail message
+     * @param throwable The cause
+     */
+    public ComponentMessageException(Component message, Throwable throwable) {
+        super(throwable);
+        this.message = message;
+    }
+
+    /**
+     * Constructs a new {@link ComponentMessageException} with the given cause.
+     *
+     * @param throwable The cause
+     */
+    public ComponentMessageException(Throwable throwable) {
+        super(throwable);
+        this.message = null;
+    }
+
+    @Override
+    @Nullable
+    public String getMessage() {
+        Component message = getText();
+        return message == null ? null : PlainComponentSerializer.INSTANCE.serialize(message);
+    }
+
+    /**
+     * Returns the text message for this exception, or null if nothing is
+     * present.
+     * 
+     * @return The text for this message
+     */
+    @Nullable
+    public Component getText() {
+        return this.message;
+    }
+
+    @Override
+    @Nullable
+    public String getLocalizedMessage() {
+        return getMessage();
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/gphelper/CommandAccessTrust.java b/sponge/src/main/java/com/griefdefender/command/gphelper/CommandAccessTrust.java
new file mode 100644
index 0000000..d20bf46
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/gphelper/CommandAccessTrust.java
@@ -0,0 +1,125 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command.gphelper;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.GriefDefender;
+import com.griefdefender.api.claim.TrustTypes;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.event.GDCauseStackManager;
+import com.griefdefender.event.GDUserTrustClaimEvent;
+import com.griefdefender.permission.GDPermissionUser;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandCompletion;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Syntax;
+import net.kyori.text.Component;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import org.spongepowered.api.entity.living.player.Player;
+
+public class CommandAccessTrust extends BaseCommand {
+
+    @CommandCompletion("@gdplayers")
+    @CommandAlias("at|accesstrust")
+    @Description("Grants a player access to interact with all blocks except inventory.")
+    @Syntax("<player>")
+    public void execute(Player src, String target) {
+
+        if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(src.getWorld().getUniqueId())) {
+            GriefDefenderPlugin.sendMessage(src, MessageCache.getInstance().CLAIM_DISABLED_WORLD);
+            return;
+        }
+
+        GDPermissionUser user = null;
+        if (target.equalsIgnoreCase("public")) {
+            user = GriefDefenderPlugin.PUBLIC_USER;
+        } else {
+            user = PermissionHolderCache.getInstance().getOrCreateUser(target);
+        }
+
+        if (user == null) {
+            GriefDefenderPlugin.sendMessage(src, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.COMMAND_INVALID_PLAYER,
+                    ImmutableMap.of(
+                    "player", target)));
+            return;
+        }
+
+        // determine which claim the player is standing in
+        GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(src.getWorld(), src.getUniqueId());
+        GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(playerData, src.getLocation());
+        if (!claim.getOwnerUniqueId().equals(src.getUniqueId()) && !playerData.canIgnoreClaim(claim) && claim.allowEdit(src) != null) {
+            GriefDefenderPlugin.sendMessage(src, MessageCache.getInstance().PERMISSION_COMMAND_TRUST);
+            return;
+        }
+
+        if (user.getUniqueId().equals(src.getUniqueId()) && !playerData.canIgnoreClaim(claim)) {
+            GriefDefenderPlugin.sendMessage(src, MessageCache.getInstance().TRUST_SELF);
+            return;
+        }
+
+        if (user != null && claim.getOwnerUniqueId().equals(user.getUniqueId())) {
+            GriefDefenderPlugin.sendMessage(src, MessageCache.getInstance().CLAIM_OWNER_ALREADY);
+            return;
+        } else {
+            //check permission here
+            if(claim.allowGrantPermission(src) != null) {
+                final Component message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.PERMISSION_TRUST,
+                        ImmutableMap.of(
+                        "player", claim.getOwnerName()));
+                GriefDefenderPlugin.sendMessage(src, message);
+                return;
+            }
+        }
+
+        GDCauseStackManager.getInstance().pushCause(src);
+        GDUserTrustClaimEvent.Add
+            event =
+            new GDUserTrustClaimEvent.Add(claim, ImmutableList.of(user.getUniqueId()), TrustTypes.ACCESSOR);
+        GriefDefender.getEventManager().post(event);
+        GDCauseStackManager.getInstance().popCause();
+        if (event.cancelled()) {
+            TextAdapter.sendComponent(src, event.getMessage().orElse(event.getMessage().orElse(MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.TRUST_PLUGIN_CANCEL,
+                    ImmutableMap.of("target", user.getName())))));
+            return;
+        }
+
+        claim.addUserTrust(user.getUniqueId(), TrustTypes.ACCESSOR);
+        claim.getInternalClaimData().setRequiresSave(true);
+        claim.getInternalClaimData().save();
+
+        final Component message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.TRUST_GRANT, ImmutableMap.of(
+                "target", user.getName(),
+                "type", TrustTypes.ACCESSOR.getName()));
+        GriefDefenderPlugin.sendMessage(src, message);
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/command/gphelper/CommandContainerTrust.java b/sponge/src/main/java/com/griefdefender/command/gphelper/CommandContainerTrust.java
new file mode 100644
index 0000000..92dda41
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/command/gphelper/CommandContainerTrust.java
@@ -0,0 +1,125 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.command.gphelper;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.GriefDefender;
+import com.griefdefender.api.claim.TrustTypes;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.event.GDCauseStackManager;
+import com.griefdefender.event.GDUserTrustClaimEvent;
+import com.griefdefender.permission.GDPermissionUser;
+
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandCompletion;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Syntax;
+import net.kyori.text.Component;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import org.spongepowered.api.entity.living.player.Player;
+
+public class CommandContainerTrust extends BaseCommand {
+
+    @CommandCompletion("@gdplayers")
+    @CommandAlias("ct|containertrust")
+    @Description("Grants a player access to interact with all blocks including inventory.")
+    @Syntax("<player>")
+    public void execute(Player src, String target) {
+
+        if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(src.getWorld().getUniqueId())) {
+            GriefDefenderPlugin.sendMessage(src, MessageCache.getInstance().CLAIM_DISABLED_WORLD);
+            return;
+        }
+
+        GDPermissionUser user = null;
+        if (target.equalsIgnoreCase("public")) {
+            user = GriefDefenderPlugin.PUBLIC_USER;
+        } else {
+            user = PermissionHolderCache.getInstance().getOrCreateUser(target);
+        }
+
+        if (user == null) {
+            GriefDefenderPlugin.sendMessage(src, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.COMMAND_INVALID_PLAYER,
+                    ImmutableMap.of(
+                    "player", target)));
+            return;
+        }
+
+        // determine which claim the player is standing in
+        GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(src.getWorld(), src.getUniqueId());
+        GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(playerData, src.getLocation());
+        if (!claim.getOwnerUniqueId().equals(src.getUniqueId()) && !playerData.canIgnoreClaim(claim) && claim.allowEdit(src) != null) {
+            GriefDefenderPlugin.sendMessage(src, MessageCache.getInstance().PERMISSION_COMMAND_TRUST);
+            return;
+        }
+
+        if (user.getUniqueId().equals(src.getUniqueId()) && !playerData.canIgnoreClaim(claim)) {
+            GriefDefenderPlugin.sendMessage(src, MessageCache.getInstance().TRUST_SELF);
+            return;
+        }
+
+        if (user != null && claim.getOwnerUniqueId().equals(user.getUniqueId())) {
+            GriefDefenderPlugin.sendMessage(src, MessageCache.getInstance().CLAIM_OWNER_ALREADY);
+            return;
+        } else {
+            //check permission here
+            if(claim.allowGrantPermission(src) != null) {
+                final Component message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.PERMISSION_TRUST,
+                        ImmutableMap.of(
+                        "player", claim.getOwnerName()));
+                GriefDefenderPlugin.sendMessage(src, message);
+                return;
+            }
+        }
+
+        GDCauseStackManager.getInstance().pushCause(src);
+        GDUserTrustClaimEvent.Add
+            event =
+            new GDUserTrustClaimEvent.Add(claim, ImmutableList.of(user.getUniqueId()), TrustTypes.CONTAINER);
+        GriefDefender.getEventManager().post(event);
+        GDCauseStackManager.getInstance().popCause();
+        if (event.cancelled()) {
+            TextAdapter.sendComponent(src, event.getMessage().orElse(event.getMessage().orElse(MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.TRUST_PLUGIN_CANCEL,
+                    ImmutableMap.of("target", user.getName())))));
+            return;
+        }
+
+        claim.addUserTrust(user.getUniqueId(), TrustTypes.CONTAINER);
+        claim.getInternalClaimData().setRequiresSave(true);
+        claim.getInternalClaimData().save();
+
+        final Component message = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.TRUST_GRANT, ImmutableMap.of(
+                "target", user.getName(),
+                "type", TrustTypes.CONTAINER.getName()));
+        GriefDefenderPlugin.sendMessage(src, message);
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/configuration/ClaimDataConfig.java b/sponge/src/main/java/com/griefdefender/configuration/ClaimDataConfig.java
new file mode 100644
index 0000000..41104de
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/configuration/ClaimDataConfig.java
@@ -0,0 +1,552 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.configuration;
+
+import com.flowpowered.math.vector.Vector3i;
+import com.griefdefender.api.Tristate;
+import com.griefdefender.api.claim.ClaimType;
+import com.griefdefender.api.claim.ClaimTypes;
+import com.griefdefender.api.data.EconomyData;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.category.ConfigCategory;
+import com.griefdefender.internal.util.BlockUtil;
+import net.kyori.text.Component;
+import ninja.leaping.configurate.objectmapping.Setting;
+import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable;
+
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+
+@ConfigSerializable
+public class ClaimDataConfig extends ConfigCategory implements IClaimData {
+
+    private boolean requiresSave = false;
+    private Vector3i lesserPos;
+    private Vector3i greaterPos;
+    private Vector3i spawnPos;
+    private ClaimStorageData claimStorage;
+
+    @Setting
+    private UUID parent;
+    @Setting(value = ClaimStorageData.MAIN_INHERIT_PARENT)
+    public boolean inheritParent = true;
+    @Setting(value = ClaimStorageData.MAIN_WORLD_UUID)
+    private UUID worldUniqueId;
+    @Setting(value = ClaimStorageData.MAIN_OWNER_UUID)
+    private UUID ownerUniqueId;
+    @Setting(value = ClaimStorageData.MAIN_CLAIM_TYPE)
+    private ClaimType claimType = ClaimTypes.BASIC;
+    @Setting(value = ClaimStorageData.MAIN_CLAIM_TYPE)
+    private ClaimType customClaimType;
+    @Setting(value = ClaimStorageData.MAIN_CLAIM_CUBOID)
+    private boolean isCuboid = false;
+    @Setting(value = ClaimStorageData.MAIN_CLAIM_RESIZABLE)
+    private boolean isResizable = true;
+    @Setting
+    private boolean isExpired = false;
+    @Setting
+    private boolean sizeRestrictions = true;
+    @Setting(value = ClaimStorageData.MAIN_ALLOW_DENY_MESSAGES)
+    private boolean allowDenyMessages = true;
+    @Setting(value = ClaimStorageData.MAIN_ALLOW_CLAIM_EXPIRATION)
+    private boolean allowClaimExpiration = true;
+    @Setting(value = ClaimStorageData.MAIN_ALLOW_FLAG_OVERRIDES)
+    private boolean allowFlagOverrides = true;
+    @Setting(value = ClaimStorageData.MAIN_REQUIRES_CLAIM_BLOCKS)
+    private boolean requiresClaimBlocks = true;
+    @Setting(value = ClaimStorageData.MAIN_CLAIM_PVP)
+    private Tristate pvpOverride = Tristate.UNDEFINED;
+    @Setting(value = ClaimStorageData.MAIN_CLAIM_DATE_CREATED)
+    private String dateCreated = Instant.now().toString();
+    @Setting(value = ClaimStorageData.MAIN_CLAIM_DATE_LAST_ACTIVE)
+    private String dateLastActive = Instant.now().toString();
+    @Setting(value = ClaimStorageData.MAIN_CLAIM_NAME)
+    private Component claimName;
+    @Setting(value = ClaimStorageData.MAIN_CLAIM_GREETING)
+    private Component claimGreetingMessage;
+    @Setting(value = ClaimStorageData.MAIN_CLAIM_FAREWELL)
+    private Component claimFarewellMessage;
+    @Setting(value = ClaimStorageData.MAIN_CLAIM_SPAWN)
+    private String claimSpawn;
+    @Setting(value = ClaimStorageData.MAIN_LESSER_BOUNDARY_CORNER)
+    private String lesserBoundaryCornerPos;
+    @Setting(value = ClaimStorageData.MAIN_GREATER_BOUNDARY_CORNER)
+    private String greaterBoundaryCornerPos;
+    @Setting(value = ClaimStorageData.MAIN_ACCESSORS)
+    private List<UUID> accessors = new ArrayList<>();
+    @Setting(value = ClaimStorageData.MAIN_BUILDERS)
+    private List<UUID> builders = new ArrayList<>();
+    @Setting(value = ClaimStorageData.MAIN_CONTAINERS)
+    private List<UUID> containers = new ArrayList<>();
+    @Setting(value = ClaimStorageData.MAIN_MANAGERS)
+    private List<UUID> managers = new ArrayList<>();
+    @Setting(value = ClaimStorageData.MAIN_ACCESSOR_GROUPS)
+    private List<String> accessorGroups = new ArrayList<>();
+    @Setting(value = ClaimStorageData.MAIN_BUILDER_GROUPS)
+    private List<String> builderGroups = new ArrayList<>();
+    @Setting(value = ClaimStorageData.MAIN_CONTAINER_GROUPS)
+    private List<String> containerGroups = new ArrayList<>();
+    @Setting(value = ClaimStorageData.MAIN_MANAGER_GROUPS)
+    private List<String> managerGroups = new ArrayList<>();
+    @Setting
+    private EconomyDataConfig economyData = new EconomyDataConfig();
+
+    public ClaimDataConfig() {
+
+    }
+
+    public ClaimDataConfig(GDClaim claim) {
+        this.lesserBoundaryCornerPos = BlockUtil.getInstance().posToString(claim.lesserBoundaryCorner);
+        this.greaterBoundaryCornerPos = BlockUtil.getInstance().posToString(claim.greaterBoundaryCorner);
+        this.isCuboid = claim.cuboid;
+        this.claimType = claim.getType();
+        //this.customClaimType = claim.getCustomType();
+        this.ownerUniqueId = claim.getOwnerUniqueId();
+    }
+
+    /*public ClaimDataConfig(LinkedHashMap<String, Object> dataMap) {
+        for (Map.Entry<String, Object> mapEntry : dataMap.entrySet()) {
+            if (mapEntry.getKey().equals("world-uuid")) {
+                this.worldUniqueId = (UUID) UUID.fromString((String) mapEntry.getValue());
+            } else if (mapEntry.getKey().equals("owner-uuid")) {
+                this.ownerUniqueId = (UUID) UUID.fromString((String) mapEntry.getValue());
+            } else if (mapEntry.getKey().equals("claim-type")) {
+                String value = (String) mapEntry.getValue();
+                if (!value.contains(":")) {
+                    value = "griefdefender:" + value;
+                }
+                this.claimType = ClaimTypeRegistryModule.getInstance().getById(value).orElse(ClaimTypes.BASIC);
+            } else if (mapEntry.getKey().equals("cuboid")) {
+                this.isCuboid = (boolean) mapEntry.getValue();
+            } else if (mapEntry.getKey().equals("claim-expiration")) {
+                this.allowClaimExpiration = (boolean) mapEntry.getValue();
+            } else if (mapEntry.getKey().equals("date-created")) {
+                this.dateCreated = (String) mapEntry.getValue();
+            } else if (mapEntry.getKey().equals("date-last-active")) {
+                this.dateLastActive = (String) mapEntry.getValue();
+            } else if (mapEntry.getKey().equals("deny-messages")) {
+                this.allowDenyMessages = (boolean) mapEntry.getValue();
+            } else if (mapEntry.getKey().equals("flag-overrides")) {
+                this.allowFlagOverrides = (boolean) mapEntry.getValue();
+            } else if (mapEntry.getKey().equals("greater-boundary-corner")) {
+                this.greaterBoundaryCornerPos = (String) mapEntry.getValue();
+            } else if (mapEntry.getKey().equals("lesser-boundary-corner")) {
+                this.lesserBoundaryCornerPos = (String) mapEntry.getValue();
+            } else if (mapEntry.getKey().equals("pvp")) {
+                this.pvpOverride = Tristate.valueOf((String) mapEntry.getValue());
+            } else if (mapEntry.getKey().equals("resizeable")) {
+                this.isResizable = (boolean) mapEntry.getValue();
+            } else if (mapEntry.getKey().equals("accessors")) {
+                List<String> stringList = (List<String>) mapEntry.getValue();
+                if (stringList != null) {
+                    for (String str : stringList) {
+                        this.accessors.add(UUID.fromString(str));
+                    }
+                }
+            } else if (mapEntry.getKey().equals("builders")) {
+                List<String> stringList = (List<String>) mapEntry.getValue();
+                if (stringList != null) {
+                    for (String str : stringList) {
+                        this.builders.add(UUID.fromString(str));
+                    }
+                }
+            } else if (mapEntry.getKey().equals("containers")) {
+                List<String> stringList = (List<String>) mapEntry.getValue();
+                if (stringList != null) {
+                    for (String str : stringList) {
+                        this.containers.add(UUID.fromString(str));
+                    }
+                }
+            } else if (mapEntry.getKey().equals("managers")) {
+                List<String> stringList = (List<String>) mapEntry.getValue();
+                if (stringList != null) {
+                    for (String str : stringList) {
+                        this.managers.add(UUID.fromString(str));
+                    }
+                }
+            }
+        }
+    }*/
+
+    @Override
+    public UUID getWorldUniqueId() {
+        return this.worldUniqueId;
+    }
+
+    @Override
+    public UUID getOwnerUniqueId() {
+        return this.ownerUniqueId;
+    }
+
+    @Override
+    public boolean allowExpiration() {
+        return this.allowClaimExpiration;
+    }
+
+    @Override
+    public boolean allowFlagOverrides() {
+        return this.allowFlagOverrides;
+    }
+
+    @Override
+    public boolean isCuboid() {
+        return this.isCuboid;
+    }
+
+    @Override
+    public boolean allowDenyMessages() {
+        return this.allowDenyMessages;
+    }
+
+    @Override
+    public Tristate getPvpOverride() {
+        return this.pvpOverride;
+    }
+
+    @Override
+    public boolean isResizable() {
+        return this.isResizable;
+    }
+
+    @Override
+    public boolean hasSizeRestrictions() {
+        if (this.claimType == ClaimTypes.ADMIN || this.claimType == ClaimTypes.WILDERNESS) {
+            this.sizeRestrictions = false;
+            return false;
+        }
+        return this.sizeRestrictions;
+    }
+
+    @Override
+    public ClaimType getType() {
+        return this.claimType;
+    }
+
+    /*@Override
+    public ClaimType getCustomType() {
+        return this.customClaimType;
+    }*/
+
+    @Override
+    public Instant getDateCreated() {
+        return Instant.parse(this.dateCreated);
+    }
+
+    @Override
+    public Instant getDateLastActive() {
+        return Instant.parse(this.dateLastActive);
+    }
+
+    @Override
+    public Optional<Component> getName() {
+        return Optional.ofNullable(this.claimName);
+    }
+
+    @Override
+    public Optional<Component> getGreeting() {
+        return Optional.ofNullable(this.claimGreetingMessage);
+    }
+
+    @Override
+    public Optional<Component> getFarewell() {
+        return Optional.ofNullable(this.claimFarewellMessage);
+    }
+
+    @Override
+    public Optional<Vector3i> getSpawnPos() {
+        if (this.spawnPos == null && this.claimSpawn != null) {
+            try {
+                this.spawnPos = BlockUtil.getInstance().posFromString(this.claimSpawn);
+                this.requiresSave = true;
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+
+        return Optional.ofNullable(this.spawnPos);
+    }
+
+    @Override
+    public Vector3i getLesserBoundaryCornerPos() {
+        if (this.lesserPos == null) {
+            try {
+                this.lesserPos = BlockUtil.getInstance().posFromString(this.lesserBoundaryCornerPos);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+
+        return this.lesserPos;
+    }
+
+    @Override
+    public Vector3i getGreaterBoundaryCornerPos() {
+        if (this.greaterPos == null) {
+            try {
+                this.greaterPos = BlockUtil.getInstance().posFromString(this.greaterBoundaryCornerPos);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+
+        return this.greaterPos;
+    }
+
+    public List<UUID> getAccessors() {
+        return this.accessors;
+    }
+
+    public List<UUID> getBuilders() {
+        return this.builders;
+    }
+
+    public List<UUID> getContainers() {
+        return this.containers;
+    }
+
+    public List<UUID> getManagers() {
+        return this.managers;
+    }
+
+    public List<String> getAccessorGroups() {
+        return this.accessorGroups;
+    }
+
+    public List<String> getBuilderGroups() {
+        return this.builderGroups;
+    }
+
+    public List<String> getContainerGroups() {
+        return this.containerGroups;
+    }
+
+    public List<String> getManagerGroups() {
+        return this.managerGroups;
+    }
+
+    @Override
+    public void setDenyMessages(boolean flag) {
+        this.requiresSave = true;
+        this.allowDenyMessages = flag;
+    }
+
+    @Override
+    public void setExpiration(boolean flag) {
+        this.requiresSave = true;
+        this.allowClaimExpiration = flag;
+    }
+
+    @Override
+    public void setFlagOverrides(boolean flag) {
+        this.allowFlagOverrides = flag;
+    }
+
+    @Override
+    public void setCuboid(boolean cuboid) {
+        this.isCuboid = cuboid;
+    }
+
+    @Override
+    public void setPvpOverride(Tristate pvp) {
+        this.requiresSave = true;
+        this.pvpOverride = pvp;
+    }
+
+    @Override
+    public void setResizable(boolean resizable) {
+        this.requiresSave = true;
+        this.isResizable = resizable;
+    }
+
+    @Override
+    public void setType(ClaimType type) {
+        this.requiresSave = true;
+        this.claimType = type;
+    }
+
+    /*@Override
+    public void setCustomType(ClaimType type) {
+        this.requiresSave = true;
+        this.customClaimType = type;
+    }*/
+
+    @Override
+    public void setDateLastActive(Instant date) {
+        this.requiresSave = true;
+        this.dateLastActive = date.toString();
+    }
+
+    @Override
+    public void setName(Component name) {
+        this.requiresSave = true;
+        this.claimName = name;
+    }
+
+    @Override
+    public void setGreeting(Component message) {
+        this.requiresSave = true;
+        this.claimGreetingMessage = message;
+    }
+
+    @Override
+    public void setFarewell(Component message) {
+        this.requiresSave = true;
+        this.claimFarewellMessage = message;
+    }
+
+    @Override
+    public void setLesserBoundaryCorner(String location) {
+        this.requiresSave = true;
+        this.lesserBoundaryCornerPos = location;
+        this.lesserPos = null;
+    }
+
+    @Override
+    public void setGreaterBoundaryCorner(String location) {
+        this.requiresSave = true;
+        this.greaterBoundaryCornerPos = location;
+        this.greaterPos = null;
+    }
+
+    @Override
+    public void setAccessors(List<UUID> accessors) {
+        this.requiresSave = true;
+        this.accessors = accessors;
+    }
+
+    @Override
+    public void setBuilders(List<UUID> builders) {
+        this.requiresSave = true;
+        this.builders = builders;
+    }
+
+    @Override
+    public void setContainers(List<UUID> containers) {
+        this.requiresSave = true;
+        this.containers = containers;
+    }
+
+    @Override
+    public void setManagers(List<UUID> coowners) {
+        this.requiresSave = true;
+        this.managers = coowners;
+    }
+
+    public boolean requiresSave() {
+        return this.requiresSave;
+    }
+
+    @Override
+    public void setRequiresSave(boolean flag) {
+        this.requiresSave = flag;
+    }
+
+    @Override
+    public void setSizeRestrictions(boolean sizeRestrictions) {
+        this.sizeRestrictions = sizeRestrictions;
+    }
+
+    @Override
+    public boolean doesInheritParent() {
+        // NOTE: admin claims ONLY inherit from other parent admin claims
+        return this.inheritParent;
+    }
+
+    @Override
+    public void setInheritParent(boolean flag) {
+        this.requiresSave = true;
+        this.inheritParent = flag;
+    }
+
+    @Override
+    public void setOwnerUniqueId(UUID newClaimOwner) {
+        this.requiresSave = true;
+        this.ownerUniqueId = newClaimOwner;
+    }
+
+    @Override
+    public void setWorldUniqueId(UUID uuid) {
+        this.requiresSave = true;
+        this.worldUniqueId = uuid;
+    }
+
+    public void setClaimStorageData(ClaimStorageData claimStorage) {
+        this.claimStorage = claimStorage;
+    }
+
+    @Override
+    public void save() {
+        this.claimStorage.save();
+    }
+
+    @Override
+    public void setSpawnPos(Vector3i spawnPos) {
+        if (spawnPos == null) {
+            return;
+        }
+
+        this.requiresSave = true;
+        this.spawnPos = spawnPos;
+        this.claimSpawn = BlockUtil.getInstance().posToString(spawnPos);
+    }
+
+    @Override
+    public boolean requiresClaimBlocks() {
+        return this.requiresClaimBlocks;
+    }
+
+    @Override
+    public void setRequiresClaimBlocks(boolean requiresClaimBlocks) {
+        this.requiresSave = true;
+        this.requiresClaimBlocks = requiresClaimBlocks;
+    }
+
+    @Override
+    public void setParent(UUID uuid) {
+        this.requiresSave = true;
+        this.parent = uuid;
+    }
+
+    @Override
+    public Optional<UUID> getParent() {
+        return Optional.ofNullable(this.parent);
+    }
+
+    public boolean isExpired() {
+        return this.isExpired;
+    }
+
+    public void setExpired(boolean expire) {
+        this.isExpired = expire;
+    }
+
+    @Override
+    public EconomyData getEconomyData() {
+        return this.economyData;
+    }
+}
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/configuration/ClaimStorageData.java b/sponge/src/main/java/com/griefdefender/configuration/ClaimStorageData.java
new file mode 100644
index 0000000..303238e
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/configuration/ClaimStorageData.java
@@ -0,0 +1,209 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.configuration;
+
+import com.google.common.reflect.TypeToken;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.claim.ClaimType;
+import com.griefdefender.api.claim.ClaimTypes;
+import ninja.leaping.configurate.ConfigurationOptions;
+import ninja.leaping.configurate.commented.CommentedConfigurationNode;
+import ninja.leaping.configurate.commented.SimpleCommentedConfigurationNode;
+import ninja.leaping.configurate.hocon.HoconConfigurationLoader;
+import ninja.leaping.configurate.objectmapping.ObjectMapper;
+import ninja.leaping.configurate.objectmapping.ObjectMappingException;
+import ninja.leaping.configurate.objectmapping.serialize.TypeSerializers;
+import org.spongepowered.api.Sponge;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.UUID;
+
+public class ClaimStorageData {
+
+    protected HoconConfigurationLoader loader;
+    private CommentedConfigurationNode root = SimpleCommentedConfigurationNode.root(ConfigurationOptions.defaults());
+    protected ObjectMapper<ClaimDataConfig>.BoundInstance configMapper;
+    protected ClaimDataConfig configBase;
+    public Path filePath;
+    public Path folderPath;
+
+    // MAIN
+    public static final String MAIN_WORLD_UUID = "world-uuid";
+    public static final String MAIN_OWNER_UUID = "owner-uuid";
+    public static final String MAIN_CLAIM_NAME = "claim-name";
+    public static final String MAIN_CLAIM_GREETING = "claim-greeting";
+    public static final String MAIN_CLAIM_FAREWELL = "claim-farewell";
+    public static final String MAIN_CLAIM_SPAWN = "claim-spawn";
+    public static final String MAIN_CLAIM_TYPE = "claim-type";
+    public static final String MAIN_CLAIM_CUSTOM_TYPE = "claim-type-custom";
+    public static final String MAIN_CLAIM_CUBOID = "cuboid";
+    public static final String MAIN_CLAIM_RESIZABLE = "resizable";
+    public static final String MAIN_CLAIM_PVP = "pvp";
+    public static final String MAIN_CLAIM_DATE_CREATED = "date-created";
+    public static final String MAIN_CLAIM_DATE_LAST_ACTIVE = "date-last-active";
+    public static final String MAIN_CLAIM_MAX_WIDTH = "max-width";
+    public static final String MAIN_CLAIM_FOR_SALE = "for-sale";
+    public static final String MAIN_CLAIM_SALE_PRICE = "sale-price";
+    public static final String MAIN_REQUIRES_CLAIM_BLOCKS = "requires-claim-blocks";
+    public static final String MAIN_SUBDIVISION_UUID = "uuid";
+    public static final String MAIN_PARENT_CLAIM_UUID = "parent-claim-uuid";
+    public static final String MAIN_LESSER_BOUNDARY_CORNER = "lesser-boundary-corner";
+    public static final String MAIN_GREATER_BOUNDARY_CORNER = "greater-boundary-corner";
+    public static final String MAIN_ACCESSORS = "accessors";
+    public static final String MAIN_BUILDERS = "builders";
+    public static final String MAIN_CONTAINERS = "containers";
+    public static final String MAIN_MANAGERS = "managers";
+    public static final String MAIN_ACCESSOR_GROUPS = "accessor-groups";
+    public static final String MAIN_BUILDER_GROUPS = "builder-groups";
+    public static final String MAIN_CONTAINER_GROUPS = "container-groups";
+    public static final String MAIN_MANAGER_GROUPS = "manager-groups";
+    public static final String MAIN_ALLOW_DENY_MESSAGES = "deny-messages";
+    public static final String MAIN_ALLOW_FLAG_OVERRIDES = "flag-overrides";
+    public static final String MAIN_ALLOW_CLAIM_EXPIRATION = "claim-expiration";
+    public static final String MAIN_TAX_PAST_DUE_DATE = "tax-past-due-date";
+    public static final String MAIN_TAX_BALANCE = "tax-balance";
+    // SUB
+    public static final String MAIN_INHERIT_PARENT = "inherit-parent";
+
+    // Used for new claims after server startup
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    public ClaimStorageData(Path path, UUID worldUniqueId, UUID ownerUniqueId, ClaimType type, boolean cuboid) {
+        this.filePath = path;
+        this.folderPath = path.getParent();
+        try {
+            if (Files.notExists(path.getParent())) {
+                Files.createDirectories(path.getParent());
+            }
+            if (Files.notExists(path)) {
+                Files.createFile(path);
+            }
+
+            this.loader = HoconConfigurationLoader.builder().setPath(path).build();
+            if (type == ClaimTypes.TOWN) {
+                this.configMapper = (ObjectMapper.BoundInstance) ObjectMapper.forClass(TownDataConfig.class).bindToNew();
+            } else {
+                this.configMapper = (ObjectMapper.BoundInstance) ObjectMapper.forClass(ClaimDataConfig.class).bindToNew();
+            }
+            this.configMapper.getInstance().setWorldUniqueId(worldUniqueId);
+            this.configMapper.getInstance().setOwnerUniqueId(ownerUniqueId);
+            this.configMapper.getInstance().setType(type);
+            this.configMapper.getInstance().setCuboid(cuboid);
+            this.configMapper.getInstance().setClaimStorageData(this);
+            reload();
+            ((EconomyDataConfig) this.configMapper.getInstance().getEconomyData()).activeConfig = GriefDefenderPlugin.getActiveConfig(Sponge.getServer().getWorld(worldUniqueId).get().getProperties());
+        } catch (Exception e) {
+            GriefDefenderPlugin.getInstance().getLogger().error("Failed to initialize configuration", e);
+        }
+    }
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    public ClaimStorageData(Path path, UUID worldUniqueId, ClaimDataConfig claimData) {
+        this.filePath = path;
+        this.folderPath = path.getParent();
+        try {
+            if (Files.notExists(path.getParent())) {
+                Files.createDirectories(path.getParent());
+            }
+            if (Files.notExists(path)) {
+                Files.createFile(path);
+            }
+
+            this.loader = HoconConfigurationLoader.builder().setPath(path).build();
+            this.configMapper = (ObjectMapper.BoundInstance) ObjectMapper.forClass(ClaimDataConfig.class).bind(claimData);
+            this.configMapper.getInstance().setClaimStorageData(this);
+            reload();
+            ((EconomyDataConfig) this.configMapper.getInstance().getEconomyData()).activeConfig = GriefDefenderPlugin.getActiveConfig(worldUniqueId);
+        } catch (Exception e) {
+            GriefDefenderPlugin.getInstance().getLogger().error("Failed to initialize configuration", e);
+        }
+    }
+
+    // Used during server load
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    public ClaimStorageData(Path path, UUID worldUniqueId) {
+        this.filePath = path;
+        this.folderPath = path.getParent();
+        try {
+            if (Files.notExists(path.getParent())) {
+                Files.createDirectories(path.getParent());
+            }
+            if (Files.notExists(path)) {
+                Files.createFile(path);
+            }
+
+            this.loader = HoconConfigurationLoader.builder().setPath(path).build();
+            if (path.getParent().endsWith("town")) {
+                this.configMapper = (ObjectMapper.BoundInstance) ObjectMapper.forClass(TownDataConfig.class).bindToNew();
+            } else {
+                this.configMapper = (ObjectMapper.BoundInstance) ObjectMapper.forClass(ClaimDataConfig.class).bindToNew();
+            }
+            this.configMapper.getInstance().setClaimStorageData(this);
+            try {
+                this.root = this.loader.load(ConfigurationOptions.defaults());
+                CommentedConfigurationNode rootNode = this.root.getNode(GriefDefenderPlugin.MOD_ID);
+                // Check if server is using existing Sponge GP data
+                if (rootNode.isVirtual()) {
+                    // check GriefPrevention
+                    CommentedConfigurationNode gpRootNode = this.root.getNode("GriefPrevention");
+                    if (!gpRootNode.isVirtual()) {
+                        rootNode.setValue(gpRootNode.getValue());
+                        gpRootNode.setValue(null);
+                    }
+                }
+                this.configBase = this.configMapper.populate(rootNode);
+            } catch (Exception e) {
+                GriefDefenderPlugin.getInstance().getLogger().error("Failed to load configuration", e);
+            }
+            ((EconomyDataConfig) this.configMapper.getInstance().getEconomyData()).activeConfig = GriefDefenderPlugin.getActiveConfig(worldUniqueId);
+        } catch (Exception e) {
+            GriefDefenderPlugin.getInstance().getLogger().error("Failed to initialize configuration", e);
+        }
+    }
+
+    public ClaimDataConfig getConfig() {
+        return this.configBase;
+    }
+
+    public void save() {
+        try {
+            this.configMapper.serialize(this.root.getNode(GriefDefenderPlugin.MOD_ID));
+            this.loader.save(this.root);
+            this.configBase.setRequiresSave(false);
+        } catch (IOException | ObjectMappingException e) {
+            GriefDefenderPlugin.getInstance().getLogger().error("Failed to save configuration", e);
+        }
+    }
+
+    public void reload() {
+        try {
+            this.root = this.loader.load(ConfigurationOptions.defaults());
+            this.configBase = this.configMapper.populate(this.root.getNode(GriefDefenderPlugin.MOD_ID));
+        } catch (Exception e) {
+            GriefDefenderPlugin.getInstance().getLogger().error("Failed to load configuration", e);
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/configuration/ClaimTemplateConfig.java b/sponge/src/main/java/com/griefdefender/configuration/ClaimTemplateConfig.java
new file mode 100644
index 0000000..a8fc800
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/configuration/ClaimTemplateConfig.java
@@ -0,0 +1,130 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.configuration;
+
+import com.google.common.collect.Maps;
+import com.griefdefender.api.permission.Context;
+import com.griefdefender.configuration.category.ConfigCategory;
+import ninja.leaping.configurate.objectmapping.Setting;
+import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable;
+
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+@ConfigSerializable
+public class ClaimTemplateConfig extends ConfigCategory {
+
+    @Setting(value = "name", comment = "The template name.")
+    public String templateName;
+    @Setting(value = "description", comment = "A description to help describe the template.")
+    public String templateDescription = "";
+    @Setting(value = ClaimStorageData.MAIN_OWNER_UUID, comment = "The owner uuid that created this template.")
+    public UUID ownerUniqueId;
+    @Setting(value = ClaimStorageData.MAIN_CLAIM_DATE_CREATED, comment = "The date and time this template was created.")
+    public String dateCreated = Instant.now().toString();
+    @Setting(value = ClaimStorageData.MAIN_ACCESSORS, comment = "The accessors associated with subdivision.")
+    public ArrayList<UUID> accessors = new ArrayList<>();
+    @Setting(value = ClaimStorageData.MAIN_BUILDERS, comment = "The builders associated with subdivision.")
+    public ArrayList<UUID> builders = new ArrayList<>();
+    @Setting(value = ClaimStorageData.MAIN_CONTAINERS, comment = "The containers associated with subdivision.")
+    public ArrayList<UUID> containers = new ArrayList<>();
+    @Setting(value = ClaimStorageData.MAIN_MANAGERS, comment = "The coowners associated with subdivision.")
+    public ArrayList<UUID> coowners = new ArrayList<>();
+    @Setting(value = ClaimStorageData.MAIN_ACCESSOR_GROUPS)
+    private List<String> accessorGroups = new ArrayList<>();
+    @Setting(value = ClaimStorageData.MAIN_BUILDER_GROUPS)
+    private List<String> builderGroups = new ArrayList<>();
+    @Setting(value = ClaimStorageData.MAIN_CONTAINER_GROUPS)
+    private List<String> containerGroups = new ArrayList<>();
+    @Setting(value = ClaimStorageData.MAIN_MANAGER_GROUPS)
+    private List<String> managerGroups = new ArrayList<>();
+    @Setting(value = "permissions")
+    private Map<Context, String> permissions = Maps.newHashMap();
+
+    public ClaimTemplateConfig() {
+
+    }
+
+    public UUID getOwnerUniqueId() {
+        return this.ownerUniqueId;
+    }
+
+    public String getDateCreated() {
+        return this.dateCreated;
+    }
+
+    public List<UUID> getAccessors() {
+        return this.accessors;
+    }
+
+
+    public List<UUID> getBuilders() {
+        return this.builders;
+    }
+
+
+    public List<UUID> getContainers() {
+        return this.containers;
+    }
+
+    public List<UUID> getCoowners() {
+        return this.coowners;
+    }
+
+    public List<String> getAccessorGroups() {
+        return this.accessorGroups;
+    }
+
+    public List<String> getBuilderGroups() {
+        return this.builderGroups;
+    }
+
+    public List<String> getContainerGroups() {
+        return this.containerGroups;
+    }
+
+    public List<String> getManagerGroups() {
+        return this.managerGroups;
+    }
+
+    public String getTemplateName() {
+        return this.templateName;
+    }
+
+    public void setTemplateName(String template) {
+        this.templateName = template;
+    }
+
+    public String getTemplateDescription() {
+        return this.templateDescription;
+    }
+
+    public void setTemplateDescription(String description) {
+        this.templateDescription = description;
+    }
+}
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/configuration/ClaimTemplateStorage.java b/sponge/src/main/java/com/griefdefender/configuration/ClaimTemplateStorage.java
new file mode 100644
index 0000000..bfccf0e
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/configuration/ClaimTemplateStorage.java
@@ -0,0 +1,133 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.configuration;
+
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.storage.FileStorage;
+import ninja.leaping.configurate.ConfigurationOptions;
+import ninja.leaping.configurate.commented.CommentedConfigurationNode;
+import ninja.leaping.configurate.commented.SimpleCommentedConfigurationNode;
+import ninja.leaping.configurate.hocon.HoconConfigurationLoader;
+import ninja.leaping.configurate.objectmapping.ObjectMapper;
+import ninja.leaping.configurate.objectmapping.ObjectMappingException;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Optional;
+import java.util.UUID;
+
+public class ClaimTemplateStorage {
+
+    private HoconConfigurationLoader loader;
+    private CommentedConfigurationNode root = SimpleCommentedConfigurationNode.root(ConfigurationOptions.defaults()
+            .setHeader(GriefDefenderPlugin.CONFIG_HEADER));
+    private ObjectMapper<ClaimTemplateConfig>.BoundInstance configMapper;
+    private ClaimTemplateConfig configBase;
+    public Path filePath;
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    public ClaimTemplateStorage(Path path) {
+        this.filePath = path;
+        try {
+            if (Files.notExists(path.getParent())) {
+                Files.createDirectories(path.getParent());
+            }
+            if (Files.notExists(path)) {
+                Files.createFile(path);
+            }
+
+            this.loader = HoconConfigurationLoader.builder().setPath(path).build();
+            this.configMapper = (ObjectMapper.BoundInstance) ObjectMapper.forClass(ClaimTemplateConfig.class).bindToNew();
+
+            if (reload()) {
+                save();
+            }
+        } catch (Exception e) {
+            GriefDefenderPlugin.getInstance().getLogger().error("Failed to initialize claim template data", e);
+        }
+    }
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    public ClaimTemplateStorage(String templateName, Optional<String> description, IClaimData claimData, UUID creator) {
+        this.filePath = FileStorage.rootWorldSavePath.resolve(FileStorage.claimTemplatePath.resolve(UUID.randomUUID().toString()));
+        try {
+            if (Files.notExists(this.filePath.getParent())) {
+                Files.createDirectories(this.filePath.getParent());
+            }
+            if (Files.notExists(this.filePath)) {
+                Files.createFile(this.filePath);
+            }
+
+            this.loader = HoconConfigurationLoader.builder().setPath(this.filePath).build();
+            this.configMapper = (ObjectMapper.BoundInstance) ObjectMapper.forClass(ClaimTemplateConfig.class).bindToNew();
+
+            reload();
+            this.configBase.templateName = templateName;
+            if (description.isPresent()) {
+                this.configBase.templateDescription = description.get();
+            }
+            this.configBase.ownerUniqueId = creator;
+            this.configBase.accessors = new ArrayList<UUID>(claimData.getAccessors());
+            this.configBase.builders = new ArrayList<UUID>(claimData.getBuilders());
+            this.configBase.containers = new ArrayList<UUID>(claimData.getContainers());
+            this.configBase.coowners = new ArrayList<UUID>(claimData.getManagers());
+            //this.configBase.flags = new HashMap<String, Tristate>(claimData.getFlags());
+            save();
+        } catch (Exception e) {
+            GriefDefenderPlugin.getInstance().getLogger().error("Failed to initialize claim template data", e);
+        }
+    }
+
+    public ClaimTemplateConfig getConfig() {
+        return this.configBase;
+    }
+
+    public void save() {
+        try {
+            this.configMapper.serialize(this.root.getNode(GriefDefenderPlugin.MOD_ID));
+            this.loader.save(this.root);
+        } catch (IOException | ObjectMappingException e) {
+            GriefDefenderPlugin.getInstance().getLogger().error("Failed to save configuration", e);
+        }
+    }
+
+    public boolean reload() {
+        try {
+            this.root = this.loader.load(ConfigurationOptions.defaults()
+                    .setHeader(GriefDefenderPlugin.CONFIG_HEADER));
+            this.configBase = this.configMapper.populate(this.root.getNode(GriefDefenderPlugin.MOD_ID));
+        } catch (Exception e) {
+            GriefDefenderPlugin.getInstance().getLogger().error("Failed to load configuration", e);
+            return false;
+        }
+        return true;
+    }
+
+    public CommentedConfigurationNode getRootNode() {
+        return this.root.getNode(GriefDefenderPlugin.MOD_ID);
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/configuration/EconomyDataConfig.java b/sponge/src/main/java/com/griefdefender/configuration/EconomyDataConfig.java
new file mode 100644
index 0000000..cc07510
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/configuration/EconomyDataConfig.java
@@ -0,0 +1,120 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.configuration;
+
+import com.google.gson.Gson;
+import com.griefdefender.api.data.EconomyData;
+import com.griefdefender.api.economy.BankTransaction;
+import com.griefdefender.configuration.category.ConfigCategory;
+import ninja.leaping.configurate.objectmapping.Setting;
+import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable;
+
+import java.time.Instant;
+import java.time.format.DateTimeParseException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+@ConfigSerializable
+public class EconomyDataConfig extends ConfigCategory implements EconomyData {
+
+    public GriefDefenderConfig<?> activeConfig;
+
+    @Setting(value = ClaimStorageData.MAIN_CLAIM_FOR_SALE)
+    private boolean forSale = false;
+    @Setting(value = ClaimStorageData.MAIN_CLAIM_SALE_PRICE)
+    private double salePrice = 0.0;
+    @Setting(value = ClaimStorageData.MAIN_TAX_BALANCE)
+    private double taxBalance = 0.0;
+    @Setting(value = ClaimStorageData.MAIN_TAX_PAST_DUE_DATE)
+    private String taxPastDueDate;
+    @Setting
+    private List<String> bankTransactionLog = new ArrayList<>();
+
+    @Override
+    public boolean isForSale() {
+        return this.forSale;
+    }
+
+    @Override
+    public double getSalePrice() {
+        return this.salePrice;
+    }
+
+    @Override
+    public void setForSale(boolean forSale) {
+        this.forSale = forSale;
+    }
+
+    @Override
+    public double getTaxBalance() {
+        return this.taxBalance;
+    }
+
+    @Override
+    public void setTaxBalance(double balance) {
+        this.taxBalance = balance;
+    }
+
+    @Override
+    public Optional<Instant> getTaxPastDueDate() {
+        if (this.taxPastDueDate == null) {
+            return Optional.empty();
+        }
+        try {
+            return Optional.of(Instant.parse(this.taxPastDueDate));
+        } catch (DateTimeParseException e) {
+            return Optional.empty();
+        }
+    }
+
+    @Override
+    public void setTaxPastDueDate(Instant date) {
+        this.taxPastDueDate = date == null ? null : date.toString();
+    }
+
+    @Override
+    public void setSalePrice(double price) {
+        this.salePrice = price;
+    }
+
+    @Override
+    public List<String> getBankTransactionLog() {
+        return this.bankTransactionLog;
+    }
+
+    @Override
+    public void addBankTransaction(BankTransaction transaction) {
+        if (this.getBankTransactionLog().size() == this.activeConfig.getConfig().claim.bankTransactionLogLimit) {
+            this.getBankTransactionLog().remove(0);
+        }
+        this.getBankTransactionLog().add(new Gson().toJson(transaction));
+    }
+
+    @Override
+    public void clearBankTransactionLog() {
+        this.bankTransactionLog.clear();
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/configuration/GriefDefenderConfig.java b/sponge/src/main/java/com/griefdefender/configuration/GriefDefenderConfig.java
new file mode 100644
index 0000000..65deaf4
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/configuration/GriefDefenderConfig.java
@@ -0,0 +1,230 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.configuration;
+
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.configuration.type.ConfigBase;
+import ninja.leaping.configurate.ConfigurationOptions;
+import ninja.leaping.configurate.Types;
+import ninja.leaping.configurate.ValueType;
+import ninja.leaping.configurate.commented.CommentedConfigurationNode;
+import ninja.leaping.configurate.commented.SimpleCommentedConfigurationNode;
+import ninja.leaping.configurate.hocon.HoconConfigurationLoader;
+import ninja.leaping.configurate.objectmapping.ObjectMapper;
+import ninja.leaping.configurate.objectmapping.ObjectMappingException;
+import ninja.leaping.configurate.util.ConfigurationNodeWalker;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+
+public class GriefDefenderConfig<T extends ConfigBase> {
+
+    private static final ConfigurationOptions LOADER_OPTIONS = ConfigurationOptions.defaults()
+            .setHeader(GriefDefenderPlugin.CONFIG_HEADER);
+
+    private final Path path;
+
+    /**
+     * The parent configuration - values are inherited from this
+     */
+    private final GriefDefenderConfig<?> parent;
+
+    /**
+     * The loader (mapped to a file) used to read/write the config to disk
+     */
+    private HoconConfigurationLoader loader;
+
+    /**
+     * A node representation of "whats actually in the file".
+     */
+    private CommentedConfigurationNode fileData = SimpleCommentedConfigurationNode.root(LOADER_OPTIONS);
+
+    /**
+     * A node representation of {@link #fileData}, merged with the data of {@link #parent}.
+     */
+    private CommentedConfigurationNode data = SimpleCommentedConfigurationNode.root(LOADER_OPTIONS);
+
+    /**
+     * The mapper instance used to populate the config instance
+     */
+    private ObjectMapper<T>.BoundInstance configMapper;
+
+    public GriefDefenderConfig(Class<T> clazz, Path path, GriefDefenderConfig<?> parent) {
+        this.parent = parent;
+        this.path = path;
+
+        try {
+            if (Files.notExists(path.getParent())) {
+                Files.createDirectories(path.getParent());
+            }
+            if (Files.notExists(path)) {
+                Files.createFile(path);
+            }
+
+            this.loader = HoconConfigurationLoader.builder().setPath(path).build();
+            this.configMapper = ObjectMapper.forClass(clazz).bindToNew();
+
+            load();
+            // In order for the removeDuplicates method to function properly, it is extremely
+            // important to avoid running save on parent BEFORE children save. Doing so will
+            // cause duplicate nodes to not be removed properly as parent would have cleaned up
+            // all duplicates prior.
+            // To handle the above issue, we only call save for world configs during init.
+            if (parent != null && parent.parent != null) {
+                save();
+            }
+        } catch (Exception e) {
+            GriefDefenderPlugin.getInstance().getLogger().error("Failed to load configuration at path " + path.toAbsolutePath());
+            e.printStackTrace();
+        }
+    }
+
+    public T getConfig() {
+        return this.configMapper.getInstance();
+    }
+
+    public boolean save() {
+        try {
+            // save from the mapped object --> node
+            CommentedConfigurationNode saveNode = SimpleCommentedConfigurationNode.root(LOADER_OPTIONS);
+            this.configMapper.serialize(saveNode.getNode(GriefDefenderPlugin.MOD_ID));
+
+            // before saving this config, remove any values already declared with the same value on the parent
+            if (this.parent != null) {
+                removeDuplicates(saveNode);
+            }
+
+            // save the data to disk
+            this.loader.save(saveNode);
+
+            // In order for the removeDuplicates method to function properly, it is extremely
+            // important to avoid running save on parent BEFORE children save. Doing so will
+            // cause duplicate nodes to not be removed as parent would have cleaned up
+            // all duplicates prior.
+            // To handle the above issue, we save AFTER saving child config.
+            if (this.parent != null) {
+                this.parent.save();
+            }
+            return true;
+        } catch (IOException | ObjectMappingException e) {
+            GriefDefenderPlugin.getInstance().getLogger().error("Failed to save configuration");
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    public void load() throws IOException, ObjectMappingException {
+        // load settings from file
+        CommentedConfigurationNode loadedNode = this.loader.load();
+
+        // store "what's in the file" separately in memory
+        this.fileData = loadedNode;
+
+        // make a copy of the file data
+        this.data = this.fileData.copy();
+
+        // merge with settings from parent
+        if (this.parent != null) {
+            this.parent.load();
+            this.data.mergeValuesFrom(this.parent.data);
+        }
+
+        // populate the config object
+        populateInstance();
+    }
+
+    private void populateInstance() throws ObjectMappingException {
+        this.configMapper.populate(this.data.getNode(GriefDefenderPlugin.MOD_ID));
+    }
+
+    /**
+     * Traverses the given {@code root} config node, removing any values which
+     * are also present and set to the same value on this configs "parent".
+     *
+     * @param root The node to process
+     */
+    private void removeDuplicates(CommentedConfigurationNode root) {
+        if (this.parent == null) {
+            throw new IllegalStateException("parent is null");
+        }
+
+        Iterator<ConfigurationNodeWalker.VisitedNode<CommentedConfigurationNode>> it = ConfigurationNodeWalker.DEPTH_FIRST_POST_ORDER.walkWithPath(root);
+        while (it.hasNext()) {
+            ConfigurationNodeWalker.VisitedNode<CommentedConfigurationNode> next = it.next();
+            CommentedConfigurationNode node = next.getNode();
+
+            // remove empty maps
+            if (node.hasMapChildren()) {
+                if (node.getChildrenMap().isEmpty()) {
+                    node.setValue(null);
+                }
+                continue;
+            }
+
+            // ignore list values
+            if (node.getParent() != null && node.getParent().getValueType() == ValueType.LIST) {
+                continue;
+            }
+
+            // if the node already exists in the parent config, remove it
+            CommentedConfigurationNode parentValue = this.parent.data.getNode(next.getPath().getArray());
+            if (Objects.equals(node.getValue(), parentValue.getValue())) {
+                node.setValue(null);
+            } else {
+                // Fix list bug
+                if (parentValue.getValue() == null) {
+                    if (node.getValueType() == ValueType.LIST) {
+                        final List<?> nodeList = (List<?>) node.getValue();
+                        if (nodeList.isEmpty()) {
+                            node.setValue(null);
+                        }
+                        continue;
+                    }
+                }
+                // Fix double bug
+                final Double nodeVal = node.getValue(Types::asDouble);
+                if (nodeVal != null) {
+                    Double parentVal = parentValue.getValue(Types::asDouble);
+                    if (parentVal == null && nodeVal.doubleValue() == 0 || (parentVal != null && nodeVal.doubleValue() == parentVal.doubleValue())) {
+                        node.setValue(null);
+                        continue;
+                    }
+                }
+            }
+        }
+    }
+
+    public CommentedConfigurationNode getRootNode() {
+        return this.data.getNode(GriefDefenderPlugin.MOD_ID);
+    }
+
+    public Path getPath() {
+        return this.path;
+    }
+}
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/configuration/IClaimData.java b/sponge/src/main/java/com/griefdefender/configuration/IClaimData.java
new file mode 100644
index 0000000..dfe7c88
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/configuration/IClaimData.java
@@ -0,0 +1,80 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.configuration;
+
+import com.griefdefender.api.claim.ClaimType;
+import com.griefdefender.api.data.ClaimData;
+
+import java.util.List;
+import java.util.UUID;
+
+public interface IClaimData extends ClaimData {
+
+    boolean requiresSave();
+
+    boolean isExpired();
+
+    List<UUID> getAccessors();
+
+    List<UUID> getBuilders();
+
+    List<UUID> getContainers();
+
+    List<UUID> getManagers();
+
+    List<String> getAccessorGroups();
+
+    List<String> getBuilderGroups();
+
+    List<String> getContainerGroups();
+
+    List<String> getManagerGroups();
+
+    void setOwnerUniqueId(UUID newClaimOwner);
+
+    void setWorldUniqueId(UUID uuid);
+
+    void setType(ClaimType type);
+
+    //void setCustomType(ClaimType type);
+
+    void setCuboid(boolean cuboid);
+
+    void setLesserBoundaryCorner(String location);
+
+    void setGreaterBoundaryCorner(String location);
+
+    void setAccessors(List<UUID> accessors);
+
+    void setBuilders(List<UUID> builders);
+
+    void setContainers(List<UUID> containers);
+
+    void setManagers(List<UUID> coowners);
+
+    void setRequiresSave(boolean flag);
+
+    void setExpired(boolean expire);
+}
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/configuration/MessageDataConfig.java b/sponge/src/main/java/com/griefdefender/configuration/MessageDataConfig.java
new file mode 100644
index 0000000..9d75edf
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/configuration/MessageDataConfig.java
@@ -0,0 +1,68 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.configuration;
+
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.configuration.category.ConfigCategory;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.serializer.legacy.LegacyComponentSerializer;
+import ninja.leaping.configurate.objectmapping.Setting;
+import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@ConfigSerializable
+public class MessageDataConfig extends ConfigCategory {
+
+    @Setting("descriptions")
+    public Map<String, String> descriptionMap = new HashMap<>();
+
+    @Setting("messages")
+    public Map<String, String> messageMap = new HashMap<>();
+
+    public Component getMessage(String message) {
+        return this.getMessage(message, ImmutableMap.of());
+    }
+
+    public Component getMessage(String message, Map<String, Object> paramMap) {
+        String rawMessage = this.messageMap.get(message);
+        if (rawMessage == null) {
+            // Should never happen but in case it does, return empty
+            return TextComponent.empty();
+        }
+        for (Map.Entry<String, Object> entry : paramMap.entrySet()) {
+            final String key = entry.getKey();
+            Object value = entry.getValue();
+            if (value instanceof Component) {
+                value = LegacyComponentSerializer.legacy().serialize((Component) value, '&');
+            }
+            rawMessage = rawMessage.replace("{" + key + "}", value.toString()); 
+        }
+
+        return LegacyComponentSerializer.legacy().deserialize(rawMessage, '&');
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/configuration/MessageStorage.java b/sponge/src/main/java/com/griefdefender/configuration/MessageStorage.java
new file mode 100644
index 0000000..bd9db30
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/configuration/MessageStorage.java
@@ -0,0 +1,380 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.configuration;
+
+import com.griefdefender.GriefDefenderPlugin;
+import ninja.leaping.configurate.ConfigurationOptions;
+import ninja.leaping.configurate.commented.CommentedConfigurationNode;
+import ninja.leaping.configurate.commented.SimpleCommentedConfigurationNode;
+import ninja.leaping.configurate.hocon.HoconConfigurationLoader;
+import ninja.leaping.configurate.objectmapping.ObjectMapper;
+import ninja.leaping.configurate.objectmapping.ObjectMappingException;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Map;
+
+public class MessageStorage {
+
+    private HoconConfigurationLoader loader;
+    private CommentedConfigurationNode root = SimpleCommentedConfigurationNode.root(ConfigurationOptions.defaults());
+    private ObjectMapper<MessageDataConfig>.BoundInstance configMapper;
+    private MessageDataConfig configBase;
+    public static MessageDataConfig MESSAGE_DATA;
+
+    // descriptions
+    public static String DESCRIPTION_ABANDON_ALL = "abandon-all";
+    public static String DESCRIPTION_ABANDON_CLAIM = "abandon-claim";
+    public static String DESCRIPTION_ABANDON_TOP = "abandon-top";
+    public static String DESCRIPTION_BUY_BLOCKS = "buy-blocks";
+    public static String DESCRIPTION_CALLBACK = "callback";
+    public static String DESCRIPTION_CLAIM_BANK = "claim-bank";
+    public static String DESCRIPTION_CLAIM_CLEAR = "claim-clear";
+    public static String DESCRIPTION_CLAIM_DEBUG = "claim-debug";
+    public static String DESCRIPTION_CLAIM_FAREWELL = "claim-farewell";
+    public static String DESCRIPTION_CLAIM_GREETING = "claim-greeting";
+    public static String DESCRIPTION_CLAIM_IGNORE = "claim-ignore";
+    public static String DESCRIPTION_CLAIM_INFO = "claim-info";
+    public static String DESCRIPTION_CLAIM_INHERIT = "claim-inherit";
+    public static String DESCRIPTION_CLAIM_LIST = "claim-list";
+    public static String DESCRIPTION_CLAIM_NAME = "claim-name";
+    public static String DESCRIPTION_CLAIM_RESTORE = "claim-restore";
+    public static String DESCRIPTION_CLAIM_SETSPAWN = "claim-setspawn";
+    public static String DESCRIPTION_CLAIM_SPAWN = "claim-spawn";
+    public static String DESCRIPTION_CLAIM_TRANSFER = "claim-transfer";
+    public static String DESCRIPTION_CLAIM_WORLDEDIT = "claim-worldedit";
+    public static String DESCRIPTION_CUBOID = "cuboid";
+    public static String DESCRIPTION_DEBUG = "debug";
+    public static String DESCRIPTION_DELETE_ALL = "delete-all";
+    public static String DESCRIPTION_DELETE_ALL_ADMIN = "delete-all-admin";
+    public static String DESCRIPTION_DELETE_CLAIM = "delete-claim";
+    public static String DESCRIPTION_DELETE_TOP = "delete-top";
+    public static String DESCRIPTION_FLAG_CLAIM = "flag-claim";
+    public static String DESCRIPTION_FLAG_GROUP = "flag-group";
+    public static String DESCRIPTION_FLAG_PLAYER = "flag-player";
+    public static String DESCRIPTION_FLAG_RESET = "flag-reset";
+    public static String DESCRIPTION_MODE_ADMIN = "mode-admin";
+    public static String DESCRIPTION_MODE_BASIC = "mode-basic";
+    public static String DESCRIPTION_MODE_NATURE = "mode-nature";
+    public static String DESCRIPTION_MODE_SUBDIVISION = "mode-subdivision";
+    public static String DESCRIPTION_MODE_TOWN = "mode-town";
+    public static String DESCRIPTION_OPTION_CLAIM = "option-claim";
+    public static String DESCRIPTION_PERMISSION_GROUP = "permission-group";
+    public static String DESCRIPTION_PERMISSION_PLAYER = "permission-player";
+    public static String DESCRIPTION_PLAYER_ADJUST_BONUS_BLOCKS = "player-adjust-bonus-blocks";
+    public static String DESCRIPTION_PLAYER_INFO = "player-info";
+
+    // messages with parameters
+    public static final String ABANDON_CLAIM_DELAY_WARNING = "abandon-claim-delay-warning";
+    public static final String ABANDON_FAILED = "abandon-failed";
+    public static final String ABANDON_OTHER_SUCCESS = "abandon-other-success";
+    public static final String ABANDON_SUCCESS = "abandon-success";
+    public static final String ADJUST_ACCRUED_BLOCKS_SUCCESS = "adjust-accrued-blocks-success";
+    public static final String ADJUST_BONUS_BLOCKS_SUCCESS = "adjust-bonus-blocks-success";
+    public static final String BANK_DEPOSIT = "bank-deposit";
+    public static final String BANK_INFO = "bank-info";
+    public static final String BANK_NO_PERMISSION = "bank-no-permission";
+    public static final String BANK_WITHDRAW = "bank-withdraw";
+    public static final String BANK_WITHDRAW_NO_FUNDS = "bank-withdraw-no-funds";
+    public static final String BLOCK_CLAIMED = "block-claimed";
+    public static final String BLOCK_NOT_CLAIMED = "block-not-claimed";
+    public static final String BLOCK_SALE_VALUE = "block-sale-value";
+    public static final String CLAIM_ABOVE_LEVEL = "claim-above-level";
+    public static final String CLAIM_ACTION_NOT_AVAILABLE = "claim-action-not-available";
+    public static final String CLAIM_BELOW_LEVEL = "claim-below-level";
+    public static final String CLAIM_CHEST_OUTSIDE_LEVEL = "claim-chest-outside-level";
+    public static final String CLAIM_CONTEXT_NOT_FOUND = "claim-context-not-found";
+    public static final String CLAIM_EXPIRED_INACTIVITY = "claim-expired-inactivity";
+    public static final String CLAIM_FAREWELL = "claim-farewell";
+    public static final String CLAIM_FAREWELL_INVALID = "claim-farewell-invalid";
+    public static final String CLAIM_GREETING = "claim-greeting";
+    public static final String CLAIM_LAST_ACTIVE = "claim-last-active";
+    public static final String CLAIM_MODE_START = "claim-mode-start";
+    public static final String CLAIM_NAME = "claim-name";
+    public static final String CLAIM_OWNER_ONLY = "claim-owner-only";
+    public static final String CLAIM_PROTECTED_ENTITY = "claim-protected-entity";
+    public static final String CLAIM_SHOW_NEARBY = "claim-show-nearby";
+    public static final String CLAIM_SIZE_MIN = "claim-size-min";
+    public static final String CLAIM_SIZE_MAX = "claim-size-max";
+    public static final String CLAIM_SIZE_NEED_BLOCKS_2D = "claim-size-need-blocks-2d";
+    public static final String CLAIM_SIZE_NEED_BLOCKS_3D = "claim-size-need-blocks-3d";
+    public static final String CLAIM_SIZE_TOO_SMALL = "size-too-small";
+    public static final String CLAIM_NO_SET_HOME = "claim-no-set-home";
+    public static final String CLAIM_START = "claim-start";
+    public static final String CLAIM_TRANSFER_EXCEEDS_LIMIT = "claim-transfer-exceeds-limit";
+    public static final String CLAIM_TRANSFER_SUCCESS = "claim-transfer-success";
+    public static final String CLAIM_TYPE_NOT_FOUND = "claim-type-not-found";
+    public static final String CLAIMINFO_UI_CLICK_CHANGE_CLAIM = "claiminfo-ui-click-change-claim";
+    public static final String CLAIMINFO_UI_TELEPORT_DIRECTION = "claiminfo-ui-teleport-direction";
+    public static final String CLAIMLIST_UI_CLICK_TELEPORT_TARGET = "claimlist-ui-click-teleport-target";
+    public static final String CLAIMLIST_UI_CLICK_TOGGLE_VALUE = "claimlist-ui-click-toggle-value";
+    public static final String COMMAND_BLOCKED = "command-blocked";
+    public static final String COMMAND_CLAIMBAN_SUCCESS_BLOCK = "command-claimban-success-block";
+    public static final String COMMAND_CLAIMBAN_SUCCESS_ENTITY = "command-claimban-success-entity";
+    public static final String COMMAND_CLAIMBAN_SUCCESS_ITEM = "command-claimban-success-item";
+    public static final String COMMAND_CLAIMCLEAR_NO_ENTITIES = "command-claimclear-no-entities";
+    public static final String COMMAND_CLAIMNAME_NOT_FOUND = "command-claimname-not-found";
+    public static final String COMMAND_CLAIMUNBAN_SUCCESS_BLOCK = "command-claimunban-success-block";
+    public static final String COMMAND_CLAIMUNBAN_SUCCESS_ENTITY = "command-claimunban-success-entity";
+    public static final String COMMAND_CLAIMUNBAN_SUCCESS_ITEM = "command-claimunban-success-item";
+    public static final String COMMAND_EXECUTE_FAILED = "command-execute-failed";
+    public static final String COMMAND_GIVEBLOCKS_CONFIRMATION = "command-giveblocks-confirmation";
+    public static final String COMMAND_GIVEBLOCKS_CONFIRMED = "command-giveblocks-confirmed";
+    public static final String COMMAND_GIVEBLOCKS_NOT_ENOUGH = "command-giveblocks-not-enough";
+    public static final String COMMAND_GIVEBLOCKS_RECEIVED = "command-giveblocks-received";
+    public static final String COMMAND_INVALID_AMOUNT = "command-invalid-amount";
+    public static final String COMMAND_INVALID_CLAIM = "command-invalid-claim";
+    public static final String COMMAND_INVALID_GROUP = "command-invalid-group";
+    public static final String COMMAND_INVALID_PLAYER = "command-invalid-player";
+    public static final String COMMAND_INVALID_TYPE = "command-invalid-type";
+    public static final String COMMAND_OPTION_EXCEEDS_ADMIN = "command-option-exceeds-admin";
+    public static final String COMMAND_PET_INVALID = "command-pet-invalid";
+    public static final String COMMAND_PLAYER_NOT_FOUND = "command-player-not-found";
+    public static final String COMMAND_WORLD_NOT_FOUND = "command-world-not-found";
+    public static final String CREATE_FAILED_CLAIM_LIMIT = "create-failed-claim-limit";
+    public static final String CREATE_FAILED_RESULT = "create-failed-result";
+    public static final String CREATE_INSUFFICIENT_BLOCKS_2D = "create-insufficient-blocks-2d";
+    public static final String CREATE_INSUFFICIENT_BLOCKS_3D = "create-insufficient-blocks-3d";
+    public static final String CREATE_OVERLAP_PLAYER = "create-overlap-player";
+    public static final String CREATE_SUCCESS = "create-success";
+    public static final String DEBUG_ERROR_UPLOAD = "debug-error-upload";
+    public static final String DELETE_ALL_TYPE_DENY = "delete-all-type-deny";
+    public static final String DELETE_ALL_TYPE_SUCCESS = "delete-all-type-success";
+    public static final String DELETE_ALL_TYPE_WARNING = "delete-all-type-warning";
+    public static final String DELETE_ALL_PLAYER_SUCCESS = "delete-all-player-success";
+    public static final String DELETE_ALL_PLAYER_WARNING = "delete-all-player-warning";
+    public static final String DELETE_CLAIM_SUCCESS = "delete-claim-success";
+    public static final String DELETE_CLAIM_WARNING = "delete-claim-warning";
+    public static final String ECONOMY_BLOCK_AVAILABLE_PURCHASE_2D = "economy-block-available-purchase-2d";
+    public static final String ECONOMY_BLOCK_AVAILABLE_PURCHASE_3D = "economy-block-available-purchase-3d";
+    public static final String ECONOMY_BLOCK_PURCHASE_CONFIRMATION = "economy-block-purchase-confirmation";
+    public static final String ECONOMY_BLOCK_PURCHASE_COST = "economy-block-purchase-cost";
+    public static final String ECONOMY_BLOCK_PURCHASE_LIMIT = "economy-block-purchase-limit";
+    public static final String ECONOMY_BLOCK_SALE_CONFIRMATION = "economy-block-sale-confirmation";
+    public static final String ECONOMY_BLOCK_SELL_ERROR = "economy-block-sell-error";
+    public static final String ECONOMY_CLAIM_ABANDON_SUCCESS = "economy-claim-abandon-success";
+    public static final String ECONOMY_CLAIM_BUY_CANCELLED = "economy-claim-buy-cancelled";
+    public static final String ECONOMY_CLAIM_BUY_CONFIRMATION = "economy-claim-buy-confirmation";
+    public static final String ECONOMY_CLAIM_BUY_CONFIRMED = "economy-claim-buy-confirmed";
+    public static final String ECONOMY_CLAIM_BUY_NOT_ENOUGH_FUNDS = "economy-claim-buy-not-enough-funds";
+    public static final String ECONOMY_CLAIM_BUY_TRANSFER_CANCELLED = "economy-claim-buy-transfer-cancelled";
+    public static final String ECONOMY_CLAIM_SALE_CONFIRMATION = "economy-claim-sale-confirmation";
+    public static final String ECONOMY_CLAIM_SALE_CONFIRMED = "economy-claim-sale-confirmed";
+    public static final String ECONOMY_CLAIM_SALE_INVALID_PRICE = "economy-claim-sale-invalid-price";
+    public static final String ECONOMY_CLAIM_SOLD = "economy-claim-sold";
+    public static final String ECONOMY_MODE_BLOCK_SALE_CONFIRMATION = "economy-mode-block-sale-confirmation";
+    public static final String ECONOMY_MODE_RESIZE_SUCCESS_2D = "economy-mode-resize-success-2d";
+    public static final String ECONOMY_MODE_RESIZE_SUCCESS_3D = "economy-mode-resize-success-3d";
+    public static final String ECONOMY_NOT_ENOUGH_FUNDS = "economy-not-enough-funds";
+    public static final String ECONOMY_PLAYER_NOT_FOUND = "economy-player-not-found";
+    public static final String ECONOMY_WITHDRAW_ERROR = "economy-withdraw-error";
+    public static final String FLAG_INVALID_CONTEXT = "flag-invalid-context";
+    public static final String FLAG_INVALID_META = "flag-invalid-meta";
+    public static final String FLAG_INVALID_TARGET = "flag-invalid-target";
+    public static final String FLAG_NOT_FOUND = "flag-not-found";
+    public static final String FLAG_NOT_SET = "flag-not-set";
+    public static final String FLAG_OVERRIDDEN = "flag-overridden";
+    public static final String FLAG_OVERRIDE_NOT_SUPPORTED = "flag-override-not-supported";
+    public static final String FLAG_SET_PERMISSION_TARGET = "flag-set-permission-target";
+    public static final String FLAG_UI_CLICK_TOGGLE = "flag-ui-click-toggle";
+    public static final String FLAG_UI_INHERIT_PARENT = "flag-ui-inherit-parent";
+    public static final String FLAG_UI_OVERRIDE_PERMISSION = "flag-ui-override-permission";
+    public static final String OPTION_INVALID_CONTEXT = "option-invalid-context";
+    public static final String OPTION_INVALID_TARGET = "option-invalid-target";
+    public static final String OPTION_INVALID_VALUE = "option-invalid-value";
+    public static final String OPTION_NOT_FOUND = "option-not-found";
+    public static final String OPTION_NOT_SET = "option-not-set";
+    public static final String OPTION_OVERRIDE_NOT_SUPPORTED = "option-override-not-supported";
+    public static final String OPTION_RESET_SUCCESS = "option-reset-success";
+    public static final String OPTION_SET_TARGET = "option-set-target";
+    public static final String OPTION_UI_CLICK_TOGGLE = "option-ui-click-toggle";
+    public static final String OPTION_UI_INHERIT_PARENT = "option-ui-inherit-parent";
+    public static final String OPTION_UI_OVERRIDDEN = "option-ui-overridden";
+    public static final String PERMISSION_ACCESS = "permission-access";
+    public static final String PERMISSION_BAN_BLOCK = "permission-ban-block";
+    public static final String PERMISSION_BAN_ENTITY = "permission-ban-entity";
+    public static final String PERMISSION_BAN_ITEM = "permission-ban-item";
+    public static final String PERMISSION_BUILD = "permission-build";
+    public static final String PERMISSION_BUILD_NEAR_CLAIM = "permission-build-near-claim";
+    public static final String PERMISSION_CLAIM_DELETE = "permission-claim-delete";
+    public static final String PERMISSION_CLAIM_IGNORE = "permission-claim-ignore";
+    public static final String PERMISSION_CLAIM_MANAGE = "permission-claim-manage";
+    public static final String PERMISSION_CLAIM_RESET_FLAGS = "permission-claim-reset-flags";
+    public static final String PERMISSION_INTERACT_BLOCK = "permission-interact-block";
+    public static final String PERMISSION_INTERACT_ENTITY = "permission-interact-entity";
+    public static final String PERMISSION_INTERACT_ITEM = "permission-interact-item";
+    public static final String PERMISSION_INTERACT_ITEM_BLOCK = "permission-interact-item-block";
+    public static final String PERMISSION_INTERACT_ITEM_ENTITY = "permission-interact-item-entity";
+    public static final String PERMISSION_INVENTORY_OPEN = "permission-inventory-open";
+    public static final String PERMISSION_ITEM_DROP = "permission-item-drop";
+    public static final String PERMISSION_ITEM_USE = "permission-item-use";
+    public static final String PERMISSION_PORTAL_ENTER = "permission-portal-enter";
+    public static final String PERMISSION_PORTAL_EXIT = "permission-portal-exit";
+    public static final String PERMISSION_PROTECTED_PORTAL = "permission-protected-portal";
+    public static final String PERMISSION_TRUST = "permission-trust";
+    public static final String PLAYER_ACCRUED_BLOCKS_EXCEEDED = "player-accrued-blocks-exceeded";
+    public static final String PLAYER_REMAINING_BLOCKS_2D = "player-remaining-blocks-2d";
+    public static final String PLAYER_REMAINING_BLOCKS_3D = "player-remaining-blocks-3d";
+    public static final String PLAYERINFO_UI_ABANDON_RETURN_RATIO = "playerinfo-ui-abandon-return-ratio";
+    public static final String PLAYERINFO_UI_BLOCK_ACCRUED = "playerinfo-ui-block-accrued";
+    public static final String PLAYERINFO_UI_BLOCK_BONUS = "playerinfo-ui-block-bonus";
+    public static final String PLAYERINFO_UI_CLAIM_LEVEL = "playerinfo-ui-claim-level";
+    public static final String PLAYERINFO_UI_CLAIM_SIZE_LIMIT = "playerinfo-ui-claim-size-limit";
+    public static final String PLAYERINFO_UI_BLOCK_INITIAL = "playerinfo-ui-block-initial";
+    public static final String PLAYERINFO_UI_BLOCK_MAX_ACCRUED = "playerinfo-ui-block-max-accrued";
+    public static final String PLAYERINFO_UI_BLOCK_REMAINING = "playerinfo-ui-block-remaining";
+    public static final String PLAYERINFO_UI_BLOCK_TOTAL = "playerinfo-ui-block-total";
+    public static final String PLAYERINFO_UI_CHUNK_TOTAL = "playerinfo-ui-chunk-total";
+    public static final String PLAYERINFO_UI_CLAIM_TOTAL = "playerinfo-ui-claim-total";
+    public static final String PLAYERINFO_UI_ECONOMY_BLOCK_AVAILABLE_PURCHASE = "playerinfo-ui-economy-block-available-purchase";
+    public static final String PLAYERINFO_UI_ECONOMY_BLOCK_COST = "playerinfo-ui-economy-block-cost";
+    public static final String PLAYERINFO_UI_ECONOMY_BLOCK_SELL_RETURN = "playerinfo-ui-economy-block-sell-return";
+    public static final String PLAYERINFO_UI_LAST_ACTIVE = "playerinfo-ui-last-active";
+    public static final String PLAYERINFO_UI_TAX_CURRENT_RATE = "playerinfo-ui-tax-current-rate";
+    public static final String PLAYERINFO_UI_TAX_GLOBAL_CLAIM_RATE = "playerinfo-ui-tax-global-claim-rate";
+    public static final String PLAYERINFO_UI_TAX_GLOBAL_TOWN_RATE = "playerinfo-ui-tax-global-town-rate";
+    public static final String PLAYERINFO_UI_TAX_TOTAL = "playerinfo-ui-tax-total";
+    public static final String PLAYERINFO_UI_UUID = "playerinfo-ui-uuid";
+    public static final String PLAYERINFO_UI_WORLD = "playerinfo-ui-world";
+    public static final String PLUGIN_COMMAND_NOT_FOUND = "plugin-command-not-found";
+    public static final String PLUGIN_NOT_FOUND = "plugin-not-found";
+    public static final String REGISTRY_BLOCK_NOT_FOUND = "registry-type-not-found";
+    public static final String REGISTRY_ENTITY_NOT_FOUND = "registry-entity-not-found";
+    public static final String REGISTRY_ITEM_NOT_FOUND = "registry-item-not-found";
+    public static final String RESIZE_SUCCESS_2D = "resize-success-2d";
+    public static final String RESIZE_SUCCESS_3D = "resize-success-3d";
+    public static final String RESULT_TYPE_CHANGE_DENY = "result-type-change-deny";
+    public static final String RESULT_TYPE_CHANGE_NOT_ADMIN = "result-type-change-not-admin";
+    public static final String RESULT_TYPE_CHILD_SAME = "result-type-child-same";
+    public static final String RESULT_TYPE_CREATE_DENY = "result-type-create-deny";
+    public static final String RESULT_TYPE_NO_CHILDREN = "result-type-no-children";
+    public static final String RESULT_TYPE_ONLY_SUBDIVISION = "result-type-only-subdivision";
+    public static final String RESULT_TYPE_REQUIRES_OWNER = "result-type-requires-owner";
+    public static final String SCHEMATIC_DELETED = "schematic-deleted";
+    public static final String SCHEMATIC_NONE = "schematic-none";
+    public static final String SCHEMATIC_RESTORE_CLICK = "schematic-restore-click"; 
+    public static final String SCHEMATIC_RESTORE_CONFIRMATION = "schematic-restore-confirmation";
+    public static final String SCHEMATIC_RESTORE_CONFIRMED = "schematic-restore-confirmed";
+    public static final String SPAWN_SET_SUCCESS = "spawn-set-success";
+    public static final String SPAWN_TELEPORT = "spawn-teleport";
+    public static final String TAX_CLAIM_EXPIRED = "tax-claim-expired";
+    public static final String TAX_CLAIM_PAID_BALANCE = "tax-claim-paid-balance";
+    public static final String TAX_CLAIM_PAID_PARTIAL = "tax-claim-paid-partial";
+    public static final String TAX_INFO = "tax-info";
+    public static final String TAX_PAST_DUE = "tax-past-due";
+    public static final String TELEPORT_CONFIRM = "teleport-confirm";
+    public static final String TELEPORT_DELAY_NOTICE = "teleport-delay-notice";
+    public static final String TOOL_NOT_EQUIPPED = "tool-not-equipped";
+    public static final String TOWN_CREATE_NOT_ENOUGH_FUNDS = "town-create-not-enough-funds";
+    public static final String TOWN_NAME = "town-name";
+    public static final String TOWN_TAG = "town-tag";
+    public static final String TRUST_ALREADY_HAS = "trust-already-has";
+    public static final String TRUST_GRANT = "trust-grant";
+    public static final String TRUST_INDIVIDUAL_ALL_CLAIMS = "trust-individual-all-claims";
+    public static final String TRUST_PLUGIN_CANCEL = "trust-plugin-cancel";
+    public static final String TUTORIAL_CLAIM_BASIC = "tutorial-claim-basic";
+    public static final String UI_CLICK_FILTER_TYPE = "ui-click-filter-type";
+    public static final String UNTRUST_INDIVIDUAL_ALL_CLAIMS = "untrust-individual-all-claims";
+    public static final String UNTRUST_INDIVIDUAL_SINGLE_CLAIM = "untrust-individual-single-claim";
+    public static final String UNTRUST_OWNER = "untrust-owner";
+
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    public MessageStorage(Path path) {
+
+        try {
+            if (Files.notExists(path.getParent())) {
+                Files.createDirectories(path.getParent());
+            }
+            if (Files.notExists(path)) {
+                Files.createFile(path);
+            }
+
+            this.loader = HoconConfigurationLoader.builder().setPath(path).build();
+            this.configMapper = (ObjectMapper.BoundInstance) ObjectMapper.forClass(MessageDataConfig.class).bindToNew();
+
+            if (reload()) {
+                save();
+            }
+        } catch (Exception e) {
+            GriefDefenderPlugin.getInstance().getLogger().error("Failed to initialize configuration", e);
+        }
+    }
+
+    public MessageDataConfig getConfig() {
+        return this.configBase;
+    }
+
+    public void save() {
+        try {
+            this.configMapper.serialize(this.root.getNode(GriefDefenderPlugin.MOD_ID));
+            this.loader.save(this.root);
+        } catch (IOException | ObjectMappingException e) {
+            GriefDefenderPlugin.getInstance().getLogger().error("Failed to save configuration", e);
+        }
+    }
+
+    public boolean reload() {
+        try {
+            this.root = this.loader.load(ConfigurationOptions.defaults());
+            this.configBase = this.configMapper.populate(this.root.getNode(GriefDefenderPlugin.MOD_ID));
+            MESSAGE_DATA = this.configBase;
+        } catch (Exception e) {
+            GriefDefenderPlugin.getInstance().getLogger().error("Failed to load configuration", e);
+            return false;
+        }
+        return true;
+    }
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    public void resetMessageData(String message) {
+        for (Map.Entry<Object, ? extends CommentedConfigurationNode> mapEntry : this.root.getNode(GriefDefenderPlugin.MOD_ID).getChildrenMap().entrySet()) {
+            CommentedConfigurationNode node = (CommentedConfigurationNode) mapEntry.getValue();
+            String key = "";
+            String comment = node.getComment().orElse(null);
+            if (comment == null && node.getKey() instanceof String) {
+                key = (String) node.getKey();
+                if (key.equalsIgnoreCase(message)) {
+                    this.root.getNode(GriefDefenderPlugin.MOD_ID).removeChild(mapEntry.getKey());
+                }
+            }
+        }
+ 
+        try {
+            this.loader.save(this.root);
+            this.configMapper = (ObjectMapper.BoundInstance) ObjectMapper.forClass(MessageDataConfig.class).bindToNew();
+            this.configBase = this.configMapper.populate(this.root.getNode(GriefDefenderPlugin.MOD_ID));
+        } catch (IOException | ObjectMappingException e) {
+            e.printStackTrace();
+        }
+
+       GriefDefenderPlugin.getInstance().messageData = this.configBase;
+    }
+
+    public CommentedConfigurationNode getRootNode() {
+        return this.root.getNode(GriefDefenderPlugin.MOD_ID);
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/configuration/PlayerDataConfig.java b/sponge/src/main/java/com/griefdefender/configuration/PlayerDataConfig.java
new file mode 100644
index 0000000..1966426
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/configuration/PlayerDataConfig.java
@@ -0,0 +1,78 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.configuration;
+
+import com.griefdefender.configuration.category.ConfigCategory;
+import ninja.leaping.configurate.objectmapping.Setting;
+import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable;
+
+@ConfigSerializable
+public class PlayerDataConfig extends ConfigCategory {
+
+    private boolean requiresSave = true;
+
+    @Setting(value = "accrued-claim-blocks", comment = "How many claim blocks the player has earned in world via play time.")
+    private int accruedClaimBlocks;
+    @Setting(value = "bonus-claim-blocks",
+            comment = "How many claim blocks the player has been gifted in world by admins, or purchased via economy integration.")
+    private int bonusClaimBlocks = 0;
+    @Setting(value = "migrated-blocks")
+    private boolean migrated = false;
+
+    public int getAccruedClaimBlocks() {
+        return this.accruedClaimBlocks;
+    }
+
+    public int getBonusClaimBlocks() {
+        return this.bonusClaimBlocks;
+    }
+
+    public void setAccruedClaimBlocks(int blocks) {
+        this.requiresSave = true;
+        this.accruedClaimBlocks = blocks;
+    }
+
+    public void setBonusClaimBlocks(int blocks) {
+        this.requiresSave = true;
+        this.bonusClaimBlocks = blocks;
+    }
+
+    public boolean requiresSave() {
+        return this.requiresSave;
+    }
+
+    public void setRequiresSave(boolean flag) {
+        this.requiresSave = flag;
+    }
+
+    // Remove after 4.0
+    public boolean hasMigratedBlocks() {
+        return this.migrated;
+    }
+
+    public void setMigratedBlocks(boolean flag) {
+        this.migrated = flag;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/configuration/PlayerStorageData.java b/sponge/src/main/java/com/griefdefender/configuration/PlayerStorageData.java
new file mode 100644
index 0000000..4ce1cd1
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/configuration/PlayerStorageData.java
@@ -0,0 +1,109 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.configuration;
+
+import com.griefdefender.GriefDefenderPlugin;
+import ninja.leaping.configurate.ConfigurationOptions;
+import ninja.leaping.configurate.commented.CommentedConfigurationNode;
+import ninja.leaping.configurate.commented.SimpleCommentedConfigurationNode;
+import ninja.leaping.configurate.hocon.HoconConfigurationLoader;
+import ninja.leaping.configurate.objectmapping.ObjectMapper;
+import ninja.leaping.configurate.objectmapping.ObjectMappingException;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+public class PlayerStorageData {
+
+    private HoconConfigurationLoader loader;
+    private CommentedConfigurationNode root = SimpleCommentedConfigurationNode.root(ConfigurationOptions.defaults());
+    private ObjectMapper<PlayerDataConfig>.BoundInstance configMapper;
+    private PlayerDataConfig configBase;
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    public PlayerStorageData(Path path) {
+
+        try {
+            if (Files.notExists(path.getParent())) {
+                Files.createDirectories(path.getParent());
+            }
+            if (Files.notExists(path)) {
+                Files.createFile(path);
+            }
+
+            this.loader = HoconConfigurationLoader.builder().setPath(path).build();
+            this.configMapper = (ObjectMapper.BoundInstance) ObjectMapper.forClass(PlayerDataConfig.class).bindToNew();
+            this.root = this.loader.load(ConfigurationOptions.defaults());
+            CommentedConfigurationNode rootNode = this.root.getNode(GriefDefenderPlugin.MOD_ID);
+            // Check if server is using existing Sponge GP data
+            if (rootNode.isVirtual()) {
+                // check GriefPrevention
+                CommentedConfigurationNode gpRootNode = this.root.getNode("GriefPrevention");
+                if (!gpRootNode.isVirtual()) {
+                    rootNode.setValue(gpRootNode.getValue());
+                    gpRootNode.setValue(null);
+                }
+            }
+            this.configBase = this.configMapper.populate(rootNode);
+            save();
+        } catch (Exception e) {
+            GriefDefenderPlugin.getInstance().getLogger().error("Failed to initialize configuration", e);
+        }
+    }
+
+    public PlayerDataConfig getConfig() {
+        return this.configBase;
+    }
+
+    public void save() {
+        try {
+            if (this.configBase != null) {
+                if (this.configBase.requiresSave()) {
+                    this.configMapper.serialize(this.root.getNode(GriefDefenderPlugin.MOD_ID));
+                    this.loader.save(this.root);
+                    this.configBase.setRequiresSave(false);
+                }
+            }
+        } catch (IOException | ObjectMappingException e) {
+            GriefDefenderPlugin.getInstance().getLogger().error("Failed to save configuration", e);
+        }
+    }
+
+    public boolean reload() {
+        try {
+            this.root = this.loader.load(ConfigurationOptions.defaults());
+            this.configBase = this.configMapper.populate(this.root.getNode(GriefDefenderPlugin.MOD_ID));
+        } catch (Exception e) {
+            GriefDefenderPlugin.getInstance().getLogger().error("Failed to load configuration", e);
+            return false;
+        }
+        return true;
+    }
+
+    public CommentedConfigurationNode getRootNode() {
+        return this.root.getNode(GriefDefenderPlugin.MOD_ID);
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/configuration/TownDataConfig.java b/sponge/src/main/java/com/griefdefender/configuration/TownDataConfig.java
new file mode 100644
index 0000000..8ba7399
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/configuration/TownDataConfig.java
@@ -0,0 +1,81 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.configuration;
+
+import com.griefdefender.api.data.TownData;
+import net.kyori.text.Component;
+import ninja.leaping.configurate.objectmapping.Setting;
+import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.UUID;
+
+@ConfigSerializable
+public class TownDataConfig extends ClaimDataConfig implements TownData {
+
+    @Setting
+    private Component townTag;
+    @Setting
+    private Map<UUID, Integer> accruedBlocks = new HashMap<>();
+    @Setting
+    private Map<UUID, Integer> bonusBlocks = new HashMap<>();
+    @Setting
+    private Map<UUID, Integer> createMode = new HashMap<>();
+    @Setting
+    private Map<UUID, String> residentPastDueTaxTimestamps = new HashMap<>();
+    @Setting
+    private Map<UUID, Double> residentTaxBalances = new HashMap<>();
+
+    @Override
+    public Optional<Component> getTownTag() {
+        return Optional.ofNullable(this.townTag);
+    }
+
+    public void setTownTag(Component tag) {
+        this.townTag = tag;
+    }
+
+    public Map<UUID, Integer> getAccruedClaimBlocks() {
+        return this.accruedBlocks;
+    }
+
+    public Map<UUID, Integer> getBonusClaimBlocks() {
+        return this.bonusBlocks;
+    }
+
+    public Map<UUID, Integer> getCreateModes() {
+        return this.createMode;
+    }
+
+    public Map<UUID, String> getResidentPastDueTaxTimestamps() {
+        return this.residentPastDueTaxTimestamps;
+    }
+
+    public Map<UUID, Double> getResidentTaxBalances() {
+        return this.residentTaxBalances;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/configuration/TownStorageData.java b/sponge/src/main/java/com/griefdefender/configuration/TownStorageData.java
new file mode 100644
index 0000000..df8acc9
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/configuration/TownStorageData.java
@@ -0,0 +1,45 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.configuration;
+
+import com.griefdefender.api.claim.ClaimTypes;
+
+import java.nio.file.Path;
+import java.util.UUID;
+
+public class TownStorageData extends ClaimStorageData {
+
+    public TownStorageData(Path path, UUID worldUniqueId, UUID ownerUniqueId, boolean cuboid) {
+        super(path, worldUniqueId, ownerUniqueId, ClaimTypes.TOWN, cuboid);
+    }
+
+    public TownStorageData(Path path, UUID worldUniqueId) {
+        super(path, worldUniqueId);
+    }
+
+    public TownDataConfig getConfig() {
+        return (TownDataConfig) this.configBase;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/configuration/category/BanCategory.java b/sponge/src/main/java/com/griefdefender/configuration/category/BanCategory.java
new file mode 100644
index 0000000..624e784
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/configuration/category/BanCategory.java
@@ -0,0 +1,184 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.configuration.category;
+
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import ninja.leaping.configurate.objectmapping.Setting;
+import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+@ConfigSerializable
+public class BanCategory extends ConfigCategory {
+
+    @Setting(value = "blocks")
+    private Map<String, Component> blocks = new HashMap<>();
+    @Setting(value = "entities")
+    private Map<String, Component> entities = new HashMap<>();
+    @Setting(value = "items")
+    private Map<String, Component> items = new HashMap<>();
+    
+
+    public Map<String, Component> getBlockMap() {
+        return this.blocks;
+    }
+
+    public Map<String, Component> getEntityMap() {
+        return this.entities;
+    }
+
+    public Map<String, Component> getItemMap() {
+        return this.items;
+    }
+
+    public boolean isBlockBanned(String id) {
+        if (id == null) {
+            return false;
+        }
+        if (!id.contains(":")) {
+            id = "minecraft:" + id;
+        }
+        return this.blocks.containsKey(id);
+    }
+
+    public void addBlockBan(String id, Component reason) {
+        if (id == null) {
+            return;
+        }
+        if (reason == null) {
+            reason = TextComponent.empty();
+        }
+        if (!id.contains(":")) {
+            id = "minecraft:" + id;
+        }
+        this.blocks.put(id, reason);
+    }
+
+    public void removeBlockBan(String id) {
+        if (id == null) {
+            return;
+        }
+        if (!id.contains(":")) {
+            id = "minecraft:" + id;
+        }
+        this.blocks.remove(id);
+    }
+
+    public Component getBlockBanReason(String id) {
+        if (id == null) {
+            return null;
+        }
+        if (!id.contains(":")) {
+            id = "minecraft:" + id;
+        }
+        return this.blocks.get(id);
+    }
+
+    public boolean isEntityBanned(String id) {
+        if (id == null) {
+            return false;
+        }
+        if (!id.contains(":")) {
+            id = "minecraft:" + id;
+        }
+        return this.entities.containsKey(id);
+    }
+
+    public void addEntityBan(String id, Component reason) {
+        if (id == null) {
+            return;
+        }
+        if (reason == null) {
+            reason = TextComponent.empty();
+        }
+        this.entities.put(id, reason);
+    }
+
+    public void removeEntityBan(String id) {
+        if (id == null) {
+            return;
+        }
+        if (!id.contains(":")) {
+            id = "minecraft:" + id;
+        }
+        this.entities.remove(id);
+    }
+
+    public Component getEntityBanReason(String id) {
+        if (id == null) {
+            return null;
+        }
+        if (!id.contains(":")) {
+            id = "minecraft:" + id;
+        }
+        return this.entities.get(id);
+    }
+
+    public boolean isItemBanned(String id) {
+        if (id == null) {
+            return false;
+        }
+        if (!id.contains(":")) {
+            id = "minecraft:" + id;
+        }
+        return this.items.containsKey(id);
+    }
+
+    public void addItemBan(String id, Component reason) {
+        if (id == null) {
+            return;
+        }
+        if (reason == null) {
+            reason = TextComponent.empty();
+        }
+        if (!id.contains(":")) {
+            id = "minecraft:" + id;
+        }
+        this.items.put(id, reason);
+    }
+
+    public void removeItemBan(String id) {
+        if (id == null) {
+            return;
+        }
+        if (!id.contains(":")) {
+            id = "minecraft:" + id;
+        }
+        this.items.remove(id);
+    }
+
+    public Component getItemBanReason(String id) {
+        if (id == null) {
+            return null;
+        }
+        if (!id.contains(":")) {
+            id = "minecraft:" + id;
+        }
+        return this.items.get(id);
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/configuration/category/BlacklistCategory.java b/sponge/src/main/java/com/griefdefender/configuration/category/BlacklistCategory.java
new file mode 100644
index 0000000..ad94ddd
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/configuration/category/BlacklistCategory.java
@@ -0,0 +1,71 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.configuration.category;
+
+import com.griefdefender.api.permission.flag.Flag;
+import com.griefdefender.registry.FlagRegistryModule;
+import ninja.leaping.configurate.objectmapping.Setting;
+import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+@ConfigSerializable
+public class BlacklistCategory extends ConfigCategory {
+
+    @Setting(value = "flag-id-blacklist", comment = "A list of id's ignored by flags.")
+    public Map<String, List<String>> flagIdBlacklist = new HashMap<>();
+
+    @Setting(value = "global-source", comment = "A global list of source id's that are ignored by events. \nNote: This only affects events where the id specified is the source.")
+    public List<String> globalSourceBlacklist = new ArrayList<>();
+
+    @Setting(value = "global-target", comment = "A global list of target id's that are ignored by events. \nNote: This only affects events where the id specified is the target.")
+    public List<String> globalTargetBlacklist = new ArrayList<>();
+
+    public List<String> getGlobalSourceBlacklist() {
+        return this.globalSourceBlacklist;
+    }
+
+    public List<String> getGlobalTargetBlacklist() {
+        return this.globalTargetBlacklist;
+    }
+
+    public BlacklistCategory() {
+        for (Flag flag : FlagRegistryModule.getInstance().getAll()) {
+            this.flagIdBlacklist.put(flag.getId().toLowerCase(), new ArrayList<>());
+        }
+        this.flagIdBlacklist.put("block-pre", new ArrayList<>());
+    }
+
+    // Used by API
+    @Nullable
+    public List<String> getFlagBlacklist(String flag) {
+        return this.flagIdBlacklist.get(flag);
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/configuration/category/ClaimCategory.java b/sponge/src/main/java/com/griefdefender/configuration/category/ClaimCategory.java
new file mode 100644
index 0000000..7fa903b
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/configuration/category/ClaimCategory.java
@@ -0,0 +1,68 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.configuration.category;
+
+import ninja.leaping.configurate.objectmapping.Setting;
+import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable;
+
+@ConfigSerializable
+public class ClaimCategory extends ConfigCategory {
+
+    @Setting(value = "auto-chest-claim-block-radius", comment = "Radius used (in blocks) for auto-created claim when a chest is placed. Set to -1 to disable chest claim creation.")
+    public int autoChestClaimBlockRadius = 4;
+    @Setting(value = "border-block-radius", comment = "Set claim border of specified radius (in blocks), centered on claim. If set to 1, adds an additional 1 block protected radius around claim.\n" + 
+            "Note: It is not recommended to set this value too high as performance can degrade due to deeper claim searches.")
+    public int borderBlockRadius = 0;
+    @Setting(value = "claim-list-max", comment = "Controls the max displayed claims when using the '/claimlist' command. Default: 200")
+    public int claimListMax = 200;
+    @Setting(value = "expiration-cleanup-interval", comment = "The interval in minutes for cleaning up expired claims. Default: 0. Set to 0 to disable.")
+    public int expirationCleanupInterval = 0;
+    @Setting(value = "auto-nature-restore", comment = "Whether survival claims will be automatically restored to world generated state when expired. \nNote: This only supports world generated blocks. Consider using 'auto-schematic-restore' if using a custom world.")
+    public boolean claimAutoNatureRestore = false;
+    @Setting(value = "auto-schematic-restore", comment = "Whether survival claims will be automatically restored to its claim creation schematic on abandon/expiration. "
+            + "\nNote: Enabling this feature will cause ALL newly created claims to automatically create a special schematic that will be used to restore claim on abandon/expiration."
+            + "\nNote: Enabling this feature will disable ability to resize claims."
+            + "\nNote: It is HIGHLY recommended to disable building in the wilderness before using this feature to avoid players exploiting."
+            + "\nNote: It is also recommended to ONLY use this feature in newly created worlds where there is no existing player data."
+            + "\nNote: This does NOT affect deletions. If admins want to restore back to original schematic, they can select '__restore__' by using /claimschematic command.")
+    public boolean claimAutoSchematicRestore = false;
+    @Setting(value = "investigation-tool", comment = "The item used to investigate claims with a right-click.\nNote: Set to empty quotes if you want to assign no item and use '/claim' mode exclusively.")
+    public String investigationTool = "minecraft:stick";
+    @Setting(value = "modification-tool", comment = "The item used to create/resize claims with a right click.\nNote: Set to empty quotes if you want to assign no item and use '/claim' mode exclusively.")
+    public String modificationTool = "minecraft:golden_shovel";
+    @Setting(value = "claims-enabled",
+            comment = "Whether claiming is enabled or not. (0 = Disabled, 1 = Enabled)")
+    public int claimsEnabled = 1;
+    @Setting(value = "bank-tax-system", comment = "Whether to enable the bank/tax system for claims. Set to true to enable.")
+    public boolean bankTaxSystem = false;
+    @Setting(value = "tax-apply-hour", comment = "The specific hour in day to apply tax to all claims.")
+    public int taxApplyHour = 12;
+    @Setting(value = "bank-transaction-log-limit")
+    public int bankTransactionLogLimit = 60;
+
+    public ClaimCategory() {
+
+    }
+}
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/configuration/category/ConfigCategory.java b/sponge/src/main/java/com/griefdefender/configuration/category/ConfigCategory.java
new file mode 100644
index 0000000..4c9f89e
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/configuration/category/ConfigCategory.java
@@ -0,0 +1,32 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.configuration.category;
+
+import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable;
+
+@ConfigSerializable
+public abstract class ConfigCategory {
+
+}
diff --git a/sponge/src/main/java/com/griefdefender/configuration/category/CustomFlagGroupCategory.java b/sponge/src/main/java/com/griefdefender/configuration/category/CustomFlagGroupCategory.java
new file mode 100644
index 0000000..11f4464
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/configuration/category/CustomFlagGroupCategory.java
@@ -0,0 +1,72 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.configuration.category;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.griefdefender.permission.flag.GDCustomFlagDefinition;
+
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import ninja.leaping.configurate.objectmapping.Setting;
+import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable;
+
+@ConfigSerializable
+public class CustomFlagGroupCategory extends ConfigCategory {
+
+    @Setting
+    boolean enabled = true;
+    @Setting(value = "admin-group", comment = "Set to true if this flag group is for admin use only."
+            + "\nNote: If admin group, the permission is 'griefdefender.admin.custom.flag.<groupname>"
+            + "\nNote: If user group (admin set false), the permission is 'griefdefender.user.custom.flag.<groupname>")
+    boolean isAdmin = false;
+    @Setting(value = "title", comment = "The title text to be used for TAB display.")
+    Component titleText = TextComponent.empty();
+    @Setting(value = "hover", comment = "The hover text to be displayed when hovering over group name in GUI.")
+    Component hoverText = TextComponent.empty();
+    @Setting
+    Map<String, GDCustomFlagDefinition> definitions = new HashMap<>();
+
+    public Map<String, GDCustomFlagDefinition> getFlagDefinitions() {
+        return this.definitions;
+    }
+
+    public Component getTitleComponent() {
+        return this.titleText;
+    }
+
+    public Component getHoverComponent() {
+        return this.hoverText;
+    }
+
+    public boolean isEnabled() {
+        return this.enabled;
+    }
+
+    public boolean isAdminGroup() {
+        return this.isAdmin;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/configuration/category/CustomFlagGroupDefinitionCategory.java b/sponge/src/main/java/com/griefdefender/configuration/category/CustomFlagGroupDefinitionCategory.java
new file mode 100644
index 0000000..91670a6
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/configuration/category/CustomFlagGroupDefinitionCategory.java
@@ -0,0 +1,70 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.configuration.category;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.griefdefender.api.claim.ClaimContexts;
+import com.griefdefender.permission.flag.GDCustomFlagDefinition;
+import com.griefdefender.permission.flag.GDCustomFlagDefinitions;
+
+import ninja.leaping.configurate.objectmapping.Setting;
+import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable;
+
+@ConfigSerializable
+public class CustomFlagGroupDefinitionCategory extends ConfigCategory {
+
+    @Setting
+    Map<String, CustomFlagGroupCategory> groups = new HashMap<>();
+
+    public Map<String, CustomFlagGroupCategory> getGroups() {
+        return this.groups;
+    }
+
+    public void initDefaults() {
+        CustomFlagGroupCategory userGroup = this.groups.get("user");
+        CustomFlagGroupCategory adminGroup = this.groups.get("admin");
+        if (userGroup == null) {
+            userGroup = new CustomFlagGroupCategory();
+        }
+        if (userGroup.isEnabled() && userGroup.getFlagDefinitions().isEmpty()) {
+            for (GDCustomFlagDefinition definition : GDCustomFlagDefinitions.USER_FLAGS) {
+                userGroup.getFlagDefinitions().put(definition.getDisplayName(), definition);
+            }
+            this.groups.put("user", userGroup);
+        }
+        if (adminGroup == null) {
+            adminGroup = new CustomFlagGroupCategory();
+        }
+        if (adminGroup.isEnabled() && adminGroup.getFlagDefinitions().isEmpty()) {
+            for (GDCustomFlagDefinition definition : GDCustomFlagDefinitions.ADMIN_FLAGS) {
+                adminGroup.getFlagDefinitions().put(definition.getDisplayName(), definition);
+            }
+            adminGroup.isAdmin = true;
+            this.groups.put("admin", adminGroup);
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/configuration/category/DefaultPermissionCategory.java b/sponge/src/main/java/com/griefdefender/configuration/category/DefaultPermissionCategory.java
new file mode 100644
index 0000000..cb37697
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/configuration/category/DefaultPermissionCategory.java
@@ -0,0 +1,205 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.configuration.category;
+
+import com.google.common.collect.Maps;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.claim.ClaimBlockSystem;
+import com.griefdefender.api.claim.ClaimType;
+import com.griefdefender.api.claim.ClaimTypes;
+import com.griefdefender.api.permission.flag.Flag;
+import com.griefdefender.api.permission.option.Options;
+import com.griefdefender.registry.ClaimTypeRegistryModule;
+import com.griefdefender.registry.FlagRegistryModule;
+import ninja.leaping.configurate.objectmapping.Setting;
+import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@ConfigSerializable
+public class DefaultPermissionCategory extends ConfigCategory {
+
+    @Setting(value = "default-claim-flags", comment = "The default flag settings used by claims. The group name represents the claim type."
+            + "\nEx: The group admin will ONLY affect admin claims."
+            + "\nSupported groups are : global, admin, basic, subdivision, town, and wilderness."
+            + "\nNote: Global represents all claim types."
+            + "\nNote: Specific types, such as wilderness, have higher priority than global."
+            + "\nNote: Defaults do not force flags onto user claims. A newly created claim will have no flags set and use these default settings until a claim owner sets flags.")
+    private Map<String, Map<String, Boolean>> defaultClaimFlags = Maps.newHashMap();
+
+    @Setting(value = "default-user-options", comment = "The default user options for all players.\nNote: Setting default claim type options will override this.")
+    private Map<String, String> defaultUserOptions = new HashMap<>();
+
+    @Setting(value = "default-user-basic-options", comment = "The default options applied to users for basic claims.\nNote: These options override default global options.")
+    private Map<String, String> defaultBasicOptions = new HashMap<>();
+
+    @Setting(value = "default-user-subdivision-options", comment = "The default options applied to users for subdivisions.\nNote: These options override default global options.")
+    private Map<String, String> defaultSubdivisionOptions = new HashMap<>();
+
+    @Setting(value = "default-user-town-options", comment = "The default options applied to users for towns.\nNote: These options override default global options.")
+    private Map<String, String> defaultTownOptions = new HashMap<>();
+
+    public DefaultPermissionCategory() {
+        Map<String, Boolean> globalFlagMap = new HashMap<>();
+        for (Flag flag : FlagRegistryModule.getInstance().getAll()) {
+            globalFlagMap.put(flag.getName(), flag.getDefaultClaimTypeValue(null));
+        }
+        this.defaultClaimFlags.put("global", globalFlagMap);
+        Map<String, Boolean> wildernessFlagMap = new HashMap<>();
+        for (Flag flag : FlagRegistryModule.getInstance().getAll()) {
+            wildernessFlagMap.put(flag.getName(), flag.getDefaultClaimTypeValue(ClaimTypes.WILDERNESS));
+        }
+        this.defaultClaimFlags.put(ClaimTypes.WILDERNESS.getName().toLowerCase(), wildernessFlagMap);
+
+        final int maxAccruedBlocks = GriefDefenderPlugin.CLAIM_BLOCK_SYSTEM == ClaimBlockSystem.VOLUME ? 20480000 : 80000;
+        this.defaultUserOptions.put(Options.EXPIRATION.getName(), "14");
+        this.defaultUserOptions.put(Options.TAX_EXPIRATION.getName(), "7");
+        this.defaultUserOptions.put(Options.TAX_EXPIRATION_DAYS_KEEP.getName(), "7");
+        this.defaultUserOptions.put(Options.TAX_RATE.getName(), "1.0");
+        this.defaultUserOptions.put(Options.BLOCKS_ACCRUED_PER_HOUR.getName(), "120");
+        this.defaultUserOptions.put(Options.CHEST_EXPIRATION.getName(), "7");
+        this.defaultUserOptions.put(Options.CREATE_LIMIT.getName(), "-1");
+        this.defaultUserOptions.put(Options.CREATE_MODE.getName(), "undefined");
+        this.defaultUserOptions.put(Options.ECONOMY_BLOCK_COST.getName(), "0.0");
+        this.defaultUserOptions.put(Options.ECONOMY_BLOCK_SELL_RETURN.getName(), "0.0");
+        this.defaultUserOptions.put(Options.INITIAL_BLOCKS.getName(), "120");
+        this.defaultUserOptions.put(Options.MAX_ACCRUED_BLOCKS.getName(), Integer.toString(maxAccruedBlocks));
+        this.defaultUserOptions.put(Options.MIN_LEVEL.getName(), "0");
+        this.defaultUserOptions.put(Options.MAX_LEVEL.getName(), "255");
+        this.defaultUserOptions.put(Options.ABANDON_DELAY.getName(), "0");
+        this.defaultUserOptions.put(Options.ABANDON_RETURN_RATIO.getName(), "1.0");
+        this.defaultUserOptions.put(Options.RAID.getName(), "true");
+        this.defaultUserOptions.put(Options.SPAWN_LIMIT.getName(), "-1");
+        this.defaultUserOptions.put(Options.PLAYER_DENY_FLIGHT.getName(), "false");
+        this.defaultUserOptions.put(Options.PLAYER_DENY_GODMODE.getName(), "false");
+        this.defaultUserOptions.put(Options.PLAYER_DENY_HUNGER.getName(), "false");
+        this.defaultUserOptions.put(Options.PLAYER_GAMEMODE.getName(), "undefined");
+        this.defaultUserOptions.put(Options.PLAYER_HEALTH_REGEN.getName(), "-1.0");
+        this.defaultUserOptions.put(Options.PLAYER_KEEP_INVENTORY.getName(), "undefined");
+        this.defaultUserOptions.put(Options.PLAYER_KEEP_LEVEL.getName(), "undefined");
+        this.defaultUserOptions.put(Options.PLAYER_TELEPORT_DELAY.getName(), "0");
+        this.defaultUserOptions.put(Options.PLAYER_WALK_SPEED.getName(), "-1");
+        this.defaultUserOptions.put(Options.PLAYER_WEATHER.getName(), "undefined");
+        this.defaultUserOptions.put(Options.PVP.getName(), "undefined");
+
+        this.defaultBasicOptions.put(Options.MIN_SIZE_X.getName(), "5");
+        this.defaultBasicOptions.put(Options.MIN_SIZE_Y.getName(), "5");
+        this.defaultBasicOptions.put(Options.MIN_SIZE_Z.getName(), "5");
+        this.defaultBasicOptions.put(Options.MAX_SIZE_X.getName(), "0");
+        this.defaultBasicOptions.put(Options.MAX_SIZE_Y.getName(), "256");
+        this.defaultBasicOptions.put(Options.MAX_SIZE_Z.getName(), "0");
+
+        this.defaultSubdivisionOptions.put(Options.MIN_SIZE_X.getName(), "1");
+        this.defaultSubdivisionOptions.put(Options.MIN_SIZE_Y.getName(), "1");
+        this.defaultSubdivisionOptions.put(Options.MIN_SIZE_Z.getName(), "1");
+        this.defaultSubdivisionOptions.put(Options.MAX_SIZE_X.getName(), "1000");
+        this.defaultSubdivisionOptions.put(Options.MAX_SIZE_Y.getName(), "256");
+        this.defaultSubdivisionOptions.put(Options.MAX_SIZE_Z.getName(), "1000");
+
+        this.defaultTownOptions.put(Options.CREATE_LIMIT.getName(), "1");
+        this.defaultTownOptions.put(Options.MIN_LEVEL.getName(), "0");
+        this.defaultTownOptions.put(Options.MIN_SIZE_X.getName(), "32");
+        this.defaultTownOptions.put(Options.MIN_SIZE_Y.getName(), "32");
+        this.defaultTownOptions.put(Options.MIN_SIZE_Z.getName(), "32");
+        this.defaultTownOptions.put(Options.MAX_LEVEL.getName(), "255");
+        this.defaultTownOptions.put(Options.MAX_SIZE_X.getName(), "0");
+        this.defaultTownOptions.put(Options.MAX_SIZE_Y.getName(), "256");
+        this.defaultTownOptions.put(Options.MAX_SIZE_Z.getName(), "0");
+        this.defaultTownOptions.put(Options.TAX_EXPIRATION.getName(), "7");
+        this.defaultTownOptions.put(Options.TAX_EXPIRATION_DAYS_KEEP.getName(), "7");
+        this.defaultTownOptions.put(Options.TAX_RATE.getName(), "1.0");
+    }
+
+    public void checkOptions() {
+        Map<String, String> options = new HashMap<>(this.defaultUserOptions);
+        for (Map.Entry<String, String> mapEntry : options.entrySet()) {
+            if (mapEntry.getKey().equalsIgnoreCase(Options.CREATE_LIMIT.getName()) && mapEntry.getValue().equals("0")) {
+                this.defaultUserOptions.put(mapEntry.getKey(), "undefined");
+                break;
+            }
+        }
+        options = new HashMap<>(this.defaultBasicOptions);
+        for (Map.Entry<String, String> mapEntry : options.entrySet()) {
+            if (mapEntry.getKey().equalsIgnoreCase(Options.CREATE_LIMIT.getName()) && mapEntry.getValue().equals("0")) {
+                this.defaultBasicOptions.put(mapEntry.getKey(), "undefined");
+                break;
+            }
+        }
+        options = new HashMap<>(this.defaultSubdivisionOptions);
+        for (Map.Entry<String, String> mapEntry : options.entrySet()) {
+            if (mapEntry.getKey().equalsIgnoreCase(Options.CREATE_LIMIT.getName()) && mapEntry.getValue().equals("0")) {
+                this.defaultSubdivisionOptions.put(mapEntry.getKey(), "undefined");
+                break;
+            }
+        }
+        options = new HashMap<>(this.defaultTownOptions);
+        for (Map.Entry<String, String> mapEntry : options.entrySet()) {
+            if (mapEntry.getKey().equalsIgnoreCase(Options.CREATE_LIMIT.getName()) && mapEntry.getValue().equals("0")) {
+                this.defaultTownOptions.put(mapEntry.getKey(), "undefined");
+                break;
+            }
+        }
+    }
+
+    public void refreshFlags() {
+        for (ClaimType type : ClaimTypeRegistryModule.getInstance().getAll()) {
+            final Map<String, Boolean> flagTypeMap = this.defaultClaimFlags.get(type.getName().toLowerCase());
+            if (flagTypeMap != null) {
+                for (Flag flag : FlagRegistryModule.getInstance().getAll()) {
+                    if (!flagTypeMap.containsKey(flag.getName())) {
+                        flagTypeMap.put(flag.getName(), flag.getDefaultClaimTypeValue(type));
+                    }
+                }
+            }
+        }
+        final Map<String, Boolean> globalFlagMap = this.defaultClaimFlags.get("global");
+        for (Flag flag : FlagRegistryModule.getInstance().getAll()) {
+            if (!globalFlagMap.containsKey(flag.getName())) {
+                globalFlagMap.put(flag.getName(), flag.getDefaultClaimTypeValue(null));
+            }
+        }
+    }
+
+    public Map<String, Boolean> getFlagDefaults(String type) {
+        return this.defaultClaimFlags.get(type.toLowerCase());
+    }
+
+    public Map<String, String> getBasicOptionDefaults() {
+        return this.defaultBasicOptions;
+    }
+
+    public Map<String, String> getSubdivisionOptionDefaults() {
+        return this.defaultSubdivisionOptions;
+    }
+
+    public Map<String, String> getTownOptionDefaults() {
+        return this.defaultTownOptions;
+    }
+
+    public Map<String, String> getUserOptionDefaults() {
+        return this.defaultUserOptions;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/configuration/category/EconomyCategory.java b/sponge/src/main/java/com/griefdefender/configuration/category/EconomyCategory.java
new file mode 100644
index 0000000..068a934
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/configuration/category/EconomyCategory.java
@@ -0,0 +1,41 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.configuration.category;
+
+import ninja.leaping.configurate.objectmapping.Setting;
+import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable;
+
+@ConfigSerializable
+public class EconomyCategory extends ConfigCategory {
+
+    @Setting(value = "economy-mode", comment = "Uses economy instead of player claim blocks for claim creation."
+            + "\nIf true, disables the claim block system in favor of economy."
+            + "\nNote: Using this mode disables the '/buyblocks' command as claim creation will pull funds directly from a player's economy balance."
+            + "\nNote: If players have existing claimblocks from past configurations, they can use the '/sellblocks' command to convert remainder to currency.")
+    public boolean economyMode = false;
+    @Setting(value = "use-claim-block-task", comment = "Claim blocks earned will be converted to economy based on 'claim-block-cost'." 
+            + "\n(Default: false)\nNote: This setting can only be used if 'economy-mode' is true.")
+    public boolean useClaimBlockTask = false;
+}
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/configuration/category/GeneralCategory.java b/sponge/src/main/java/com/griefdefender/configuration/category/GeneralCategory.java
new file mode 100644
index 0000000..54f7936
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/configuration/category/GeneralCategory.java
@@ -0,0 +1,35 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.configuration.category;
+
+import ninja.leaping.configurate.objectmapping.Setting;
+import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable;
+
+@ConfigSerializable
+public class GeneralCategory extends ConfigCategory {
+
+    @Setting(value = "max-claim-inspection-distance", comment = "The max claim inspection block distance. (Default: 100)")
+    public int maxClaimInspectionDistance = 100;
+}
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/configuration/category/MessageCategory.java b/sponge/src/main/java/com/griefdefender/configuration/category/MessageCategory.java
new file mode 100644
index 0000000..ad89e9d
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/configuration/category/MessageCategory.java
@@ -0,0 +1,44 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.configuration.category;
+
+import ninja.leaping.configurate.objectmapping.Setting;
+import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable;
+
+@ConfigSerializable
+public class MessageCategory extends ConfigCategory {
+
+    @Setting(value = "locale", comment = "Set the locale to use for GD messages. (Default: en_US)\n" +
+            "Available languages: en_US, fr_FR, ru_RU. The data is stored under assets in jar.\n" +
+            "Note: The language code must be lowercase and the country code must be uppercase.")
+    public String locale = "en_US";
+
+    @Setting(value = "enter-exit-show-gd-prefix", comment = "Whether GD prefix should be shown in enter/exit claim messages. (Default: true)")
+    public boolean enterExitShowGdPrefix = true;
+
+    @Setting(value = "enter-exit-chat-type", comment = "The default chat type to use when sending enter/claim messages to a player.\n" + 
+            "(0 = Chat, 1 = ActionBar, 2 = Title)/nNote: ActionBar is only available in MC 1.11+")
+    public int enterExitChatType = 0;
+}
diff --git a/sponge/src/main/java/com/griefdefender/configuration/category/MigratorCategory.java b/sponge/src/main/java/com/griefdefender/configuration/category/MigratorCategory.java
new file mode 100644
index 0000000..5ad3754
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/configuration/category/MigratorCategory.java
@@ -0,0 +1,53 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.configuration.category;
+
+import ninja.leaping.configurate.objectmapping.Setting;
+import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable;
+
+@ConfigSerializable
+public class MigratorCategory extends ConfigCategory {
+
+    @Setting(value = "griefprevention-bukkit", comment = "Set to true to enable the griefprevention bukkit migrator." + 
+            "\nNote: Migrates GP bukkit classic claim data and GPFlags data, if available, to current format." +
+            "\nNote: It is recommended to backup data before using.")
+    public boolean gpBukkitMigrator = false;
+
+    @Setting(value = "griefprevention-sponge", comment = "Set to true to enable the griefprevention sponge migrator." + 
+            "\nNote: Migrates GP sponge claim data to current format." +
+            "\nNote: It is recommended to backup data before using.")
+    public boolean gpSpongeMigrator = false;
+
+    @Setting(value = "red-protect", comment = 
+            "Set to true to enable RedProtect data migrator." +
+            "\nNote: All RedProtect data will be converted into basic claim data.")
+    public boolean redProtectMigrator = false;
+
+    @Setting(value = "worldguard", comment = 
+            "Set to true to enable WorldGuard data migrator." +
+            "\nNote: Only cuboid regions are supported." +
+            "\nNote: It is recommended to backup data before using.")
+    public boolean worldGuardMigrator = false;
+}
diff --git a/sponge/src/main/java/com/griefdefender/configuration/category/ModuleCategory.java b/sponge/src/main/java/com/griefdefender/configuration/category/ModuleCategory.java
new file mode 100644
index 0000000..1fb28a4
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/configuration/category/ModuleCategory.java
@@ -0,0 +1,56 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.configuration.category;
+
+import com.google.common.collect.Maps;
+import com.griefdefender.api.permission.flag.Flag;
+import com.griefdefender.registry.FlagRegistryModule;
+import ninja.leaping.configurate.objectmapping.Setting;
+import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable;
+
+import java.util.Map;
+
+@ConfigSerializable
+public class ModuleCategory {
+
+    @Setting(value = "protection", comment = "Controls which protection modules are enabled." 
+            + "\nNote: If you want full protection, it is recommended to keep everything enabled.")
+    private Map<String, Boolean> protection = Maps.newHashMap();
+
+    public ModuleCategory() {
+        for (Flag flag : FlagRegistryModule.getInstance().getAll()) {
+            this.protection.put(flag.getName().toLowerCase(), true);
+        }
+    }
+
+    public boolean isProtectionModuleEnabled(String flag) {
+        final Boolean result = this.protection.get(flag);
+        if (result == null) {
+            return false;
+        }
+
+        return result;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/configuration/category/OptionCategory.java b/sponge/src/main/java/com/griefdefender/configuration/category/OptionCategory.java
new file mode 100644
index 0000000..4569a6e
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/configuration/category/OptionCategory.java
@@ -0,0 +1,64 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.configuration.category;
+
+import com.griefdefender.api.permission.option.Options;
+import ninja.leaping.configurate.objectmapping.Setting;
+import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@ConfigSerializable
+public class OptionCategory extends ConfigCategory {
+
+    @Setting(value = "user-town-options", comment = "A list of options standard users can manage in their towns with the /co commands.")
+    private List<String> userTownOptions = new ArrayList<>();
+
+    public OptionCategory() {
+        this.userTownOptions.add(Options.ABANDON_RETURN_RATIO.toString());
+        this.userTownOptions.add(Options.BLOCKS_ACCRUED_PER_HOUR.toString());
+        this.userTownOptions.add(Options.EXPIRATION.toString());
+        this.userTownOptions.add(Options.CREATE_LIMIT.toString());
+        this.userTownOptions.add(Options.EXPIRATION.toString());
+        this.userTownOptions.add(Options.INITIAL_BLOCKS.toString());
+        this.userTownOptions.add(Options.MAX_ACCRUED_BLOCKS.toString());
+        this.userTownOptions.add(Options.MAX_LEVEL.toString());
+        this.userTownOptions.add(Options.MAX_SIZE_X.toString());
+        this.userTownOptions.add(Options.MAX_SIZE_Y.toString());
+        this.userTownOptions.add(Options.MAX_SIZE_Z.toString());
+        this.userTownOptions.add(Options.MIN_LEVEL.toString());
+        this.userTownOptions.add(Options.MIN_SIZE_X.toString());
+        this.userTownOptions.add(Options.MIN_SIZE_Y.toString());
+        this.userTownOptions.add(Options.MIN_SIZE_Z.toString());
+        this.userTownOptions.add(Options.TAX_EXPIRATION.toString());
+        this.userTownOptions.add(Options.TAX_EXPIRATION_DAYS_KEEP.toString());
+        this.userTownOptions.add(Options.TAX_RATE.toString());
+    }
+
+    public List<String> getUserTownOptions() {
+        return this.userTownOptions;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/configuration/category/PlayerDataCategory.java b/sponge/src/main/java/com/griefdefender/configuration/category/PlayerDataCategory.java
new file mode 100644
index 0000000..375929a
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/configuration/category/PlayerDataCategory.java
@@ -0,0 +1,58 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.configuration.category;
+
+import com.griefdefender.api.claim.ClaimBlockSystem;
+import ninja.leaping.configurate.objectmapping.Setting;
+import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable;
+
+@ConfigSerializable
+public class PlayerDataCategory extends ConfigCategory {
+
+    @Setting(value = "use-global-storage", comment = "Whether player data should be stored globally. False will store all data per world.")
+    public boolean useGlobalPlayerDataStorage = true;
+    @Setting(value = "claim-block-system", comment = "Determines which claim block system to use for claims. (Default: AREA)\nIf set to VOLUME, claim blocks will use the chunk count system to balance 3d claiming."
+            + "\nIf set to AREA, the standard 2d block count system will be used.")
+    public ClaimBlockSystem claimBlockSystem = ClaimBlockSystem.AREA;
+    @Setting(value = "migrate-volume-rate", comment = "The rate to multiply each accrued claim blocks total by."
+            + "\nSet to a value greater than -1 to enable. (Default: 256)."
+            + "\nNote: This should only be used when migrating from area (2D system) to volume (3D system)."
+            + "\nEach chunk is worth 65,536 blocks in the new system compared to 256 in old."
+            + "\nThis requires 'claim-block-system' to be set to VOLUME.")
+    public int migrateVolumeRate = -1;
+    @Setting(value = "migrate-area-rate", comment = "The rate to divide each accrued claim blocks total by."
+            + "\nSet to a value greater than -1 to enable. (Default: 256)."
+            + "\nNote: This should only be used when migrating from volume (3D system) to area (2D system)."
+            + "\nIn this system, a chunk costs 256 blocks."
+            + "\nThis requires 'claim-block-system' to be set to AREA.")
+    public int migrateAreaRate = -1;
+    @Setting(value = "reset-migrations", comment = "If enabled, resets all playerdata migration flags to allow for another migration."
+            + "\nNote: Use this with caution as it can easily mess up claim block data. It is highly recommended to backup before using.")
+    public boolean resetMigrations = false;
+    @Setting(value = "reset-accrued-claim-blocks", comment = "If enabled, resets all playerdata accrued claim blocks to match total cost of claims owned."
+            + "\nExample: If a player has 5 basic claims with a total cost of 1000, this will set their accrued claim blocks to 1000."
+            + "\nNote: This will also reset all bonus claim blocks to 0. It is highly recommended to backup before using.")
+    public boolean resetAccruedClaimBlocks = false;
+}
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/configuration/category/PvpCategory.java b/sponge/src/main/java/com/griefdefender/configuration/category/PvpCategory.java
new file mode 100644
index 0000000..1a2a7e1
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/configuration/category/PvpCategory.java
@@ -0,0 +1,35 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.configuration.category;
+
+import ninja.leaping.configurate.objectmapping.Setting;
+import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable;
+
+@ConfigSerializable
+public class PvpCategory extends ConfigCategory {
+
+    @Setting(value = "combat-timeout", comment = "How long combat is considered to continue after the most recent damage.")
+    public int combatTimeout = 15;
+}
diff --git a/sponge/src/main/java/com/griefdefender/configuration/category/ThreadCategory.java b/sponge/src/main/java/com/griefdefender/configuration/category/ThreadCategory.java
new file mode 100644
index 0000000..b5c0a50
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/configuration/category/ThreadCategory.java
@@ -0,0 +1,35 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.configuration.category;
+
+import ninja.leaping.configurate.objectmapping.Setting;
+import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable;
+
+@ConfigSerializable
+public class ThreadCategory extends ConfigCategory {
+
+    @Setting(value = "executor-threads", comment = "The number of threads to use for GD's executor. (Default: 1)")
+    public int numExecutorThreads = 1;
+}
diff --git a/sponge/src/main/java/com/griefdefender/configuration/category/TownCategory.java b/sponge/src/main/java/com/griefdefender/configuration/category/TownCategory.java
new file mode 100644
index 0000000..2ad7ae1
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/configuration/category/TownCategory.java
@@ -0,0 +1,45 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.configuration.category;
+
+import ninja.leaping.configurate.objectmapping.Setting;
+import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable;
+
+@ConfigSerializable
+public class TownCategory extends ConfigCategory {
+
+    @Setting(value = "cleanup-task-interval", comment = "The interval in minutes for restoring blocks in an expired town. Set to 0 to disable. Note: This only supports vanilla blocks. Use with caution if using custom biomes.")
+    public int cleanupTaskInterval = 5;
+    @Setting(value = "auto-nature-restore", comment = "Whether survival towns will be automatically restored to nature when auto-deleted.")
+    public boolean autoNatureRestore = false;
+    @Setting(value = "creation-cost", comment = "The required amount of funds to create a town. (Default: 0)\nNote: This requires an Economy plugin.")
+    public double cost = 0;
+    @Setting(value = "clan-require-town", comment = "If true, requires a town to be owned for MCClans.")
+    public boolean clanRequireTown = true;
+
+    public TownCategory() {
+
+    }
+}
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/configuration/category/VisualCategory.java b/sponge/src/main/java/com/griefdefender/configuration/category/VisualCategory.java
new file mode 100644
index 0000000..db499f7
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/configuration/category/VisualCategory.java
@@ -0,0 +1,94 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.configuration.category;
+
+import ninja.leaping.configurate.objectmapping.Setting;
+import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable;
+import org.spongepowered.api.MinecraftVersion;
+import org.spongepowered.api.Sponge;
+
+@ConfigSerializable
+public class VisualCategory extends ConfigCategory {
+
+    @Setting(value = "hide-borders-when-using-wecui", comment = "Whether to hide the glowstone/gold block borders when using WECUI.")
+    public boolean hideBorders = false;
+    @Setting(value = "hide-wecui-drag-visuals-2d", comment = "Whether drag visuals should be shown while creating a claim in 2D mode.")
+    public boolean hideDrag2d = true;
+
+    @Setting(value = "claim-create-block", comment = "The visual block used during claim creation. (Default: minecraft:diamond_block)")
+    public String claimCreateStartBlock = "minecraft:diamond_block";
+
+    @Setting(value = "admin-accent-block", comment = "The visual accent block used for admin claims. (Default: minecraft:pumpkin)")
+    public String visualAdminAccentBlock = "minecraft:pumpkin";
+    @Setting(value = "admin-corner-block", comment = "The visual corner block used for admin claims. (Default: minecraft:glowstone)")
+    public String visualAdminCornerBlock = "minecraft:glowstone";
+    @Setting(value = "admin-filler-block", comment = "The visual filler block used for admin claims. (Default: minecraft:pumpkin)")
+    public String visualAdminFillerBlock = "minecraft:pumpkin";
+
+    @Setting(value = "basic-accent-block", comment = "The visual accent block used for basic claims. (Default: minecraft:gold_block)")
+    public String visualBasicAccentBlock = "minecraft:gold_block";
+    @Setting(value = "basic-corner-block", comment = "The visual corner block used for basic claims. (Default: minecraft:glowstone)")
+    public String visualBasicCornerBlock = "minecraft:glowstone";
+    @Setting(value = "basic-filler-block", comment = "The visual filler block used for basic claims. (Default: minecraft:gold_block)")
+    public String visualBasicFillerBlock = "minecraft:gold_block";
+
+    @Setting(value = "error-accent-block", comment = "The visual accent block used to visualize an error in a claim. (Default: minecraft:netherrack)")
+    public String visualErrorAccentBlock = "minecraft:netherrack";
+    @Setting(value = "error-corner-block", comment = "The visual corner block used to visualize an error in a claim. (Default: minecraft:redstone_ore)")
+    public String visualErrorCornerBlock = "minecraft:redstone_ore";
+    @Setting(value = "error-filler-block", comment = "The visual filler block used to visualize an error in a claim. (Default: minecraft:diamond_block)")
+    public String visualErrorFillerBlock = "minecraft:diamond_block";
+
+    @Setting(value = "subdivision-accent-block", comment = "The visual accent block used for subdivision claims. (Default: minecraft:white_wool or minecraft:wool for legacy versions)")
+    public String visualSubdivisionAccentBlock;
+    @Setting(value = "subdivision-corner-block", comment = "The visual corner block used for subdivision claims. (Default: minecraft:iron_block)")
+    public String visualSubdivisionCornerBlock = "minecraft:iron_block";
+    @Setting(value = "subdivision-filler-block", comment = "The visual filler block used for subdivision claims. (Default: minecraft:white_wool or minecraft:wool for legacy versions)")
+    public String visualSubdivisionFillerBlock;
+
+    @Setting(value = "town-accent-block", comment = "The visual accent block used for town claims. (Default: minecraft:emerald_block)")
+    public String visualTownAccentBlock = "minecraft:emerald_block";
+    @Setting(value = "town-corner-block", comment = "The visual corner block used for town claims. (Default: minecraft:glowstone)")
+    public String visualTownCornerBlock = "minecraft:glowstone";
+    @Setting(value = "town-filler-block", comment = "The visual filler block used for town claims. (Default: minecraft:emerald_block)")
+    public String visualTownFillerBlock = "minecraft:emerald_block";
+
+    @Setting(value = "nature-accent-block", comment = "The visual accent block used while in restore nature mode. (Default: minecraft:diamond_block)")
+    public String visualNatureAccentBlock = "minecraft:diamond_block";
+    @Setting(value = "nature-corner-block", comment = "The visual corner block used while in restore nature mode. (Default: minecraft:diamond_block)")
+    public String visualNatureCornerBlock = "minecraft:diamond_block";
+
+    public VisualCategory() {
+        final MinecraftVersion version = Sponge.getPlatform().getMinecraftVersion();
+        if (version.getName().contains("1.8.8") || version.getName().contains("1.12")) {
+            this.visualSubdivisionAccentBlock = "minecraft:wool";
+            this.visualSubdivisionFillerBlock = "minecraft:wool";
+        } else {
+            this.visualSubdivisionAccentBlock = "minecraft:white_wool";
+            this.visualSubdivisionFillerBlock = "minecraft:white_wool";
+        }
+    }
+
+}
diff --git a/sponge/src/main/java/com/griefdefender/configuration/serializer/ClaimTypeSerializer.java b/sponge/src/main/java/com/griefdefender/configuration/serializer/ClaimTypeSerializer.java
new file mode 100644
index 0000000..fc48e3e
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/configuration/serializer/ClaimTypeSerializer.java
@@ -0,0 +1,50 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.configuration.serializer;
+
+import com.google.common.reflect.TypeToken;
+import com.griefdefender.api.claim.ClaimType;
+import com.griefdefender.registry.ClaimTypeRegistryModule;
+import ninja.leaping.configurate.ConfigurationNode;
+import ninja.leaping.configurate.objectmapping.ObjectMappingException;
+import ninja.leaping.configurate.objectmapping.serialize.TypeSerializer;
+
+public class ClaimTypeSerializer implements TypeSerializer<ClaimType> {
+
+    @Override
+    public ClaimType deserialize(TypeToken<?> type, ConfigurationNode node) throws ObjectMappingException {
+        ClaimType ret = ClaimTypeRegistryModule.getInstance().getById(node.getString().toLowerCase()).orElse(null);
+        if (ret == null) {
+            throw new ObjectMappingException("Input '" + node.getValue() + "' was not a valid value for type " + type);
+        }
+        return ret;
+    }
+
+    @Override
+    public void serialize(TypeToken<?> type, ClaimType obj, ConfigurationNode node) throws ObjectMappingException {
+       node.setValue(obj.getName());
+    }
+
+}
diff --git a/sponge/src/main/java/com/griefdefender/configuration/serializer/ComponentConfigSerializer.java b/sponge/src/main/java/com/griefdefender/configuration/serializer/ComponentConfigSerializer.java
new file mode 100644
index 0000000..159af38
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/configuration/serializer/ComponentConfigSerializer.java
@@ -0,0 +1,86 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.configuration.serializer;
+
+import com.google.common.reflect.TypeToken;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.serializer.gson.GsonComponentSerializer;
+import net.kyori.text.serializer.legacy.LegacyComponentSerializer;
+import ninja.leaping.configurate.ConfigurationNode;
+import ninja.leaping.configurate.gson.GsonConfigurationLoader;
+import ninja.leaping.configurate.loader.HeaderMode;
+import ninja.leaping.configurate.objectmapping.ObjectMappingException;
+import ninja.leaping.configurate.objectmapping.serialize.TypeSerializer;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.StringWriter;
+
+public class ComponentConfigSerializer implements TypeSerializer<Component> {
+
+    /**
+     * Creates a new {@link ComponentConfigSerializer}. Normally this should not
+     * need to be created more than once.
+     */
+    public ComponentConfigSerializer() {
+    }
+
+    @Override
+    public Component deserialize(TypeToken<?> type, ConfigurationNode node) throws ObjectMappingException {
+        if (node.getString() == null || node.getString().isEmpty()) {
+            return TextComponent.empty();
+        }
+        if (node.getString().contains("text=")) {
+            // Try sponge data
+            StringWriter writer = new StringWriter();
+
+            GsonConfigurationLoader gsonLoader = GsonConfigurationLoader.builder()
+                    .setIndent(0)
+                    .setSink(() -> new BufferedWriter(writer))
+                    .setHeaderMode(HeaderMode.NONE)
+                    .build();
+
+            try {
+                gsonLoader.save(node);
+            } catch (IOException e) {
+                throw new ObjectMappingException(e);
+            }
+            return GsonComponentSerializer.INSTANCE.deserialize(writer.toString());
+        }
+
+        return LegacyComponentSerializer.legacy().deserialize(node.getString(), '&');
+    }
+
+    @Override
+    public void serialize(TypeToken<?> type, Component obj, ConfigurationNode node) throws ObjectMappingException {
+        if (obj == TextComponent.empty()) {
+            node.setValue("");
+        } else {
+            node.setValue(LegacyComponentSerializer.legacy().serialize(obj, '&'));
+        }
+    }
+
+}
diff --git a/sponge/src/main/java/com/griefdefender/configuration/serializer/CreateModeTypeSerializer.java b/sponge/src/main/java/com/griefdefender/configuration/serializer/CreateModeTypeSerializer.java
new file mode 100644
index 0000000..aadb03b
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/configuration/serializer/CreateModeTypeSerializer.java
@@ -0,0 +1,54 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.configuration.serializer;
+
+import com.google.common.reflect.TypeToken;
+import com.griefdefender.api.permission.option.type.CreateModeType;
+import com.griefdefender.api.permission.option.type.CreateModeTypes;
+
+import ninja.leaping.configurate.ConfigurationNode;
+import ninja.leaping.configurate.objectmapping.ObjectMappingException;
+import ninja.leaping.configurate.objectmapping.serialize.TypeSerializer;
+
+public class CreateModeTypeSerializer implements TypeSerializer<CreateModeType> {
+
+    @Override
+    public CreateModeType deserialize(TypeToken<?> type, ConfigurationNode node) throws ObjectMappingException {
+        switch (node.getString().toLowerCase()) {
+            case "area" :
+                return CreateModeTypes.AREA;
+            case "volume" : 
+                return CreateModeTypes.VOLUME;
+            default :
+                return CreateModeTypes.UNDEFINED;
+        }
+    }
+
+    @Override
+    public void serialize(TypeToken<?> type, CreateModeType obj, ConfigurationNode node) throws ObjectMappingException {
+       node.setValue(obj.getName());
+    }
+
+}
diff --git a/sponge/src/main/java/com/griefdefender/configuration/serializer/CustomFlagSerializer.java b/sponge/src/main/java/com/griefdefender/configuration/serializer/CustomFlagSerializer.java
new file mode 100644
index 0000000..a0808ad
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/configuration/serializer/CustomFlagSerializer.java
@@ -0,0 +1,193 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.configuration.serializer;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+
+import com.google.common.reflect.TypeToken;
+import com.griefdefender.api.permission.Context;
+import com.griefdefender.api.permission.ContextKeys;
+import com.griefdefender.api.permission.flag.Flag;
+import com.griefdefender.permission.flag.CustomFlagData;
+import com.griefdefender.permission.flag.GDCustomFlagDefinition;
+import com.griefdefender.registry.FlagRegistryModule;
+
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.serializer.legacy.LegacyComponentSerializer;
+import ninja.leaping.configurate.ConfigurationNode;
+import ninja.leaping.configurate.objectmapping.ObjectMappingException;
+import ninja.leaping.configurate.objectmapping.serialize.TypeSerializer;
+
+public class CustomFlagSerializer implements TypeSerializer<GDCustomFlagDefinition> {
+
+    @Override
+    public GDCustomFlagDefinition deserialize(TypeToken<?> type, ConfigurationNode node) throws ObjectMappingException {
+        final String flagDisplayName = node.getKey().toString();
+        final boolean enabled = node.getNode("enabled").getBoolean();
+        final String descr = node.getNode("description").getString();
+        Component description = TextComponent.empty();
+        if (descr != null) {
+            description = LegacyComponentSerializer.legacy().deserialize(descr, '&');
+        }
+
+        List<String> contextList = node.getNode("contexts").getList(TypeToken.of(String.class));
+        List<String> permissionList = node.getNode("permissions").getList(TypeToken.of(String.class));
+        if (permissionList == null) {
+            throw new ObjectMappingException("No permissions found for flag definition '" + flagDisplayName + "'. You must specify at least 1 or more permissions.");
+        }
+
+        List<CustomFlagData> flagDataList = new ArrayList<>();
+        for (String permissionEntry : permissionList) {
+            String permission = permissionEntry.replace(" ", "");
+            String[] parts = permission.split(",");
+            Flag linkedFlag = null;
+            Set<Context> flagContexts = new HashSet<>();
+            for (String part : parts) {
+                String[] split =  part.split("=");
+                String key = split[0];
+                String value = split[1];
+                // Handle linked Flag
+                if (key.equalsIgnoreCase("flag")) {
+                    final String flagName = value;
+                    linkedFlag = FlagRegistryModule.getInstance().getById(flagName).orElse(null);
+                    if (linkedFlag == null) {
+                        throw new ObjectMappingException("Input '" + flagName + "' is not a valid GD flag to link to.");
+                    }
+                } else { //contexts
+                    // validate context key
+                    switch (key) {
+                        case ContextKeys.SOURCE:
+                        case ContextKeys.TARGET:
+                            if (!value.contains(":") && !value.contains("#")) {
+                                value = "minecraft:" + value;
+                            }
+                            flagContexts.add(new Context(key, value));
+                            break;
+                        case "used_item":
+                        case "item_name":
+                        case ContextKeys.CLAIM_DEFAULT:
+                        case ContextKeys.CLAIM_OVERRIDE:
+                        case ContextKeys.STATE:
+                            flagContexts.add(new Context(key, value));
+                            break;
+                        default:
+                            throw new ObjectMappingException("Invalid context '" + key + "' with value '" + value + "'.");
+                    }
+                }
+            }
+            if (linkedFlag == null) {
+                throw new ObjectMappingException("No linked flag specified. You need to specify 'flag=<flagname>'.");
+            }
+
+            flagDataList.add(new CustomFlagData(linkedFlag, flagContexts));
+        }
+        final GDCustomFlagDefinition flagDefinition = new GDCustomFlagDefinition(flagDataList, flagDisplayName, description);
+        flagDefinition.setIsEnabled(enabled);
+        Set<Context> contexts = new HashSet<>();
+        if (contextList != null) {
+            for (String context : contextList) {
+                final String parts[] = context.split("=");
+                if (parts.length <= 1) {
+                    throw new ObjectMappingException("Invalid context '" + context + "' for flag definition '" + flagDisplayName + "'. Skipping...");
+                }
+                final String key = parts[0];
+                final String value = parts[1];
+                if (key.equalsIgnoreCase("default") || key.equalsIgnoreCase("gd_claim_default")) {
+                    if (!value.equalsIgnoreCase("global") && !value.equalsIgnoreCase("basic") && !value.equalsIgnoreCase("admin")
+                            && !value.equalsIgnoreCase("subdivision") && !value.equalsIgnoreCase("town")) {
+                        throw new ObjectMappingException("Invalid context '" + key + "' with value '" + value + "'.");
+                    }
+                    contexts.add(new Context("gd_claim_default", value));
+                } else if (key.equalsIgnoreCase("override") || key.equalsIgnoreCase("gd_claim_override")) {
+                    if (!value.equalsIgnoreCase("global") && !value.equalsIgnoreCase("basic") && !value.equalsIgnoreCase("admin")
+                            && !value.equalsIgnoreCase("subdivision") && !value.equalsIgnoreCase("town")) {
+                        // try UUID
+                        if (value.length() == 36) {
+                            UUID uuid = null;
+                            try {
+                                uuid = UUID.fromString(value);
+                            } catch (IllegalArgumentException e) {
+                                throw new ObjectMappingException("Invalid context '" + key + "' with value '" + value + "'.");
+                            }
+                        } else {
+                            throw new ObjectMappingException("Invalid context '" + key + "' with value '" + value + "'.");
+                        }
+                    }
+                    contexts.add(new Context("gd_claim_override", value));
+                } else {
+                    contexts.add(new Context(key, value));
+                }
+            }
+            flagDefinition.setDefinitionContexts(contexts);
+        }
+        return flagDefinition;
+    }
+
+    @Override
+    public void serialize(TypeToken<?> type, GDCustomFlagDefinition obj, ConfigurationNode node) throws ObjectMappingException {
+        node.getNode("enabled").setValue(obj.isEnabled());
+        String description = "";
+        if (obj.getDescription() != TextComponent.empty()) {
+            description = LegacyComponentSerializer.legacy().serialize((Component) obj.getDescription(), '&');
+            node.getNode("description").setValue(description);
+        }
+
+        if (!obj.getDefinitionContexts().isEmpty()) {
+            List<String> contextList = new ArrayList<>();
+            ConfigurationNode contextNode = node.getNode("contexts");
+            for (Context context : obj.getDefinitionContexts()) {
+                contextList.add(context.getKey().toLowerCase() + "=" + context.getValue().toLowerCase());
+            }
+            contextNode.setValue(contextList);
+        }
+        ConfigurationNode permissionNode = node.getNode("permissions");
+        List<String> permissions = new ArrayList<>();
+        for (CustomFlagData flagData : obj.getFlagData()) {
+            int count = 0;
+            final Flag flag = flagData.getFlag();
+            final Set<Context> dataContexts = flagData.getContexts();
+            String permission = "";
+            if (count > 0) {
+                permission += ", ";
+            }
+            permission += "flag=" + flag.getName().toLowerCase();
+            count++;
+
+            for (Context context : dataContexts) {
+                String key = context.getKey();
+                permission += ", " + key + "=" + context.getValue();
+            }
+
+            permissions.add(permission);
+        }
+        permissionNode.setValue(permissions);
+    }
+
+}
diff --git a/sponge/src/main/java/com/griefdefender/configuration/serializer/GameModeTypeSerializer.java b/sponge/src/main/java/com/griefdefender/configuration/serializer/GameModeTypeSerializer.java
new file mode 100644
index 0000000..72fd7e4
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/configuration/serializer/GameModeTypeSerializer.java
@@ -0,0 +1,58 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.configuration.serializer;
+
+import com.google.common.reflect.TypeToken;
+import com.griefdefender.api.permission.option.type.GameModeType;
+import com.griefdefender.api.permission.option.type.GameModeTypes;
+
+import ninja.leaping.configurate.ConfigurationNode;
+import ninja.leaping.configurate.objectmapping.ObjectMappingException;
+import ninja.leaping.configurate.objectmapping.serialize.TypeSerializer;
+
+public class GameModeTypeSerializer implements TypeSerializer<GameModeType> {
+
+    @Override
+    public GameModeType deserialize(TypeToken<?> type, ConfigurationNode node) throws ObjectMappingException {
+        switch(node.getString().toLowerCase()) {
+            case "adventure" :
+                return GameModeTypes.ADVENTURE;
+            case "creative" :
+                return GameModeTypes.CREATIVE;
+            case "spectator" :
+                return GameModeTypes.SPECTATOR;
+            case "survival" :
+                return GameModeTypes.SURVIVAL;
+            default : 
+                return GameModeTypes.UNDEFINED;
+        }
+    }
+
+    @Override
+    public void serialize(TypeToken<?> type, GameModeType obj, ConfigurationNode node) throws ObjectMappingException {
+       node.setValue(obj.getName());
+    }
+
+}
diff --git a/sponge/src/main/java/com/griefdefender/configuration/serializer/WeatherTypeSerializer.java b/sponge/src/main/java/com/griefdefender/configuration/serializer/WeatherTypeSerializer.java
new file mode 100644
index 0000000..6151a57
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/configuration/serializer/WeatherTypeSerializer.java
@@ -0,0 +1,54 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.configuration.serializer;
+
+import com.google.common.reflect.TypeToken;
+import com.griefdefender.api.permission.option.type.WeatherType;
+import com.griefdefender.api.permission.option.type.WeatherTypes;
+
+import ninja.leaping.configurate.ConfigurationNode;
+import ninja.leaping.configurate.objectmapping.ObjectMappingException;
+import ninja.leaping.configurate.objectmapping.serialize.TypeSerializer;
+
+public class WeatherTypeSerializer implements TypeSerializer<WeatherType> {
+
+    @Override
+    public WeatherType deserialize(TypeToken<?> type, ConfigurationNode node) throws ObjectMappingException {
+        switch (node.getString().toLowerCase()) {
+        case "clear" :
+            return WeatherTypes.CLEAR;
+        case "rain" : 
+            return WeatherTypes.RAIN;
+        default :
+            return WeatherTypes.UNDEFINED;
+    }
+    }
+
+    @Override
+    public void serialize(TypeToken<?> type, WeatherType obj, ConfigurationNode node) throws ObjectMappingException {
+       node.setValue(obj.getName());
+    }
+
+}
diff --git a/sponge/src/main/java/com/griefdefender/configuration/type/ConfigBase.java b/sponge/src/main/java/com/griefdefender/configuration/type/ConfigBase.java
new file mode 100644
index 0000000..7888462
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/configuration/type/ConfigBase.java
@@ -0,0 +1,71 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.configuration.type;
+
+import com.griefdefender.configuration.category.BanCategory;
+import com.griefdefender.configuration.category.BlacklistCategory;
+import com.griefdefender.configuration.category.ClaimCategory;
+import com.griefdefender.configuration.category.GeneralCategory;
+import com.griefdefender.configuration.category.OptionCategory;
+import com.griefdefender.configuration.category.PvpCategory;
+import com.griefdefender.configuration.category.TownCategory;
+import com.griefdefender.configuration.category.VisualCategory;
+
+import ninja.leaping.configurate.objectmapping.Setting;
+
+public class ConfigBase {
+
+    @Setting(value = "bans", comment = "Controls which item/block/entity id's are banned globally from all events. " 
+            + "\nNote: Id's support wildcards '?' and '*' by using Apache's wildcard matcher." 
+            + "\nThe wildcard '?' represents a single character."
+            + "\nThe wildcard '*' represents zero or more characters."
+            + "\nFor more information on usage, see https://commons.apache.org/proper/commons-io/javadocs/api-2.5/org/apache/commons/io/FilenameUtils.html#wildcardMatch(java.lang.String,%20java.lang.String)")
+    public BanCategory bans = new BanCategory();
+
+    @Setting(value = "blacklist", comment = "Controls which item/block/entity id's are blacklisted from events either on a per-flag basis or globally. " 
+            + "\nNote: Id's support wildcards '?' and '*' by using Apache's wildcard matcher." 
+            + "\nThe wildcard '?' represents a single character."
+            + "\nThe wildcard '*' represents zero or more characters."
+            + "\nFor more information on usage, see https://commons.apache.org/proper/commons-io/javadocs/api-2.5/org/apache/commons/io/FilenameUtils.html#wildcardMatch(java.lang.String,%20java.lang.String)")
+    public BlacklistCategory blacklist = new BlacklistCategory();
+
+    @Setting
+    public ClaimCategory claim = new ClaimCategory();
+
+    @Setting
+    public OptionCategory options = new OptionCategory();
+
+    @Setting
+    public GeneralCategory general = new GeneralCategory();
+
+    @Setting
+    public PvpCategory pvp = new PvpCategory();
+
+    @Setting
+    public TownCategory town = new TownCategory();
+
+    @Setting
+    public VisualCategory visual = new VisualCategory();
+}
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/configuration/type/GlobalConfig.java b/sponge/src/main/java/com/griefdefender/configuration/type/GlobalConfig.java
new file mode 100644
index 0000000..038627c
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/configuration/type/GlobalConfig.java
@@ -0,0 +1,85 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.configuration.type;
+
+import com.griefdefender.configuration.category.CustomFlagGroupDefinitionCategory;
+import com.griefdefender.configuration.category.DefaultPermissionCategory;
+import com.griefdefender.configuration.category.EconomyCategory;
+import com.griefdefender.configuration.category.MessageCategory;
+import com.griefdefender.configuration.category.MigratorCategory;
+import com.griefdefender.configuration.category.ModuleCategory;
+import com.griefdefender.configuration.category.PlayerDataCategory;
+import com.griefdefender.configuration.category.ThreadCategory;
+import com.griefdefender.configuration.category.TownCategory;
+import ninja.leaping.configurate.objectmapping.Setting;
+
+public class GlobalConfig extends ConfigBase {
+
+    @Setting(value = "custom-flags", comment = "Used to define a group of custom flags for players/admins."
+            + "\nEach group defined will be displayed in the flag GUI for users."
+            + "\nGroups can have the following settings : "
+            + "\n    enabled=<true|false>: Whether the group is enabled."
+            + "\n    admin-group=<true|false>: Whether this group is considered for admin use only."
+            + "\n    hover=<text>: The hover text to be displayed when hovering over group name in GUI."
+            + "\n    title=<text>: The title text to be used for TAB display."
+            + "\n    value=<true|false>: This is used to set a default value for the flag definition. It is only used in conjunction with 'override=<type>, default=<type> settings."
+            + "\n    contexts=[\"key=value\"]: A list of optional definition contexts that will be applied to all permissions."
+            + "\nNote: This is primary used with 'default' and 'override' contexts. Ex. contexts=[\"default=global\"]"
+            + "\nEach group will have an associated permission in order to be viewable."
+            + "\nThe 'user' group will use the permission : 'griefdefender.custom.flag.group.user'"
+            + "\nThe 'admin' group will use the permission : 'griefdefender.custom.flag.group.admin'"
+            + "\nWithin each group, you can define flag definitions."
+            + "\nEach flag definition must be defined in the following format:"
+                    + "\nenabled: Controls whether the definition is enabled. Accepts a value of 'true' or 'false'"
+                    + "\ndescription: The flag description to display on hover. Uses the legacy text format."
+                    + "\npermissions: The list of permissions to link to definition. Each permission accepts the following contexts :"
+                    + "\n    flag=<linked-flag>: This context is used to link the permission to a GD specific flag. Ex. 'flag=block-break' would link permission to GD's block-break flag"
+                    + "\n    source=<id>: This context is used to specify a source id such as 'minecraft:creeper'."
+                    + "\n    target=<id>: This context is used to specify a target id such as 'minecraft:chest'."
+                    + "\n    state=<properties>: This context is used to specify a blockstate property such as 'state=lit:true'."
+                    + "\nNote: Required if no source or target context is specified, the permission will default to ALL."
+                    + "\nNote: Available contexts are : flag, source, target, state, used_item, item_name"
+                    + "\nThese contexts may change, See https://github.com/bloodmc/GriefDefender/wiki for latest information.")
+    public CustomFlagGroupDefinitionCategory customFlags = new CustomFlagGroupDefinitionCategory();
+    @Setting
+    public EconomyCategory economy = new EconomyCategory();
+    @Setting
+    public PlayerDataCategory playerdata = new PlayerDataCategory();
+    @Setting
+    public MessageCategory message = new MessageCategory();
+    @Setting(comment = 
+            "List of migrators that convert old or other protection data into the current GD claim data format." + 
+            "\nNote: It is recommended to backup data before using.")
+    public MigratorCategory migrator = new MigratorCategory();
+    @Setting(value = "modules")
+    public ModuleCategory modules = new ModuleCategory();
+    @Setting(value = "default-permissions")
+    public DefaultPermissionCategory permissionCategory = new DefaultPermissionCategory();
+    @Setting
+    public ThreadCategory thread = new ThreadCategory();
+
+    @Setting
+    public TownCategory town = new TownCategory();
+}
diff --git a/sponge/src/main/java/com/griefdefender/economy/GDBankTransaction.java b/sponge/src/main/java/com/griefdefender/economy/GDBankTransaction.java
new file mode 100644
index 0000000..7b98b5b
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/economy/GDBankTransaction.java
@@ -0,0 +1,125 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.economy;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.economy.BankTransaction;
+import com.griefdefender.api.economy.BankTransactionType;
+
+import java.time.Instant;
+import java.util.Optional;
+import java.util.UUID;
+
+public class GDBankTransaction implements BankTransaction {
+
+    public final BankTransactionType type;
+    public final UUID source;
+    public final Instant timestamp;
+    public final double amount;
+
+    public GDBankTransaction(Claim claim, BankTransactionType type, Instant timestamp, double amount) {
+        this(type, timestamp, amount);
+    }
+
+    public GDBankTransaction(BankTransactionType type, UUID source, Instant timestamp, double amount) {
+        this.type = type;
+        this.timestamp = timestamp;
+        this.amount = amount;
+        this.source = source;
+    }
+
+    public GDBankTransaction(BankTransactionType type, Instant timestamp, double amount) {
+        this.type = type;
+        this.timestamp = timestamp;
+        this.amount = amount;
+        this.source = null;
+    }
+
+    @Override
+    public BankTransactionType getType() {
+        return this.type;
+    }
+
+    @Override
+    public Optional<UUID> getSource() {
+        return Optional.ofNullable(this.source);
+    }
+
+    @Override
+    public Instant getTimestamp() {
+        return this.timestamp;
+    }
+
+    @Override
+    public Double getAmount() {
+        return this.amount;
+    }
+
+    public static class BankTransactionBuilder implements Builder {
+
+        private UUID source;
+        private double amount;
+        private BankTransactionType type;
+
+        @Override
+        public Builder type(BankTransactionType type) {
+            this.type = type;
+            return this;
+        }
+
+        @Override
+        public Builder source(UUID source) {
+            this.source = source;
+            return this;
+        }
+
+        @Override
+        public Builder amount(double amount) {
+            this.amount = amount;
+            return this;
+        }
+
+        @Override
+        public Builder reset() {
+            this.source = null;
+            this.amount = 0;
+            this.type = null;
+            return this;
+        }
+
+        @Override
+        public BankTransaction build() {
+            checkNotNull(this.type);
+            checkNotNull(this.amount);
+            if (this.source != null) {
+                return new GDBankTransaction(this.type, this.source, Instant.now(), this.amount); 
+            }
+            return new GDBankTransaction(this.type, Instant.now(), this.amount); 
+        }
+        
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/event/GDAttackPlayerEvent.java b/sponge/src/main/java/com/griefdefender/event/GDAttackPlayerEvent.java
new file mode 100644
index 0000000..789daea
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/event/GDAttackPlayerEvent.java
@@ -0,0 +1,45 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.event;
+
+import com.griefdefender.api.User;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.event.AttackPlayerEvent;
+
+import java.util.UUID;
+
+public class GDAttackPlayerEvent extends GDClaimEvent implements AttackPlayerEvent {
+
+    private User user;
+
+    public GDAttackPlayerEvent(Claim claim, User user) {
+        super(claim);
+    }
+
+    @Override
+    public User getUser() {
+        return this.user;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/event/GDBorderClaimEvent.java b/sponge/src/main/java/com/griefdefender/event/GDBorderClaimEvent.java
new file mode 100644
index 0000000..000f351
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/event/GDBorderClaimEvent.java
@@ -0,0 +1,125 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.event;
+
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.ChatType;
+import com.griefdefender.api.ChatTypes;
+import com.griefdefender.api.User;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.event.BorderClaimEvent;
+import com.griefdefender.cache.PermissionHolderCache;
+
+import net.kyori.text.Component;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.spongepowered.api.entity.Entity;
+import org.spongepowered.api.entity.living.player.Player;
+
+import java.util.Optional;
+import java.util.UUID;
+
+public class GDBorderClaimEvent extends GDClaimEvent implements BorderClaimEvent {
+
+    private final User user;
+    private final Entity entity;
+    private final UUID uniqueId;
+    private final Claim exitClaim;
+    private Component enterMessage;
+    private Component exitMessage;
+    private ChatType enterChatType = ChatTypes.CHAT;
+    private ChatType exitChatType = ChatTypes.CHAT;
+
+    public GDBorderClaimEvent(Entity entity, Claim exit, Claim enter) {
+        super(enter);
+        this.user = entity instanceof Player ? PermissionHolderCache.getInstance().getOrCreateUser((Player) entity) : null;
+        this.entity = entity;
+        this.uniqueId = entity.getUniqueId();
+        this.exitClaim = exit;
+        final int defaultChatType = GriefDefenderPlugin.getGlobalConfig().getConfig().message.enterExitChatType;
+        if (defaultChatType == 1) {
+            this.enterChatType = ChatTypes.ACTION_BAR;
+            this.exitChatType = ChatTypes.ACTION_BAR;
+        }
+    }
+
+    @Override
+    public Claim getExitClaim() {
+        return this.exitClaim;
+    }
+
+    public Entity getEntity() {
+        return this.entity;
+    }
+
+    @Override
+    public UUID getEntityUniqueId() {
+        return this.uniqueId;
+    }
+
+    @Override
+    public Optional<User> getUser() {
+        return Optional.ofNullable(this.user);
+    }
+
+    @Override
+    public Optional<Component> getEnterMessage() {
+        if (this.enterMessage == null) {
+            return this.getClaim().getData().getGreeting();
+        }
+
+        return Optional.of(this.enterMessage);
+    }
+
+    @Override
+    public Optional<Component> getExitMessage() {
+        if (this.exitMessage == null) {
+            return this.exitClaim.getData().getFarewell();
+        }
+
+        return Optional.of(this.exitMessage);
+    }
+
+    @Override
+    public void setEnterMessage(@Nullable Component message, ChatType chatType) {
+        this.enterMessage = message;
+        this.enterChatType = chatType;
+    }
+
+    @Override
+    public void setExitMessage(@Nullable Component message, ChatType chatType) {
+        this.exitMessage = message;
+        this.exitChatType = chatType;
+    }
+
+    @Override
+    public ChatType getExitMessageChatType() {
+        return exitChatType;
+    }
+
+    @Override
+    public ChatType getEnterMessageChatType() {
+        return enterChatType;
+    }
+}
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/event/GDCauseStackManager.java b/sponge/src/main/java/com/griefdefender/event/GDCauseStackManager.java
new file mode 100644
index 0000000..f84f4be
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/event/GDCauseStackManager.java
@@ -0,0 +1,97 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.event;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.collect.Queues;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.event.EventCause;
+import com.griefdefender.cache.PermissionHolderCache;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.entity.living.player.User;
+
+import java.util.Deque;
+
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+public final class GDCauseStackManager {
+
+    private int tick_stored;
+
+    private static GDCauseStackManager instance;
+
+    private final Deque<Object> cause = Queues.newArrayDeque();
+
+    @Nullable private EventCause cached_cause;
+
+    public EventCause getCurrentCause() {
+        if (Sponge.getServer().getRunningTimeTicks() != tick_stored) {
+            this.cached_cause = null;
+            this.cause.clear();
+        }
+        if (this.cached_cause == null) {
+            if (this.cause.isEmpty()) {
+                this.cached_cause = EventCause.of(GriefDefenderPlugin.getInstance());
+            } else {
+                this.cached_cause = EventCause.of(this.cause);
+            }
+        }
+        return this.cached_cause;
+    }
+
+    public GDCauseStackManager pushCause(Object obj) {
+        checkNotNull(obj, "obj");
+        if (obj instanceof User) {
+            obj = PermissionHolderCache.getInstance().getOrCreateUser((User) obj);
+        }
+        if (tick_stored == Sponge.getServer().getRunningTimeTicks()) {
+            this.cause.push(obj);
+            return this;
+        }
+
+        this.cached_cause = null;
+        this.cause.push(obj);
+        tick_stored = Sponge.getServer().getRunningTimeTicks();
+        return this;
+    }
+
+    public Object popCause() {
+        this.cached_cause = null;
+        return this.cause.pop();
+    }
+
+    public Object peekCause() {
+        return this.cause.peek();
+    }
+
+    public static GDCauseStackManager getInstance() {
+        return instance;
+    }
+
+    static {
+        instance = new GDCauseStackManager();
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/event/GDChangeClaimEvent.java b/sponge/src/main/java/com/griefdefender/event/GDChangeClaimEvent.java
new file mode 100644
index 0000000..cefcd07
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/event/GDChangeClaimEvent.java
@@ -0,0 +1,79 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.event;
+
+import com.flowpowered.math.vector.Vector3i;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.ClaimType;
+import com.griefdefender.api.event.ChangeClaimEvent;
+import org.spongepowered.api.world.Location;
+import org.spongepowered.api.world.World;
+
+public class GDChangeClaimEvent extends GDClaimEvent implements ChangeClaimEvent {
+
+    public GDChangeClaimEvent(Claim claim) {
+        super(claim);
+    }
+
+    public static class Type extends GDChangeClaimEvent implements ChangeClaimEvent.Type {
+        private final ClaimType originalType;
+        private final ClaimType newType;
+    
+        public Type(Claim claim, ClaimType newType) {
+            super(claim);
+            this.originalType = claim.getType();
+            this.newType = newType;
+        }
+    
+        @Override
+        public ClaimType getOriginalType() {
+            return originalType;
+        }
+    
+        @Override
+        public ClaimType getType() {
+            return newType;
+        }
+    }
+
+    public static class Resize extends GDChangeClaimEvent implements ChangeClaimEvent.Resize {
+        private Vector3i startCorner;
+        private Vector3i endCorner;
+
+        public Resize(Claim claim, Location<World> startCorner, Location<World> endCorner) {
+            super(claim);
+        }
+
+        @Override
+        public Vector3i getStartCorner() {
+            return this.startCorner;
+        }
+
+        @Override
+        public Vector3i getEndCorner() {
+            return this.endCorner;
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/event/GDClaimEvent.java b/sponge/src/main/java/com/griefdefender/event/GDClaimEvent.java
new file mode 100644
index 0000000..08cf739
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/event/GDClaimEvent.java
@@ -0,0 +1,82 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.event;
+
+import com.google.common.collect.ImmutableList;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.event.ClaimEvent;
+import com.griefdefender.api.event.EventCause;
+import net.kyori.text.Component;
+import org.spongepowered.api.Sponge;
+
+import java.util.List;
+import java.util.Optional;
+
+public class GDClaimEvent implements ClaimEvent {
+
+    private List<Claim> claims;
+    private Component message;
+    private boolean isCancelled = false;
+    private final EventCause cause;
+
+    public GDClaimEvent(Claim claim) {
+        this.claims = ImmutableList.of(claim);
+        this.cause = EventCause.of(Sponge.getCauseStackManager().getCurrentCause().all());
+    }
+
+    public GDClaimEvent(List<Claim> claims) {
+        this.claims = ImmutableList.copyOf(claims);
+        this.cause = EventCause.of(Sponge.getCauseStackManager().getCurrentCause().all());
+    }
+
+    @Override
+    public List<Claim> getClaims() {
+        return this.claims;
+    }
+
+    @Override
+    public void setMessage(Component message) {
+        this.message = message;
+    }
+
+    @Override
+    public Optional<Component> getMessage() {
+        return Optional.ofNullable(this.message);
+    }
+
+    public boolean cancelled() {
+        return this.isCancelled;
+    }
+
+    public void cancelled(boolean cancel) {
+        this.isCancelled = cancel;
+    }
+
+    @Override
+    public EventCause getCause() {
+        return this.cause;
+    }
+
+}
diff --git a/sponge/src/main/java/com/griefdefender/event/GDCreateClaimEvent.java b/sponge/src/main/java/com/griefdefender/event/GDCreateClaimEvent.java
new file mode 100644
index 0000000..c6dd960
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/event/GDCreateClaimEvent.java
@@ -0,0 +1,49 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.event;
+
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.event.CreateClaimEvent;
+
+public class GDCreateClaimEvent extends GDClaimEvent implements CreateClaimEvent {
+
+    public GDCreateClaimEvent(Claim claim) {
+        super(claim);
+    }
+
+    public static class Pre extends GDCreateClaimEvent {
+
+        public Pre(Claim claim) {
+            super(claim);
+        }
+    }
+
+    public static class Post extends GDCreateClaimEvent {
+
+        public Post(Claim claim) {
+            super(claim);
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/event/GDFlagPermissionEvent.java b/sponge/src/main/java/com/griefdefender/event/GDFlagPermissionEvent.java
new file mode 100644
index 0000000..9ff71b4
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/event/GDFlagPermissionEvent.java
@@ -0,0 +1,82 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.event;
+
+import com.griefdefender.api.Subject;
+import com.griefdefender.api.Tristate;
+import com.griefdefender.api.event.FlagPermissionEvent;
+import com.griefdefender.api.permission.Context;
+import com.griefdefender.api.permission.flag.Flag;
+
+public class GDFlagPermissionEvent extends GDPermissionEvent implements FlagPermissionEvent {
+
+    public GDFlagPermissionEvent(Subject subject) {
+        super(subject);
+    }
+
+    public GDFlagPermissionEvent(Subject subject, java.util.Set<Context> contexts) {
+        super(subject, contexts);
+    }
+
+    public static class ClearAll extends GDFlagPermissionEvent implements FlagPermissionEvent.ClearAll {
+
+        public ClearAll(Subject subject) {
+            super(subject);
+        }
+
+        public ClearAll(Subject subject, java.util.Set<Context> contexts) {
+            super(subject, contexts);
+        }
+    }
+
+    public static class Clear extends GDFlagPermissionEvent implements FlagPermissionEvent.Clear {
+
+        public Clear(Subject subject, java.util.Set<Context> contexts) {
+            super(subject, contexts);
+        }
+    }
+
+    public static class Set extends GDFlagPermissionEvent implements FlagPermissionEvent.Set {
+
+        private final Flag flag;
+        private final Tristate value;
+
+        public Set(Subject subject, Flag flag, Tristate value, java.util.Set<Context> contexts) {
+            super(subject, contexts);
+            this.flag = flag;
+            this.value = value;
+        }
+
+        @Override
+        public Flag getFlag() {
+            return this.flag;
+        }
+
+        @Override
+        public Tristate getValue() {
+            return this.value;
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/event/GDGroupTrustClaimEvent.java b/sponge/src/main/java/com/griefdefender/event/GDGroupTrustClaimEvent.java
new file mode 100644
index 0000000..74d6e22
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/event/GDGroupTrustClaimEvent.java
@@ -0,0 +1,71 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.event;
+
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.TrustType;
+import com.griefdefender.api.event.GroupTrustClaimEvent;
+
+import java.util.List;
+
+public class GDGroupTrustClaimEvent extends GDTrustClaimEvent implements GroupTrustClaimEvent {
+
+    private List<String> groups;
+
+    public GDGroupTrustClaimEvent(Claim claim, List<String> groups, TrustType trustType) {
+        super(claim, trustType);
+        this.groups = groups;
+    }
+
+    public GDGroupTrustClaimEvent(List<Claim> claims, List<String> groups, TrustType trustType) {
+        super(claims, trustType);
+        this.groups = groups;
+    }
+
+    @Override
+    public List<String> getGroups() {
+        return this.groups;
+    }
+
+    public static class Add extends GDGroupTrustClaimEvent implements GroupTrustClaimEvent.Add {
+        public Add(List<Claim> claims, List<String> groups, TrustType trustType) {
+            super(claims, groups, trustType);
+        }
+
+        public Add(Claim claim, List<String> groups, TrustType trustType) {
+            super(claim, groups, trustType);
+        }
+    }
+
+    public static class Remove extends GDGroupTrustClaimEvent implements GroupTrustClaimEvent.Remove {
+        public Remove(List<Claim> claims, List<String> groups, TrustType trustType) {
+            super(claims, groups, trustType);
+        }
+
+        public Remove(Claim claim, List<String> groups, TrustType trustType) {
+            super(claim, groups, trustType);
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/event/GDLoadClaimEvent.java b/sponge/src/main/java/com/griefdefender/event/GDLoadClaimEvent.java
new file mode 100644
index 0000000..87ddb1b
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/event/GDLoadClaimEvent.java
@@ -0,0 +1,49 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.event;
+
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.event.LoadClaimEvent;
+
+public class GDLoadClaimEvent extends GDClaimEvent implements LoadClaimEvent {
+
+    public GDLoadClaimEvent(Claim claim) {
+        super(claim);
+    }
+
+    public static class Pre extends GDLoadClaimEvent {
+
+        public Pre(Claim claim) {
+            super(claim);
+        }
+    }
+
+    public static class Post extends GDLoadClaimEvent {
+
+        public Post(Claim claim) {
+            super(claim);
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/event/GDOptionEvent.java b/sponge/src/main/java/com/griefdefender/event/GDOptionEvent.java
new file mode 100644
index 0000000..4e5d59a
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/event/GDOptionEvent.java
@@ -0,0 +1,73 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.event;
+
+import com.griefdefender.api.event.OptionPermissionEvent;
+import com.griefdefender.api.permission.Context;
+import com.griefdefender.api.permission.option.Option;
+import com.griefdefender.permission.GDPermissionHolder;
+
+public class GDOptionEvent extends GDPermissionEvent implements OptionPermissionEvent {
+
+    public GDOptionEvent(GDPermissionHolder subject, java.util.Set<Context> contexts) {
+        super(subject, contexts);
+    }
+
+    public static class ClearAll extends GDOptionEvent implements OptionPermissionEvent.ClearAll {
+
+        public ClearAll(GDPermissionHolder subject, java.util.Set<Context> contexts) {
+            super(subject, contexts);
+        }
+    }
+
+    public static class Clear extends GDOptionEvent implements OptionPermissionEvent.Clear {
+
+        public Clear(GDPermissionHolder subject, java.util.Set<Context> contexts) {
+            super(subject, contexts);
+        }
+    }
+
+    public static class Set extends GDOptionEvent implements OptionPermissionEvent.Set {
+
+        private final Option option;
+        private final String value;
+
+        public Set(GDPermissionHolder subject, Option option, String value, java.util.Set<Context> contexts) {
+            super(subject, contexts);
+            this.option = option;
+            this.value = value;
+        }
+
+        @Override
+        public Option getOption() {
+            return this.option;
+        }
+
+        @Override
+        public String getValue() {
+            return this.value;
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/event/GDPermissionEvent.java b/sponge/src/main/java/com/griefdefender/event/GDPermissionEvent.java
new file mode 100644
index 0000000..fc8cb5d
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/event/GDPermissionEvent.java
@@ -0,0 +1,91 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.event;
+
+import java.util.HashSet;
+import java.util.Optional;
+import java.util.Set;
+
+import com.griefdefender.api.Subject;
+import com.griefdefender.api.event.EventCause;
+import com.griefdefender.api.event.PermissionEvent;
+import com.griefdefender.api.permission.Context;
+
+import net.kyori.text.Component;
+
+public class GDPermissionEvent implements PermissionEvent {
+
+    private final Subject subject;
+    private Set<Context> contexts;
+    private Component message;
+    private boolean isCancelled = false;
+
+    public GDPermissionEvent(Subject subject) {
+        this.subject = subject;
+    }
+
+    public GDPermissionEvent(Subject subject, Set<Context> contexts) {
+        this.subject = subject;
+        this.contexts = contexts;
+    }
+
+    @Override
+    public Subject getSubject() {
+        return this.subject;
+    }
+
+    @Override
+    public Set<Context> getContexts() {
+        if (this.contexts == null) {
+            this.contexts = new HashSet<>();
+        }
+        return this.contexts;
+    }
+
+    @Override
+    public void setMessage(Component message) {
+        this.message = message;
+    }
+
+    @Override
+    public Optional<Component> getMessage() {
+        return Optional.ofNullable(this.message);
+    }
+
+    @Override
+    public boolean cancelled() {
+        return this.isCancelled;
+    }
+
+    @Override
+    public void cancelled(boolean cancelled) {
+        this.isCancelled = cancelled;
+    }
+
+    @Override
+    public EventCause getCause() {
+        return GDCauseStackManager.getInstance().getCurrentCause();
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/event/GDRemoveClaimEvent.java b/sponge/src/main/java/com/griefdefender/event/GDRemoveClaimEvent.java
new file mode 100644
index 0000000..6b7711a
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/event/GDRemoveClaimEvent.java
@@ -0,0 +1,83 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.event;
+
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.event.RemoveClaimEvent;
+import com.griefdefender.configuration.category.ClaimCategory;
+
+import java.util.List;
+
+public class GDRemoveClaimEvent extends GDClaimEvent implements RemoveClaimEvent {
+
+    private boolean shouldRestore;
+
+    public GDRemoveClaimEvent(Claim claim) {
+        super(claim);
+        final ClaimCategory claimCategory = GriefDefenderPlugin.getActiveConfig(claim.getWorldUniqueId()).getConfig().claim;
+        if (claimCategory.claimAutoNatureRestore || claimCategory.claimAutoSchematicRestore) {
+            shouldRestore = true;
+        } else {
+            shouldRestore = false;
+        }
+    }
+
+    public GDRemoveClaimEvent(List<Claim> claims) {
+        super(claims);
+    }
+
+    public static class Abandon extends GDRemoveClaimEvent implements RemoveClaimEvent.Abandon {
+
+        public Abandon(Claim claim) {
+            super(claim);
+        }
+
+        public Abandon(List<Claim> claims) {
+            super(claims);
+        }
+    }
+
+    public static class Delete extends GDRemoveClaimEvent implements RemoveClaimEvent.Delete {
+
+        public Delete(Claim claim) {
+            super(claim);
+        }
+
+        public Delete(List<Claim> claims) {
+            super(claims);
+        }
+    }
+
+    @Override
+    public boolean isRestoring() {
+        return shouldRestore;
+    }
+
+    @Override
+    public void shouldRestore(boolean restore) {
+        this.shouldRestore = restore;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/event/GDSaveClaimEvent.java b/sponge/src/main/java/com/griefdefender/event/GDSaveClaimEvent.java
new file mode 100644
index 0000000..11751df
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/event/GDSaveClaimEvent.java
@@ -0,0 +1,49 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.event;
+
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.event.SaveClaimEvent;
+
+public class GDSaveClaimEvent extends GDClaimEvent implements SaveClaimEvent {
+
+    public GDSaveClaimEvent(Claim claim) {
+        super(claim);
+    }
+
+    public static class Pre extends GDSaveClaimEvent {
+
+        public Pre(Claim claim) {
+            super(claim);
+        }
+    }
+
+    public static class Post extends GDSaveClaimEvent {
+
+        public Post(Claim claim) {
+            super(claim);
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/event/GDTaxClaimEvent.java b/sponge/src/main/java/com/griefdefender/event/GDTaxClaimEvent.java
new file mode 100644
index 0000000..44ee9f4
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/event/GDTaxClaimEvent.java
@@ -0,0 +1,67 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.event;
+
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.event.TaxClaimEvent;
+
+public class GDTaxClaimEvent extends GDClaimEvent implements TaxClaimEvent {
+
+    private final double originalTaxRate;
+    private final double originalTaxAmount;
+    private double taxRate;
+
+    public GDTaxClaimEvent(Claim claim, double rate, double amount) {
+        super(claim);
+        this.originalTaxRate = rate;
+        this.originalTaxAmount = amount;
+        this.taxRate = rate;
+    }
+
+    @Override
+    public double getOriginalTaxRate() {
+        return this.originalTaxRate;
+    }
+
+    @Override
+    public double getOriginalTaxAmount() {
+        return this.originalTaxAmount;
+    }
+
+    @Override
+    public double getTaxRate() {
+        return this.taxRate;
+    }
+
+    @Override
+    public double getTaxAmount() {
+        return (this.getClaim().getClaimBlocks() / 256) * this.taxRate;
+    }
+
+    @Override
+    public void setTaxRate(double newTaxRate) {
+        this.taxRate = newTaxRate;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/event/GDTransferClaimEvent.java b/sponge/src/main/java/com/griefdefender/event/GDTransferClaimEvent.java
new file mode 100644
index 0000000..61c5d63
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/event/GDTransferClaimEvent.java
@@ -0,0 +1,57 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.event;
+
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.event.TransferClaimEvent;
+
+import java.util.UUID;
+
+public class GDTransferClaimEvent extends GDClaimEvent implements TransferClaimEvent {
+
+    private UUID originalOwner;
+    private UUID newOwner;
+
+    public GDTransferClaimEvent(Claim claim, UUID originalOwner, UUID newOwner) {
+        super(claim);
+        this.originalOwner = originalOwner;
+        this.newOwner = newOwner;
+    }
+
+    @Override
+    public UUID getOriginalOwner() {
+        return this.originalOwner;
+    }
+
+    @Override
+    public UUID getNewOwner() {
+        return this.newOwner;
+    }
+
+    @Override
+    public void setNewOwner(UUID newOwner) {
+        this.newOwner = newOwner;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/event/GDTrustClaimEvent.java b/sponge/src/main/java/com/griefdefender/event/GDTrustClaimEvent.java
new file mode 100644
index 0000000..6eb4896
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/event/GDTrustClaimEvent.java
@@ -0,0 +1,51 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.event;
+
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.TrustType;
+import com.griefdefender.api.event.TrustClaimEvent;
+
+import java.util.List;
+
+public class GDTrustClaimEvent extends GDClaimEvent implements TrustClaimEvent {
+
+    private TrustType trustType;
+
+    public GDTrustClaimEvent(Claim claim, TrustType trustType) {
+        super(claim);
+        this.trustType = trustType;
+    }
+
+    public GDTrustClaimEvent(List<Claim> claims, TrustType trustType) {
+        super(claims);
+        this.trustType = trustType;
+    }
+
+    @Override
+    public TrustType getTrustType() {
+        return this.trustType;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/event/GDUserTrustClaimEvent.java b/sponge/src/main/java/com/griefdefender/event/GDUserTrustClaimEvent.java
new file mode 100644
index 0000000..566c8b8
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/event/GDUserTrustClaimEvent.java
@@ -0,0 +1,72 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.event;
+
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.TrustType;
+import com.griefdefender.api.event.UserTrustClaimEvent;
+
+import java.util.List;
+import java.util.UUID;
+
+public class GDUserTrustClaimEvent extends GDTrustClaimEvent implements UserTrustClaimEvent {
+
+    private List<UUID> users;
+
+    public GDUserTrustClaimEvent(Claim claim, List<UUID> users, TrustType trustType) {
+        super(claim, trustType);
+        this.users = users;
+    }
+
+    public GDUserTrustClaimEvent(List<Claim> claims, List<UUID> users, TrustType trustType) {
+        super(claims, trustType);
+        this.users = users;
+    }
+
+    @Override
+    public List<UUID> getUsers() {
+        return this.users;
+    }
+
+    public static class Add extends GDUserTrustClaimEvent implements UserTrustClaimEvent.Add {
+        public Add(List<Claim> claims, List<UUID> users, TrustType trustType) {
+            super(claims, users, trustType);
+        }
+
+        public Add(Claim claim, List<UUID> users, TrustType trustType) {
+            super(claim, users, trustType);
+        }
+    }
+
+    public static class Remove extends GDUserTrustClaimEvent implements UserTrustClaimEvent.Remove {
+        public Remove(List<Claim> claims,List<UUID> users, TrustType trustType) {
+            super(claims, users, trustType);
+        }
+
+        public Remove(Claim claim, List<UUID> users, TrustType trustType) {
+            super(claim, users, trustType);
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/event/package-info.java b/sponge/src/main/java/com/griefdefender/event/package-info.java
new file mode 100644
index 0000000..7c09a8f
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/event/package-info.java
@@ -0,0 +1,25 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.event;
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/inject/GriefDefenderImplModule.java b/sponge/src/main/java/com/griefdefender/inject/GriefDefenderImplModule.java
new file mode 100644
index 0000000..2d5c586
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/inject/GriefDefenderImplModule.java
@@ -0,0 +1,62 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.inject;
+
+import com.google.inject.PrivateModule;
+import com.google.inject.binder.AnnotatedBindingBuilder;
+import com.griefdefender.GDCore;
+import com.griefdefender.GDEventManager;
+import com.griefdefender.GDVersion;
+import com.griefdefender.api.Core;
+import com.griefdefender.api.GriefDefender;
+import com.griefdefender.api.Registry;
+import com.griefdefender.api.Version;
+import com.griefdefender.api.event.EventManager;
+import com.griefdefender.api.permission.PermissionManager;
+import com.griefdefender.permission.GDPermissionManager;
+import com.griefdefender.registry.GDRegistry;
+
+import javax.annotation.OverridingMethodsMustInvokeSuper;
+
+public class GriefDefenderImplModule extends PrivateModule {
+
+    @Override
+    @OverridingMethodsMustInvokeSuper
+    protected void configure() {
+        this.bindAndExpose(Core.class).to(GDCore.class);
+        this.bindAndExpose(EventManager.class).to(GDEventManager.class);
+        this.bindAndExpose(PermissionManager.class).to(GDPermissionManager.class);
+        this.bindAndExpose(Registry.class).to(GDRegistry.class);
+        this.bindAndExpose(Version.class).to(GDVersion.class);
+
+        this.requestStaticInjection(GriefDefender.class);
+    }
+
+    protected <T> AnnotatedBindingBuilder<T> bindAndExpose(final Class<T> type) {
+        this.expose(type);
+        return this.bind(type);
+    }
+
+}
diff --git a/sponge/src/main/java/com/griefdefender/internal/EntityRemovalListener.java b/sponge/src/main/java/com/griefdefender/internal/EntityRemovalListener.java
new file mode 100644
index 0000000..c9e1d87
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/internal/EntityRemovalListener.java
@@ -0,0 +1,92 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.internal;
+
+import com.griefdefender.internal.util.BlockUtil;
+import net.minecraft.block.state.IBlockState;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.util.SoundCategory;
+import net.minecraft.util.SoundEvent;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.IWorldEventListener;
+import net.minecraft.world.World;
+
+public class EntityRemovalListener implements IWorldEventListener {
+
+    @Override
+    public void onEntityRemoved(Entity entityIn) {
+        BlockUtil.ENTITY_BLOCK_CACHE.remove(entityIn.getEntityId());
+    }
+
+    @Override
+    public void notifyBlockUpdate(World worldIn, BlockPos pos, IBlockState oldState, IBlockState newState, int flags) {
+    }
+
+    @Override
+    public void notifyLightSet(BlockPos pos) {
+    }
+
+    @Override
+    public void markBlockRangeForRenderUpdate(int x1, int y1, int z1, int x2, int y2, int z2) {
+    }
+
+    @Override
+    public void playSoundToAllNearExcept(EntityPlayer player, SoundEvent soundIn, SoundCategory category, double x, double y, double z, float volume,
+            float pitch) {
+    }
+
+    @Override
+    public void playRecord(SoundEvent soundIn, BlockPos pos) {
+    }
+
+    @Override
+    public void spawnParticle(int particleID, boolean ignoreRange, double xCoord, double yCoord, double zCoord, double xSpeed, double ySpeed,
+            double zSpeed, int... parameters) {
+    }
+
+    @Override
+    public void spawnParticle(int p_190570_1_, boolean p_190570_2_, boolean p_190570_3_, double p_190570_4_, double p_190570_6_, double p_190570_8_,
+            double p_190570_10_, double p_190570_12_, double p_190570_14_, int... p_190570_16_) {
+    }
+
+    @Override
+    public void onEntityAdded(Entity entityIn) {
+
+    }
+
+    @Override
+    public void broadcastSound(int soundID, BlockPos pos, int data) {
+    }
+
+    @Override
+    public void playEvent(EntityPlayer player, int type, BlockPos blockPosIn, int data) {
+    }
+
+    @Override
+    public void sendBlockBreakProgress(int breakerId, BlockPos pos, int progress) {
+    }
+
+}
diff --git a/sponge/src/main/java/com/griefdefender/internal/NbtDataHelper.java b/sponge/src/main/java/com/griefdefender/internal/NbtDataHelper.java
new file mode 100644
index 0000000..a3f63b1
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/internal/NbtDataHelper.java
@@ -0,0 +1,61 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.internal;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.nbt.NBTTagCompound;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.entity.living.player.User;
+import org.spongepowered.api.service.user.UserStorageService;
+
+import java.util.Optional;
+import java.util.UUID;
+
+public class NbtDataHelper {
+
+    public static final String FORGE_DATA = "ForgeData";
+    public static final String SPONGE_DATA = "SpongeData";
+    public static final String SPONGE_ENTITY_CREATOR = "Creator";
+
+    public static Optional<User> getOwnerOfEntity(net.minecraft.entity.Entity entity) {
+        NBTTagCompound nbt = new NBTTagCompound();
+        entity.writeToNBT(nbt);
+        if (nbt.hasKey(FORGE_DATA)) {
+            NBTTagCompound forgeNBT = nbt.getCompoundTag(FORGE_DATA);
+            if (forgeNBT.hasKey(SPONGE_DATA) && forgeNBT.getCompoundTag(SPONGE_DATA).hasKey(SPONGE_ENTITY_CREATOR)) {
+                NBTTagCompound creatorNBT = forgeNBT.getCompoundTag(SPONGE_DATA).getCompoundTag(SPONGE_ENTITY_CREATOR);
+                UUID uuid = new UUID(creatorNBT.getLong("uuid_most"), creatorNBT.getLong("uuid_least"));
+                // get player if online
+                EntityPlayer player = entity.world.getPlayerEntityByUUID(uuid);
+                if (player != null) {
+                    return Optional.of((User) player);
+                }
+                // player is not online, get user from storage if one exists
+                return Sponge.getGame().getServiceManager().provide(UserStorageService.class).get().get(uuid);
+            }
+        }
+        return Optional.empty();
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/internal/pagination/ActivePagination.java b/sponge/src/main/java/com/griefdefender/internal/pagination/ActivePagination.java
new file mode 100644
index 0000000..8f6677a
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/internal/pagination/ActivePagination.java
@@ -0,0 +1,218 @@
+/*
+ * This file is part of Sponge, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) SpongePowered <https://www.spongepowered.org>
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.internal.pagination;
+
+import com.griefdefender.command.CommandException;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import net.kyori.text.event.ClickEvent;
+import net.kyori.text.event.HoverEvent;
+import net.kyori.text.format.TextColor;
+import net.kyori.text.format.TextDecoration;
+import org.spongepowered.api.command.CommandSource;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.function.Supplier;
+
+import javax.annotation.Nullable;
+
+/**
+ * Holds logic for an active pagination that is occurring.
+ */
+public abstract class ActivePagination {
+
+    private static final Component SLASH_TEXT = TextComponent.of("/");
+    private static final Component DIVIDER_TEXT = TextComponent.of(" ");
+    private static final Component CONTINUATION_TEXT = TextComponent.of("...");
+    private final Supplier<Optional<CommandSource>> src;
+    private final UUID id = UUID.randomUUID();
+    private final Component nextPageText;
+    private final Component prevPageText;
+    @Nullable
+    private final Component title;
+    @Nullable
+    private final Component header;
+    @Nullable
+    private final Component footer;
+    private int currentPage;
+    private final int maxContentLinesPerPage;
+    protected final GDPaginationCalculator calc;
+    private final Component padding;
+
+    public ActivePagination(Supplier<Optional<CommandSource>> src, GDPaginationCalculator calc, @Nullable Component title,
+            @Nullable Component  header, @Nullable Component  footer, Component  padding) {
+        this.src = src;
+        this.calc = calc;
+        this.title = title;
+        this.header = header;
+        this.footer = footer;
+        this.padding = padding;
+        this.nextPageText = TextComponent.builder("»")
+                .color(TextColor.BLUE)
+                .decoration(TextDecoration.UNDERLINED, true)
+                .clickEvent(ClickEvent.runCommand("/gd:pagination " + this.id.toString() + " next"))
+                .hoverEvent(HoverEvent.showText(TextComponent.of("/page next")))
+                .insertion("/page next")
+                .build();
+        this.prevPageText = TextComponent.builder("«")
+                .color(TextColor.BLUE)
+                .decoration(TextDecoration.UNDERLINED, true)
+                .clickEvent(ClickEvent.runCommand("/gd:pagination " + this.id.toString() + " prev"))
+                .hoverEvent(HoverEvent.showText(TextComponent.of("/page prev")))
+                .insertion("/page prev")
+                .build();
+        int maxContentLinesPerPage = calc.getLinesPerPage(src.get().get()) - 1;
+        if (title != null) {
+            maxContentLinesPerPage -= calc.getLines(title);
+        }
+        if (header != null) {
+            maxContentLinesPerPage -= calc.getLines(header);
+        }
+        if (footer != null) {
+            maxContentLinesPerPage -= calc.getLines(footer);
+        }
+        this.maxContentLinesPerPage = maxContentLinesPerPage;
+    }
+
+    public UUID getId() {
+        return this.id;
+    }
+
+    protected abstract Iterable<Component> getLines(int page) throws CommandException;
+
+    protected abstract boolean hasPrevious(int page);
+
+    protected abstract boolean hasNext(int page);
+
+    protected abstract int getTotalPages();
+
+    public void nextPage() throws CommandException {
+        specificPage(this.currentPage + 1);
+    }
+
+    public void previousPage() throws CommandException {
+        specificPage(this.currentPage - 1);
+    }
+
+    public void currentPage() throws CommandException {
+        specificPage(this.currentPage);
+    }
+
+    protected int getCurrentPage() {
+        return this.currentPage;
+    }
+
+    protected int getMaxContentLinesPerPage() {
+        return this.maxContentLinesPerPage;
+    }
+
+    public void specificPage(int page) throws CommandException {
+        CommandSource src = this.src.get()
+                .orElseThrow(() -> new CommandException(TextComponent.of(String.format("Source for pagination %s is no longer active!", getId()))));
+        this.currentPage = page;
+
+        List<Component> toSend = new ArrayList<>();
+        Component title = this.title;
+        if (title != null) {
+            toSend.add(title);
+        }
+        if (this.header != null) {
+            toSend.add(this.header);
+        }
+
+        for (Component line : getLines(page)) {
+            toSend.add(line);
+        }
+
+        Component footer = calculateFooter(page);
+        toSend.add(this.calc.center(footer, this.padding));
+        if (this.footer != null) {
+            toSend.add(this.footer);
+        }
+        for (Component component : toSend) {
+            TextAdapter.sendComponent(src, component);
+        }
+    }
+
+    protected Component calculateFooter(int currentPage) {
+        boolean hasPrevious = hasPrevious(currentPage);
+        boolean hasNext = hasNext(currentPage);
+
+        TextComponent.Builder ret = null;
+        if (hasPrevious) {
+            ret = TextComponent.builder("").append(this.prevPageText).append(DIVIDER_TEXT);
+        } else {
+            ret = TextComponent.builder("«").append(DIVIDER_TEXT);
+        }
+        boolean needsDiv = false;
+        int totalPages = getTotalPages();
+        if (totalPages > 1) {
+            ret.append(TextComponent.builder("")
+                    .hoverEvent(HoverEvent.showText(TextComponent.of("/page " + currentPage)))
+                    .clickEvent(ClickEvent.runCommand("/gd:pagination " + this.id + ' ' + currentPage))
+                    .insertion("/page " + currentPage)
+                    .append(TextComponent.of(String.valueOf(currentPage))).build())
+            .append(SLASH_TEXT)
+            .append(TextComponent.builder("")
+                    .hoverEvent(HoverEvent.showText(TextComponent.of("/page " + totalPages)))
+                    .clickEvent(ClickEvent.runCommand("/gd:pagination " + this.id + ' ' + totalPages))
+                    .insertion("/page " + totalPages)
+                    .append(TextComponent.of(String.valueOf(totalPages))).build());
+            needsDiv = true;
+        }
+        if (hasNext) {
+            if (needsDiv) {
+                ret.append(DIVIDER_TEXT);
+            }
+            ret.append(this.nextPageText);
+        } else {
+            if (needsDiv) {
+                ret.append(DIVIDER_TEXT);
+            }
+            ret.append(TextComponent.of("»"));
+        }
+
+        ret.color(this.padding.color());
+        if (this.title != null) {
+            ret.mergeDecorations(this.title);
+        }
+        return ret.build();
+    }
+
+    protected void padPage(final List<Component> currentPage, final int currentPageLines, final boolean addContinuation) {
+        final int maxContentLinesPerPage = getMaxContentLinesPerPage();
+        for (int i = currentPageLines; i < maxContentLinesPerPage; i++) {
+            if (addContinuation && i == maxContentLinesPerPage - 1) {
+                currentPage.add(CONTINUATION_TEXT);
+            } else {
+                currentPage.add(0, TextComponent.empty());
+            }
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/internal/pagination/GDPaginationBuilder.java b/sponge/src/main/java/com/griefdefender/internal/pagination/GDPaginationBuilder.java
new file mode 100644
index 0000000..833ae9e
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/internal/pagination/GDPaginationBuilder.java
@@ -0,0 +1,137 @@
+/*
+ * This file is part of Sponge, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) SpongePowered <https://www.spongepowered.org>
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.internal.pagination;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.collect.ImmutableList;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+
+import javax.annotation.Nullable;
+
+public class GDPaginationBuilder implements PaginationList.Builder {
+
+    @Nullable
+    private Iterable<Component> contents;
+    @Nullable
+    private Component title;
+    @Nullable
+    private Component header;
+    @Nullable
+    private Component footer;
+    private Component paginationSpacer = TextComponent.of("=");
+    private int linesPerPage = 20;
+
+    @Nullable
+    private PaginationList paginationList;
+
+    @Override
+    public PaginationList.Builder contents(Iterable<Component> contents) {
+        checkNotNull(contents, "The contents cannot be null!");
+        this.contents = contents;
+        this.paginationList = null;
+        return this;
+    }
+
+    @Override
+    public PaginationList.Builder contents(Component... contents) {
+        checkNotNull(contents, "The contents cannot be null!");
+        this.contents = ImmutableList.copyOf(contents);
+        this.paginationList = null;
+        return this;
+    }
+
+    @Override
+    public PaginationList.Builder title(@Nullable Component title) {
+        this.title = title;
+        this.paginationList = null;
+        return this;
+    }
+
+    @Override
+    public PaginationList.Builder header(@Nullable Component header) {
+        this.header = header;
+        this.paginationList = null;
+        return this;
+    }
+
+    @Override
+    public PaginationList.Builder footer(@Nullable Component footer) {
+        this.footer = footer;
+        this.paginationList = null;
+        return this;
+    }
+
+    @Override
+    public PaginationList.Builder padding(Component padding) {
+        checkNotNull(padding, "The padding cannot be null!");
+        this.paginationSpacer = padding;
+        this.paginationList = null;
+        return this;
+    }
+
+    @Override
+    public PaginationList.Builder linesPerPage(int linesPerPage) {
+        this.linesPerPage = linesPerPage;
+        return this;
+    }
+
+    @Override
+    public PaginationList build() {
+        checkState(this.contents != null, "The contents of the pagination list cannot be null!");
+
+        if (this.paginationList == null) {
+            this.paginationList = new GDPaginationList(this.contents, this.title, this.header, this.footer, this.paginationSpacer, this.linesPerPage);
+        }
+        return this.paginationList;
+    }
+
+    //@Override
+    public PaginationList.Builder from(PaginationList list) {
+        this.reset();
+        this.contents = list.getContents();
+        this.title = list.getTitle().orElse(null);
+        this.header = list.getHeader().orElse(null);
+        this.footer = list.getFooter().orElse(null);
+        this.paginationSpacer = list.getPadding();
+
+        this.paginationList = null;
+        return this;
+    }
+
+    //@Override
+    public PaginationList.Builder reset() {
+        this.contents = null;
+        this.title = null;
+        this.header = null;
+        this.footer = null;
+        this.paginationSpacer = TextComponent.of("=");
+
+        this.paginationList = null;
+        return this;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/internal/pagination/GDPaginationCalculator.java b/sponge/src/main/java/com/griefdefender/internal/pagination/GDPaginationCalculator.java
new file mode 100644
index 0000000..356fa02
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/internal/pagination/GDPaginationCalculator.java
@@ -0,0 +1,328 @@
+/*
+ * This file is part of Sponge, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) SpongePowered <https://www.spongepowered.org>
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.internal.pagination;
+
+import com.flowpowered.math.GenericMath;
+import com.google.common.annotations.VisibleForTesting;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.serializer.gson.GsonComponentSerializer;
+import net.minecraft.util.text.ITextComponent;
+import net.minecraft.util.text.TextComponentString;
+import net.minecraft.util.text.TextComponentTranslation;
+import ninja.leaping.configurate.ConfigurationNode;
+import ninja.leaping.configurate.commented.CommentedConfigurationNode;
+import ninja.leaping.configurate.hocon.HoconConfigurationLoader;
+import ninja.leaping.configurate.loader.ConfigurationLoader;
+import ninja.leaping.configurate.loader.HeaderMode;
+import org.spongepowered.api.command.CommandSource;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.PrimitiveIterator;
+
+/**
+ * Pagination calculator for players. Handles calculation of text widths,
+ * centering text, adding padding, adding spacing, and more.
+ */
+public class GDPaginationCalculator {
+
+    private static final String NON_UNICODE_CHARS;
+    private static final int[] NON_UNICODE_CHAR_WIDTHS;
+    private static final byte[] UNICODE_CHAR_WIDTHS;
+    private static final int LINE_WIDTH = 319;
+
+    private final int linesPerPage;
+
+    /**
+     * Constructs a new pagination calculator.
+     *
+     * @param linesPerPage The amount of lines per page there should be
+     */
+    public GDPaginationCalculator(int linesPerPage) {
+        this.linesPerPage = linesPerPage;
+    }
+
+    static {
+        ConfigurationLoader<CommentedConfigurationNode> loader = HoconConfigurationLoader.builder()
+                .setURL(GDPaginationCalculator.class.getResource("font-sizes.json"))
+                .setHeaderMode(HeaderMode.NONE)
+                .build();
+        try {
+            ConfigurationNode node = loader.load();
+            NON_UNICODE_CHARS = node.getNode("non-unicode").getString();
+            List<? extends ConfigurationNode> charWidths = node.getNode("char-widths").getChildrenList();
+            int[] nonUnicodeCharWidths = new int[charWidths.size()];
+            for (int i = 0; i < nonUnicodeCharWidths.length; ++i) {
+                nonUnicodeCharWidths[i] = charWidths.get(i).getInt();
+            }
+            NON_UNICODE_CHAR_WIDTHS = nonUnicodeCharWidths;
+
+            List<? extends ConfigurationNode> glyphWidths = node.getNode("glyph-widths").getChildrenList();
+            byte[] unicodeCharWidths = new byte[glyphWidths.size()];
+            for (int i = 0; i < unicodeCharWidths.length; ++i) {
+                unicodeCharWidths[i] = (byte) glyphWidths.get(i).getInt();
+            }
+            UNICODE_CHAR_WIDTHS = unicodeCharWidths;
+        } catch (IOException e) {
+            throw new ExceptionInInitializerError(e);
+        }
+    }
+
+    int getLinesPerPage(CommandSource source) {
+        return this.linesPerPage;
+    }
+
+    /**
+     * Gets the number of lines the specified text flows into.
+     *
+     * @param text The text to calculate the number of lines for
+     * @return The number of lines that this text flows into
+     */
+    int getLines(Component text) {
+        //TODO: this needs fixing as well.
+        return (int) Math.ceil((double) this.getWidth(text) / LINE_WIDTH);
+    }
+
+    /**
+     * Gets the width of a character with the specified code
+     * point, accounting for if its text is bold our not.
+     *
+     * @param codePoint The code point of the character
+     * @param isBold Whether or not the character is bold or not
+     * @return The width of the character at the code point
+     */
+    @VisibleForTesting
+    int getWidth(int codePoint, boolean isBold) {
+        int nonUnicodeIdx = NON_UNICODE_CHARS.indexOf(codePoint);
+        int width;
+        if (codePoint == 32) {
+            width = 4;
+        } else if (codePoint > 0 && nonUnicodeIdx != -1) {
+            width = NON_UNICODE_CHAR_WIDTHS[nonUnicodeIdx];
+        } else if (UNICODE_CHAR_WIDTHS[codePoint] != 0) {
+            //from 1.9 & 255 to avoid strange signed int math ruining things.
+            //https://bugs.mojang.com/browse/MC-7181
+            final int temp = UNICODE_CHAR_WIDTHS[codePoint] & 255;
+            // Split into high and low nibbles.
+            //bit digits
+            //87654321 >>> 4 = 00008765
+            int startColumn = temp >>> 4;
+            //87654321 & 00001111 = 00004321
+            int endColumn = temp & 15;
+
+            width = (endColumn + 1) - startColumn;
+            //Why does this scaling happen?
+            //I believe it makes unicode fonts skinnier to better match the character widths of the default Minecraft
+            // font however there is a int math vs float math bug in the Minecraft FontRenderer.
+            //The float math is adjusted for rendering, they attempt to do the same thing for calculating string widths
+            //using integer math, this has potential rounding errors, but we should copy it and use ints as well.
+            width = (width / 2) + 1;
+        } else {
+            width = 0;
+        }
+        //if bolded width gets 1 added.
+        if(isBold && width > 0) {
+            width = width + 1;
+        }
+
+        return width;
+    }
+
+    /**
+     * Calculates the width of a given text as the number of character
+     * pixels/columns the line takes up.
+     *
+     * @param text The text to get the width of
+     * @return The amount of character pixels/columns the text takes up
+     */
+    @VisibleForTesting
+    int getWidth(Component text) {
+        final ITextComponent internal = ITextComponent.Serializer.jsonToComponent(GsonComponentSerializer.INSTANCE.serialize(text));
+        Iterable<ITextComponent> children = new TextComponentIterable(internal, true);
+        int total = 0;
+
+        for(ITextComponent child : children) {
+            PrimitiveIterator.OfInt i_it;
+            if(child instanceof TextComponentString || child instanceof TextComponentTranslation) {
+                i_it = child.getUnformattedComponentText().codePoints().iterator();
+            } else {
+                continue;
+            }
+
+            boolean bold = child.getStyle().getBold();
+
+            Integer cp;
+            boolean newLine = false;
+            while(i_it.hasNext()){
+                cp = i_it.next();
+                if (cp == '\n') {
+                    // if the previous character is a '\n'
+                    if (newLine) {
+                        total += LINE_WIDTH;
+                    } else {
+                        total = ((int) Math.ceil((double) total / LINE_WIDTH)) * LINE_WIDTH;
+                        newLine = true;
+                    }
+                } else {
+                    int width = getWidth(cp, bold);
+                    total += width;
+                    newLine = false;
+                }
+            }
+        }
+
+        return total;
+    }
+
+    /**
+     * Centers a text within the middle of the chat box.
+     *
+     * <p>Generally used for titles and footers.</p>
+     *
+     * <p>To use no heading, just pass in a 0 width text for
+     * the first argument.</p>
+     *
+     * @param text The text to center
+     * @param padding A padding character with a width >1
+     * @return The centered text, or if too big, the original text
+     */
+    //TODO: Probably should completely rewrite this to not compute padding, but loop until the padding is done, unless
+    //we can get accurate computation of padding ahead of time.
+    Component center(Component text, Component padding) {
+        int inputLength = getWidth(text);
+        //Minecraft breaks lines when the next character would be > then LINE_WIDTH, this seems most graceful way to fail
+        if (inputLength >= LINE_WIDTH) {
+            return text;
+        }
+        final Component textWithSpaces = addSpaces(TextComponent.of(" "), text);
+
+        //Minecraft breaks lines when the next character would be > then LINE_WIDTH
+        boolean addSpaces = getWidth(textWithSpaces) <= LINE_WIDTH;
+
+        //TODO: suspect, why are we changing the style of the padding, they may want different styles on the padding.
+        Component styledPadding = withStyle(padding, text);
+        int paddingLength = getWidth(styledPadding);
+        final TextComponent.Builder output = TextComponent.builder("");
+
+        //Using 0 width unicode symbols as padding throws us into an unending loop, replace them with the default padding
+        if(paddingLength < 1) {
+            padding = TextComponent.of("=");
+            styledPadding = withColor(withStyle(padding, text), text);
+            paddingLength = getWidth(styledPadding);
+        }
+
+        //if we only need padding
+        if (inputLength == 0) {
+            addPadding(padding, output, GenericMath.floor((double) LINE_WIDTH / paddingLength));
+        } else {
+            if(addSpaces) {
+                text = textWithSpaces;
+                inputLength = getWidth(textWithSpaces);
+            }
+
+            int paddingNecessary = LINE_WIDTH - inputLength;
+
+            int paddingCount = GenericMath.floor(paddingNecessary / paddingLength);
+            //pick a halfway point
+            int beforePadding = GenericMath.floor(paddingCount / 2.0);
+            //Do not use ceil, this prevents floating point errors.
+            int afterPadding = paddingCount - beforePadding;
+
+            addPadding(styledPadding, output, beforePadding);
+            output.append(text);
+            addPadding(styledPadding, output, afterPadding);
+        }
+
+        return this.finalizeBuilder(text, output);
+    }
+
+    /**
+     * Gives the first text argument the style of the second.
+     *
+     * @param text The text to stylize
+     * @param styled The styled text
+     * @return The original text now stylized
+     */
+    private Component withStyle(Component text, Component styled) {
+        return ((TextComponent) text).toBuilder().mergeStyle(styled).build();
+    }
+
+    /**
+     * Gives the first text argument the color of the second.
+     *
+     * @param text The text to color
+     * @param colored The colored text
+     * @return The original text now colored
+     */
+    private Component withColor(Component text, Component colored) {
+        return ((TextComponent) text).toBuilder().color(colored.color()).build();
+    }
+
+    /**
+     * Finalizes the builder used in centering text.
+     *
+     * @param text The text to get the style from
+     * @param build The work in progress text builder
+     * @return The finalized, properly styled text.
+     */
+    private Component finalizeBuilder(Component text, TextComponent.Builder build) {
+        return build.mergeDecorations(text).build();
+    }
+
+    /**
+     * Adds spaces to both sides of the specified text.
+     *
+     * <p>Overrides all color and style with the
+     * text's color and style.</p>
+     *
+     * @param spaces The spaces to use
+     * @param text The text to add to
+     * @return The text with the added spaces
+     */
+    private Component addSpaces(Component spaces, Component text) {
+        return ((TextComponent) spaces).toBuilder()
+                .append(text)
+                .append(spaces)
+                .color(text.color())
+                .mergeDecorations(text)
+                .build();
+    }
+
+    /**
+     * Adds the specified padding text to a piece of text being built
+     * up to a certain amount specified by a count.
+     *
+     * @param padding The padding text to use
+     * @param build The work in progress text to add to
+     * @param count The amount of padding to add
+     */
+    private void addPadding(Component padding, TextComponent.Builder build, int count) {
+        if (count > 0) {
+            build.append(Collections.nCopies(count, padding));
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/internal/pagination/GDPaginationHolder.java b/sponge/src/main/java/com/griefdefender/internal/pagination/GDPaginationHolder.java
new file mode 100644
index 0000000..d0ec062
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/internal/pagination/GDPaginationHolder.java
@@ -0,0 +1,138 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.internal.pagination;
+
+import com.github.benmanes.caffeine.cache.Cache;
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.google.common.collect.MapMaker;
+import com.griefdefender.command.CommandException;
+import net.kyori.text.TextComponent;
+import net.kyori.text.format.TextColor;
+import org.spongepowered.api.command.CommandSource;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.text.channel.MessageReceiver;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.TimeUnit;
+
+import javax.annotation.Nullable;
+
+public class GDPaginationHolder {
+
+    private static final GDPaginationHolder INSTANCE = new GDPaginationHolder();
+
+    private final ConcurrentMap<MessageReceiver, SourcePaginations> activePaginations = new MapMaker().weakKeys().makeMap();
+
+    // We have a second active pagination system because of the way Players are handled by the server.
+    // As Players are recreated every time they die in game, just storing the player in a weak map will
+    // cause the player to be removed form the map upon death. Thus, player paginations get redirected
+    // through to this cache instead, which last for 10 minutes from last access.
+    private final Cache<UUID, SourcePaginations> playerActivePaginations = Caffeine.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES)
+            .build();
+
+    public ActivePagination getActivePagination(CommandSource src, String id) throws CommandException {
+        SourcePaginations paginations = GDPaginationHolder.getInstance().getPaginationState(src, false);
+        if (paginations == null) {
+            return null;
+        }
+
+        UUID pageId = null;
+        try {
+            pageId = UUID.fromString(id);
+        } catch (IllegalArgumentException ex) {
+            throw new CommandException(TextComponent.of("Input was not a valid UUID!", TextColor.RED));
+        }
+
+        ActivePagination pagination = paginations.get(pageId);
+        if (pagination == null) {
+            throw new CommandException(TextComponent.of("No pagination registered for id " + id.toString(), TextColor.RED));
+        }
+        return pagination;
+    }
+
+    @Nullable
+    SourcePaginations getPaginationState(MessageReceiver source, boolean create) {
+        if (source instanceof Player) {
+            return getPaginationStateForPlayer((Player) source, create);
+        }
+
+        return getPaginationStateForNonPlayer(source, create);
+    }
+
+    @Nullable
+    SourcePaginations getPaginationStateForNonPlayer(MessageReceiver source, boolean create) {
+        SourcePaginations ret = this.activePaginations.get(source);
+        if (ret == null && create) {
+            ret = new SourcePaginations();
+            SourcePaginations existing = this.activePaginations.putIfAbsent(source, ret);
+            if (existing != null) {
+                ret = existing;
+            }
+        }
+        return ret;
+    }
+
+    @Nullable
+    SourcePaginations getPaginationStateForPlayer(Player source, boolean create) {
+        return this.playerActivePaginations.get(source.getUniqueId(), k -> create ? new SourcePaginations() : null);
+    }
+
+    static class SourcePaginations {
+        private final Map<UUID, ActivePagination> paginations = new ConcurrentHashMap<>();
+        @Nullable private volatile UUID lastUuid;
+
+        @Nullable public ActivePagination get(UUID uuid) {
+            return this.paginations.get(uuid);
+        }
+
+        public void put(ActivePagination pagination) {
+            synchronized (this.paginations) {
+                this.paginations.put(pagination.getId(), pagination);
+                this.lastUuid = pagination.getId();
+            }
+        }
+
+        public Set<UUID> keys() {
+            return this.paginations.keySet();
+        }
+
+        @Nullable
+        public UUID getLastUuid() {
+            return this.lastUuid;
+        }
+    }
+
+    public static GDPaginationHolder getInstance() {
+        return INSTANCE;
+    }
+
+    public Cache<UUID, SourcePaginations> getActivePaginationCache() {
+        return this.playerActivePaginations;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/internal/pagination/GDPaginationList.java b/sponge/src/main/java/com/griefdefender/internal/pagination/GDPaginationList.java
new file mode 100644
index 0000000..a883f13
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/internal/pagination/GDPaginationList.java
@@ -0,0 +1,145 @@
+/*
+ * This file is part of Sponge, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) SpongePowered <https://www.spongepowered.org>
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.internal.pagination;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Maps;
+import com.griefdefender.command.CommandException;
+import net.kyori.text.Component;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.command.CommandSource;
+import org.spongepowered.api.command.source.ProxySource;
+import org.spongepowered.api.entity.living.player.Player;
+
+import java.lang.ref.WeakReference;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
+
+import javax.annotation.Nullable;
+
+public class GDPaginationList implements PaginationList {
+
+    private final Iterable<Component> contents;
+    private final Optional<Component> title;
+    private final Optional<Component> header;
+    private final Optional<Component> footer;
+    private final Component paginationSpacer;
+    private final int linesPerPage;
+
+    public GDPaginationList(Iterable<Component> contents, @Nullable Component title, @Nullable Component header,
+            @Nullable Component footer, Component paginationSpacer, int linesPerPage) {
+        this.contents = contents;
+        this.title = Optional.ofNullable(title);
+        this.header = Optional.ofNullable(header);
+        this.footer = Optional.ofNullable(footer);
+        this.paginationSpacer = paginationSpacer;
+        this.linesPerPage = linesPerPage;
+    }
+
+    @Override
+    public Iterable<Component> getContents() {
+        return this.contents;
+    }
+
+    @Override
+    public Optional<Component> getTitle() {
+        return this.title;
+    }
+
+    @Override
+    public Optional<Component> getHeader() {
+        return this.header;
+    }
+
+    @Override
+    public Optional<Component> getFooter() {
+        return this.footer;
+    }
+
+    @Override
+    public Component getPadding() {
+        return this.paginationSpacer;
+    }
+
+    @Override
+    public int getLinesPerPage() {
+        return this.linesPerPage;
+    }
+
+    @Override
+    public void sendTo(final CommandSource receiver, int page) {
+        checkNotNull(receiver, "The message receiver cannot be null!");
+
+        CommandSource realSource = receiver;
+        while (realSource instanceof ProxySource) {
+            realSource = ((ProxySource)realSource).getOriginalSource();
+        }
+        final GDPaginationCalculator calculator = new GDPaginationCalculator(this.linesPerPage);
+        Iterable<Map.Entry<Component, Integer>> counts = StreamSupport.stream(this.contents.spliterator(), false).map(input -> {
+            int lines = calculator.getLines(input);
+            return Maps.immutableEntry(input, lines);
+        }).collect(Collectors.toList());
+
+        Component title = this.title.orElse(null);
+        if (title != null) {
+            title = calculator.center(title, this.paginationSpacer);
+        }
+
+        // If the MessageReceiver is a Player, then upon death, they will become a different MessageReceiver object.
+        // Thus, we use a supplier to supply the player from the server, if required.
+        Supplier<Optional<CommandSource>> messageReceiverSupplier;
+        if (receiver instanceof Player) {
+            final UUID playerUuid = ((Player) receiver).getUniqueId();
+            messageReceiverSupplier = () -> Optional.ofNullable(Sponge.getServer().getPlayer(playerUuid).orElse(null)).map(x -> (Player) x);
+        } else {
+            WeakReference<CommandSource> srcReference = new WeakReference<>(receiver);
+            messageReceiverSupplier = () -> Optional.ofNullable(srcReference.get());
+        }
+
+        ActivePagination pagination;
+        if (this.contents instanceof List) { // If it started out as a list, it's probably reasonable to copy it to another list
+            pagination = new ListPagination(messageReceiverSupplier, calculator, ImmutableList.copyOf(counts), title, this.header.orElse(null),
+                    this.footer.orElse(null), this.paginationSpacer);
+        } else {
+            pagination = new IterablePagination(messageReceiverSupplier, calculator, counts, title, this.header.orElse(null),
+                    this.footer.orElse(null), this.paginationSpacer);
+        }
+
+        GDPaginationHolder.getInstance().getPaginationState(receiver, true).put(pagination);
+        try {
+            pagination.specificPage(page);
+        } catch (CommandException e) {
+            TextAdapter.sendComponent(receiver, e.getText());
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/internal/pagination/IterablePagination.java b/sponge/src/main/java/com/griefdefender/internal/pagination/IterablePagination.java
new file mode 100644
index 0000000..0de9ebb
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/internal/pagination/IterablePagination.java
@@ -0,0 +1,127 @@
+/*
+ * This file is part of Sponge, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) SpongePowered <https://www.spongepowered.org>
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.internal.pagination;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Iterators;
+import com.google.common.collect.Lists;
+import com.google.common.collect.PeekingIterator;
+import com.griefdefender.command.CommandException;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import org.spongepowered.api.command.CommandSource;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Supplier;
+
+import javax.annotation.Nullable;
+
+/**
+ * Pagination occurring for an iterable -- we don't know its size.
+ */
+class IterablePagination extends ActivePagination {
+
+    private final PeekingIterator<Map.Entry<Component, Integer>> countIterator;
+    private int lastPage;
+
+    public IterablePagination(Supplier<Optional<CommandSource>> src, GDPaginationCalculator calc, Iterable<Map.Entry<Component, Integer>> counts,
+            @Nullable Component title, @Nullable Component header, @Nullable Component footer, Component padding) {
+        super(src, calc, title, header, footer, padding);
+        this.countIterator = Iterators.peekingIterator(counts.iterator());
+    }
+
+    @Override
+    protected Iterable<Component> getLines(int page) throws CommandException {
+        if (!this.countIterator.hasNext()) {
+            throw new CommandException(TextComponent.of("You're already at the end of the pagination list iterator."));
+        }
+
+        if (page < 1) {
+            throw new CommandException(TextComponent.of(String.format("Page %s does not exist!", page)));
+        }
+
+        if (page <= this.lastPage) {
+            throw new CommandException(TextComponent.of("You cannot go to previous pages in an iterable pagination."));
+        } else if (page > this.lastPage + 1) {
+            getLines(page - 1);
+        }
+        this.lastPage = page;
+
+        if (getMaxContentLinesPerPage() <= 0) {
+            return Lists.newArrayList(Iterators.transform(this.countIterator, new Function<Map.Entry<Component, Integer>, Component>() {
+
+                @Nullable
+                @Override
+                public Component apply(Map.Entry<Component, Integer> input) {
+                    return input.getKey();
+                }
+            }));
+        }
+
+        List<Component> ret = new ArrayList<>(getMaxContentLinesPerPage());
+        int addedLines = 0;
+        while (addedLines <= getMaxContentLinesPerPage()) {
+            if (!this.countIterator.hasNext()) {
+                // Pad the last page, but only if it isn't the first.
+                if (page > 1) {
+                    padPage(ret, addedLines, false);
+                }
+                break;
+            }
+            if (addedLines + this.countIterator.peek().getValue() > getMaxContentLinesPerPage()) {
+                // Add the continuation marker, pad if required
+                padPage(ret, addedLines, true);
+                break;
+            }
+            Map.Entry<Component, Integer> ent = this.countIterator.next();
+            ret.add(ent.getKey());
+            addedLines += ent.getValue();
+        }
+        return ret;
+    }
+
+    @Override
+    protected boolean hasPrevious(int page) {
+        return false;
+    }
+
+    @Override
+    protected boolean hasNext(int page) {
+        return page == getCurrentPage() && this.countIterator.hasNext();
+    }
+
+    @Override
+    protected int getTotalPages() {
+        return -1;
+    }
+
+    @Override
+    public void previousPage() throws CommandException {
+        throw new CommandException(TextComponent.of("You cannot go to previous pages in an iterable pagination."));
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/internal/pagination/ListPagination.java b/sponge/src/main/java/com/griefdefender/internal/pagination/ListPagination.java
new file mode 100644
index 0000000..c75451f
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/internal/pagination/ListPagination.java
@@ -0,0 +1,107 @@
+/*
+ * This file is part of Sponge, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) SpongePowered <https://www.spongepowered.org>
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.internal.pagination;
+
+import com.google.common.collect.ImmutableList;
+import com.griefdefender.command.CommandException;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import org.spongepowered.api.command.CommandSource;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Supplier;
+
+import javax.annotation.Nullable;
+
+/**
+ * Pagination working with a list of values.
+ */
+class ListPagination extends ActivePagination {
+    private final List<List<Component>> pages;
+
+    public ListPagination(Supplier<Optional<CommandSource>> src, GDPaginationCalculator calc, List<Map.Entry<Component, Integer>> lines,
+            @Nullable Component title, @Nullable Component header, @Nullable Component footer, Component padding) {
+        super(src, calc, title, header, footer, padding);
+        List<List<Component>> pages = new ArrayList<>();
+        List<Component> currentPage = new ArrayList<>();
+        int currentPageLines = 0;
+
+        for (Map.Entry<Component, Integer> ent : lines) {
+            final boolean finiteLinesPerPage  = getMaxContentLinesPerPage() > 0;
+            final boolean willExceedPageLength = ent.getValue() + currentPageLines > getMaxContentLinesPerPage();
+            final boolean currentPageNotEmpty = currentPageLines != 0;
+            final boolean spillToNextPage = finiteLinesPerPage && willExceedPageLength && currentPageNotEmpty;
+            if (spillToNextPage) {
+                padPage(currentPage, currentPageLines, true);
+                currentPageLines = 0;
+                pages.add(currentPage);
+                currentPage = new ArrayList<>();
+            }
+            currentPageLines += ent.getValue();
+            currentPage.add(ent.getKey());
+        }
+        //last page is not yet committed
+        final boolean lastPageNotEmpty = currentPageLines > 0;
+        if (lastPageNotEmpty) {
+            if (!pages.isEmpty()) {
+                // Only pad if we have a previous page
+                padPage(currentPage, currentPageLines, false);
+            }
+            pages.add(currentPage);
+        }
+        this.pages = pages;
+    }
+
+    @Override
+    protected Iterable<Component> getLines(int page) throws CommandException {
+        final int size = this.pages.size();
+        if (size == 0) {
+            return ImmutableList.of();
+        } else if (page < 1) {
+            throw new CommandException(TextComponent.of(String.format("Page %s does not exist!", page)));
+        } else if (page > size) {
+            throw new CommandException(TextComponent.of(String.format("Page %s is greater than the max of %s!", page, size)));
+        }
+        return this.pages.get(page - 1);
+    }
+
+    @Override
+    protected boolean hasPrevious(int page) {
+        return page > 1;
+    }
+
+    @Override
+    protected boolean hasNext(int page) {
+        return page < this.pages.size();
+    }
+
+    @Override
+    protected int getTotalPages() {
+        return this.pages.size();
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/internal/pagination/PaginationList.java b/sponge/src/main/java/com/griefdefender/internal/pagination/PaginationList.java
new file mode 100644
index 0000000..b0624fa
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/internal/pagination/PaginationList.java
@@ -0,0 +1,274 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) SpongePowered <https://www.spongepowered.org>
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.internal.pagination;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import net.kyori.text.Component;
+import org.spongepowered.api.command.CommandSource;
+import org.spongepowered.api.text.channel.MessageReceiver;
+
+import java.util.List;
+import java.util.Optional;
+
+import javax.annotation.Nullable;
+
+/**
+ * Represents an immutable iterable of {@link Text}s, which can be sent to
+ * a {@link MessageReceiver}.
+ *
+ * <p>An instance of this class may be obtained using {@link Builder}.</p>
+ */
+public interface PaginationList {
+
+    /**
+     * Creates a new {@link Builder} to build a pagination list.
+     *
+     * @return The new builder
+     */
+    public static GDPaginationBuilder builder() {
+        return new GDPaginationBuilder();
+    }
+
+    /**
+     * Gets the contents of this pagination list.
+     *
+     * @return The contents of this pagination list
+     */
+    Iterable<Component> getContents();
+
+    /**
+     * Gets the title text to be used in the title bar of this pagination.
+     *
+     * @return The title text
+     */
+    Optional<Component> getTitle();
+
+    /**
+     * Gets the header to be displayed for this output on all pages after the
+     * title bar but before the contents, if available.
+     *
+     * <p>Header and footer will use this Text's style and color for
+     * formatting.</p>
+     *
+     * @return The header to be displayed
+     */
+    Optional<Component> getHeader();
+
+    /**
+     * Gets the footer to be displayed for this output on all pages after the
+     * contents and page navigation bar, if available.
+     *
+     * @return The footer
+     */
+    Optional<Component> getFooter();
+
+    /**
+     * Gets the padding character to be used when centering headers and footers.
+     *
+     * @return The padding character
+     */
+    Component getPadding();
+
+    /**
+     * Gets the maximum amount of lines that will be sent per page.
+     *
+     * <p>This defaults to the maximum amount of lines that can be displayed
+     * on a source's screen at one time if not specified.</p>
+     *
+     * @return The maximum amount of lines that will be sent per page
+     */
+    int getLinesPerPage();
+
+    /**
+     * Sends the first page of the constructed pagination list
+     * to the specified message receiver.
+     *
+     * @param receiver The receiver to send the first page to
+     * @see PaginationList#sendTo(MessageReceiver, int) to send a specific page
+     */
+    default void sendTo(CommandSource receiver) {
+        sendTo(receiver, 1);
+    }
+
+    /**
+     * Send the specified page of the constructed pagination list
+     * to the specified message receiver.
+     *
+     * <p>A page that is out of bounds will result in a friendly
+     * error message being sent to the receiver.</p>
+     *
+     * <p>Pages start at an index of 1.</p>
+     *
+     * @param receiver The receiver to send the page to
+     * @param page The page to send, starting at an index of 1
+     */
+    void sendTo(CommandSource receiver, int page);
+
+    /**
+     * Sends the first page of the constructed pagination list to
+     * all {@link MessageReceiver}s within an {@link Iterable}.
+     *
+     * @param receivers The message receivers to send the first page to
+     * @see PaginationList#sendTo(Iterable, int) to send a specific page
+     */
+    default void sendTo(Iterable<CommandSource> receivers) {
+        sendTo(receivers, 1);
+    }
+
+    /**
+     * Sends the specified page of the constructed pagination list
+     * all {@link MessageReceiver}s within an {@link Iterable}.
+     *
+     * @param receivers The message receivers to send the page to
+     * @param page The page to send
+     */
+    default void sendTo(Iterable<CommandSource> receivers, int page) {
+        checkNotNull(receivers, "The iterable of receivers cannot be null!");
+        for (CommandSource receiver : receivers) {
+            sendTo(receiver, page);
+        }
+    }
+
+    /**
+     * Builds a paginated output for an iterable of {@link Text}s.
+     */
+    interface Builder {
+
+        /**
+         * Sets the contents of this output as an iterable.
+         *
+         * <p>If this {@link Iterable} is a {@link List}, bidirectional
+         * navigation is supported. Otherwise, only going to the next page will
+         * be supported.</p>
+         *
+         * @param contents The contents to output
+         * @return This builder
+         */
+        Builder contents(Iterable<Component> contents);
+
+        /**
+         * Sets the contents of this output to be the given array of contents.
+         *
+         * @param contents The contents to output
+         * @return This builder
+         */
+        Builder contents(Component... contents);
+
+        /**
+         * Sets the title text to be used in the title bar of this pagination.
+         *
+         * <p>This should be less than one line long.</p>
+         *
+         * @param title The title to use
+         * @return This builder
+         */
+        Builder title(@Nullable Component title);
+
+        /**
+         * Sets the header to be displayed for this output on all pages after
+         * the title bar but before the contents.
+         *
+         * <p>The header and footer will use this Text's style and color for
+         * formatting.</p>
+         *
+         * <p>If the header is not specified, or passed in as <code>null</code>,
+         * it will be omitted when displaying the list.</p>
+         *
+         * @param header The header to set
+         * @return This builder
+         */
+        Builder header(@Nullable Component header);
+
+        /**
+         * Sets the footer to be displayed for this output on all pages after
+         * the contents and page navigation bar.
+         *
+         * <p>If the footer is not specified, or passed in as <code>null</code>,
+         * it will be omitted when displaying the list.</p>
+         *
+         * @param footer The footer to set
+         * @return This builder
+         */
+        Builder footer(@Nullable Component footer);
+
+        /**
+         * Sets the padding character to be used when centering headers and
+         * footers.
+         *
+         * @param padding The padding to use
+         * @return This builder
+         */
+        Builder padding(Component padding);
+
+        /**
+         * Sets the maximum number of lines that can be displayed per page.
+         *
+         * <p>This defaults to the maximum amount of lines that can be displayed
+         * on a source's screen at one time if not specified.</p>
+         *
+         * @param linesPerPage The maximum number of lines to display per page
+         * @return This builder
+         */
+        Builder linesPerPage(int linesPerPage);
+
+        /**
+         * Creates a {@link PaginationList} from this pagination builder.
+         *
+         * @return The pagination list
+         * @throws IllegalStateException If no contents were specified
+         */
+        PaginationList build();
+
+        /**
+         * Sends the constructed pagination list to the given receiver.
+         *
+         * @param receiver The receiver to send the list to
+         * @return The constructed pagination list
+         */
+        default PaginationList sendTo(CommandSource receiver) {
+            final PaginationList list = build();
+            list.sendTo(receiver);
+            return list;
+        }
+
+        /**
+         * Sends the constructed pagination list to all
+         * {@link MessageReceiver}s within an {@link Iterable}.
+         *
+         * @param receivers The message receivers to send the list to
+         * @return The constructed pagination list
+         */
+        default PaginationList sendTo(Iterable<CommandSource> receivers) {
+            checkNotNull(receivers, "The iterable of receivers cannot be null!");
+            final PaginationList list = build();
+            for (CommandSource r : receivers) {
+                list.sendTo(r);
+            }
+            return list;
+        }
+
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/internal/pagination/TextComponentIterable.java b/sponge/src/main/java/com/griefdefender/internal/pagination/TextComponentIterable.java
new file mode 100644
index 0000000..a736301
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/internal/pagination/TextComponentIterable.java
@@ -0,0 +1,49 @@
+/*
+ * This file is part of Sponge, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) SpongePowered <https://www.spongepowered.org>
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.internal.pagination;
+
+import net.minecraft.util.text.ITextComponent;
+
+import java.util.Iterator;
+
+public class TextComponentIterable implements Iterable<ITextComponent> {
+
+    private final ITextComponent component;
+    private final boolean includeSelf;
+
+    public TextComponentIterable(ITextComponent component, boolean includeSelf) {
+        this.component = component;
+        this.includeSelf = includeSelf;
+    }
+
+    @Override
+    public Iterator<ITextComponent> iterator() {
+        if (this.includeSelf) {
+            return new TextComponentIterator(this.component);
+        }
+        return new TextComponentIterator(this.component.getSiblings().iterator());
+    }
+
+}
diff --git a/sponge/src/main/java/com/griefdefender/internal/pagination/TextComponentIterator.java b/sponge/src/main/java/com/griefdefender/internal/pagination/TextComponentIterator.java
new file mode 100644
index 0000000..b3d8811
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/internal/pagination/TextComponentIterator.java
@@ -0,0 +1,101 @@
+/*
+ * This file is part of Sponge, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) SpongePowered <https://www.spongepowered.org>
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.internal.pagination;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.collect.UnmodifiableIterator;
+import net.minecraft.util.text.ITextComponent;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import javax.annotation.Nullable;
+
+public class TextComponentIterator extends UnmodifiableIterator<ITextComponent> {
+
+    private ITextComponent component;
+    private Iterator<ITextComponent> children;
+    @Nullable private Iterator<ITextComponent> currentChildIterator;
+
+    public TextComponentIterator(ITextComponent component) {
+        this.component = checkNotNull(component, "component");
+    }
+
+    public TextComponentIterator(Iterator<ITextComponent> children) {
+        this.children = checkNotNull(children, "children");
+        if (this.children.hasNext()) {
+            this.setCurrentChildIterator();
+        }
+    }
+
+    @Override
+    public boolean hasNext() {
+        return this.component != null || (this.currentChildIterator != null && this.currentChildIterator.hasNext());
+    }
+
+    // In order for this method to work properly, 'currentChildIterator' must be ready to return an element
+    // (i.e its 'hasNext()' method returns true) when this method returns. If this condition can no longer be met,
+    // we're done iterating.
+    @Override
+    public ITextComponent next() {
+        if (!hasNext()) {
+            throw new NoSuchElementException();
+        }
+
+        if (this.component != null) {
+            return this.init();
+        }
+
+        ITextComponent result = this.currentChildIterator.next();
+
+        if (!this.currentChildIterator.hasNext() && this.children.hasNext()) {
+            this.setCurrentChildIterator();
+        }
+
+        return result;
+    }
+
+    private ITextComponent init() {
+        this.children = this.component.getSiblings().iterator();
+
+        ITextComponent result = this.component;
+        this.component = null;
+
+        // An iterator of an empty TextComponentTranslation doesn't have children. Thus, calling 'this.currentChildIterator.next()'
+        // at the end of this method will lead to a NoSuchElementException. To fix this, we
+        // initialize currentChildIterator so that the following call to 'hasNext()' will properly return 'false' if necessary
+        if (this.children.hasNext()) {
+            this.setCurrentChildIterator();
+        }
+
+        return result;
+    }
+
+    private void setCurrentChildIterator() {
+        this.currentChildIterator = new TextComponentIterable(this.children.next(), true).iterator();
+    }
+
+}
diff --git a/sponge/src/main/java/com/griefdefender/internal/pagination/font-sizes.json b/sponge/src/main/java/com/griefdefender/internal/pagination/font-sizes.json
new file mode 100644
index 0000000..b6954cb
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/internal/pagination/font-sizes.json
@@ -0,0 +1,50 @@
+# To create/update this file: Use the following code (accurate as of MC 1.8), run on the client. The nonUnicode string constant is extracted from FontRenderer -- it's inlined though.
+# This must be run while a game is active -- I've stuck this in a command
+# ```java
+#     public void printCharSizes() {
+#         ConfigurationNode node = SimpleConfigurationNode.root();
+#         String nonUnicode = "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130\u0131\u0152\u0153"
+#                 + "\u015e"
+#                 + "\u015f\u0174"
+#                 + "\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&\'()*+,-./0123456789:;"
+#                 + "<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000";
+#         List<Integer> nonUnicodeCodePoints = new ArrayList<Integer>(nonUnicode.length());
+#         for (int i = 0; i < nonUnicode.length(); ++i) {
+#             nonUnicodeCodePoints.add(nonUnicode.codePointAt(i));
+#         }
+#         node.getNode("non-unicode").setValue(nonUnicode);
+#         int[] charWidths = Minecraft.getMinecraft().getRenderManager().getFontRenderer().charWidth;
+#         final List<Integer> charWidthsList = new ArrayList<Integer>(charWidths.length);
+#         for (int i : charWidths) {
+#             charWidthsList.add(i);
+#         }
+#         charWidthsList.set(32, 4); // Space is handled weirdly
+#         node.getNode("char-widths").setValue(charWidthsList);
+#         InputStream var1 = null;
+# 
+#         final List<Byte> glyphSizezList = new ArrayList<Byte>(65536);
+#         try {
+#             var1 = Minecraft.getMinecraft().getResourceManager().getResource(new ResourceLocation("font/glyph_sizes.bin")).getInputStream();
+#             int b;
+#             while ((b = var1.read()) != -1) {
+#                 glyphSizezList.add((byte) b);
+#             }
+#         } catch (IOException var6) {
+#             throw new RuntimeException(var6);
+#         } finally {
+#             IOUtils.closeQuietly(var1);
+#         }
+#         node.getNode("glyph-widths").setValue(glyphSizezList);
+# 
+#         try {
+#             HoconConfigurationLoader.builder()
+#                     .setRenderOptions(ConfigRenderOptions.concise())
+#                     .setFile(new File("font-sizes.conf"))
+#                     .build().save(node);
+#         } catch (IOException e) {
+#             logger.error("Unable to write character size information", e);
+#         }
+#     }
+# ```
+
+{"char-widths":[6,6,6,6,6,6,4,6,6,6,6,6,6,6,6,4,4,6,7,6,6,6,6,6,6,1,1,1,1,1,1,1,4,2,5,6,6,6,6,3,5,5,5,6,2,6,2,6,6,6,6,6,6,6,6,6,6,6,2,2,5,6,5,6,7,6,6,6,6,6,6,6,6,4,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,4,6,4,6,6,3,6,6,6,6,6,5,6,6,2,6,5,3,6,6,6,6,6,6,6,4,6,6,6,6,6,6,5,2,5,7,6,6,6,6,6,6,6,6,6,6,6,6,4,6,3,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,4,6,6,3,6,6,6,6,6,6,6,7,6,6,6,2,6,6,8,9,9,6,6,6,8,8,6,8,8,8,8,8,6,6,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,6,9,9,9,5,9,9,8,7,7,8,7,8,8,8,7,8,8,7,9,9,6,7,7,7,7,7,9,6,7,8,7,6,6,9,7,6,7,1],"glyph-widths":[15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,68,38,22,23,23,23,68,53,36,23,23,52,22,52,22,22,38,22,22,22,22,22,22,22,22,52,52,38,22,21,22,22,22,22,22,22,22,22,22,22,38,23,22,22,22,22,22,22,23,22,22,23,22,23,22,22,23,22,70,22,19,22,23,36,22,22,22,22,22,21,22,22,38,21,22,38,23,22,22,22,22,22,22,21,22,22,23,22,22,22,53,68,36,23,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,68,23,23,22,23,68,22,37,7,38,22,22,15,7,22,36,23,38,38,53,38,22,52,36,36,38,22,22,22,22,22,22,22,22,22,22,22,23,22,22,22,22,22,38,38,38,38,6,22,22,22,22,22,22,22,22,22,22,22,22,23,22,22,22,22,22,22,22,22,23,22,22,22,22,22,38,38,38,38,22,22,22,22,22,22,22,22,22,22,22,22,22,22,38,22,22,22,22,22,23,23,22,22,22,22,22,22,22,22,22,22,6,23,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,7,6,22,22,38,38,22,22,38,38,38,38,22,38,23,22,6,6,22,22,38,22,38,22,38,22,21,6,38,22,22,6,6,22,22,22,22,22,22,22,22,22,23,23,23,23,22,22,6,6,22,22,22,22,22,22,22,22,22,22,23,21,23,21,23,21,22,22,22,22,22,22,22,22,23,23,22,23,22,23,23,22,23,22,22,22,22,22,22,37,6,6,22,22,22,22,22,23,23,6,6,22,22,22,22,22,22,23,21,23,22,22,21,38,22,22,38,22,23,6,22,22,22,22,22,22,23,23,23,22,22,22,21,21,23,21,23,23,23,22,22,23,23,22,22,22,22,22,22,22,22,22,21,22,51,36,21,68,23,23,23,23,23,7,23,23,23,22,22,38,38,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,22,22,22,22,22,22,22,22,22,22,22,23,23,23,22,22,22,22,22,22,22,22,23,23,22,22,6,6,22,22,6,6,22,22,6,6,22,22,6,6,22,22,6,6,22,22,6,6,22,22,22,22,23,21,22,22,22,22,22,23,22,38,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,22,37,23,22,21,6,6,6,6,6,22,23,23,22,22,22,22,6,22,22,6,23,23,23,23,22,22,23,23,22,22,22,22,22,22,23,23,22,22,23,22,22,23,22,23,23,22,22,22,22,22,22,22,38,53,38,22,22,54,23,23,23,23,23,23,22,22,23,23,23,22,22,23,22,22,22,22,22,22,22,21,21,21,22,21,22,23,22,22,22,23,22,23,23,23,22,22,22,22,22,22,22,22,22,23,38,22,22,22,23,22,22,23,23,23,23,23,23,23,39,22,23,22,21,23,38,38,37,38,21,23,21,21,21,36,22,52,52,52,37,37,22,22,37,37,23,23,37,37,68,37,37,37,68,37,37,37,36,36,37,37,21,21,21,21,22,52,37,70,22,23,6,22,21,36,21,21,21,21,21,21,21,21,21,20,21,22,22,21,21,53,53,37,36,22,22,22,52,36,36,36,36,22,22,22,22,22,22,22,22,7,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,22,22,22,22,22,22,22,22,23,22,22,22,22,22,22,7,7,22,22,7,22,7,22,22,22,6,22,22,7,22,22,22,22,22,22,22,22,22,22,22,22,22,23,22,15,22,22,22,22,22,22,7,22,22,22,22,22,7,7,7,7,7,7,7,22,22,22,22,22,22,22,22,6,22,22,22,22,21,22,6,21,37,37,22,6,0,0,52,22,22,22,52,0,0,0,0,0,51,21,22,53,22,22,22,0,22,0,23,23,21,22,22,22,23,22,22,22,22,38,22,23,22,22,22,22,23,22,0,22,23,23,23,22,23,23,38,23,22,22,22,53,22,22,22,23,22,22,38,22,22,53,38,22,22,22,22,22,22,22,22,23,22,22,23,22,23,23,37,22,22,22,23,22,22,23,23,23,23,23,23,23,22,22,22,22,22,38,22,21,22,21,7,23,22,38,22,22,22,22,7,22,22,22,23,23,22,22,22,38,22,21,21,22,22,22,22,23,22,22,22,22,22,22,23,22,22,22,38,38,22,7,23,23,22,22,23,23,22,22,22,22,7,22,23,22,22,22,22,22,22,22,22,22,22,22,23,23,23,22,23,22,23,7,23,22,22,22,22,22,22,22,22,22,23,22,23,22,22,22,22,22,22,22,22,22,22,22,23,22,23,22,23,22,23,7,23,22,22,22,22,22,22,22,22,22,22,22,38,38,21,7,23,22,22,22,22,38,23,23,23,22,23,23,23,23,23,23,23,23,23,23,22,22,23,23,22,22,23,22,23,23,23,23,23,23,23,23,23,23,22,22,23,22,6,22,22,7,13,30,23,23,22,22,22,22,22,22,23,23,22,22,23,23,22,22,22,22,23,23,23,23,7,7,23,23,23,23,23,23,22,22,22,22,23,23,23,38,23,38,23,23,7,7,23,23,22,22,22,22,22,22,22,22,38,23,23,22,22,23,23,22,22,23,23,22,22,23,23,53,22,22,22,22,23,23,22,22,22,22,22,22,23,23,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,22,23,22,23,23,22,22,22,22,22,22,22,21,22,22,22,22,22,22,23,23,23,23,23,23,7,7,23,23,22,22,23,23,22,22,22,22,7,7,7,7,7,7,23,22,22,23,22,22,7,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,23,22,23,23,22,22,22,22,23,23,22,22,23,22,22,22,22,23,22,23,22,23,22,22,22,23,22,23,22,23,22,22,22,23,21,22,22,23,0,0,35,35,36,37,36,21,21,0,23,22,23,23,22,23,21,22,23,23,22,21,23,23,22,22,22,23,23,23,21,22,23,22,39,23,23,23,22,23,23,22,22,54,23,23,22,23,23,0,52,22,0,0,0,0,0,0,22,22,22,22,22,22,22,22,6,22,22,22,23,23,7,23,6,22,22,22,22,22,22,22,22,22,22,22,23,6,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,68,22,22,53,22,22,37,22,0,0,0,0,0,0,0,0,22,23,22,22,22,36,38,22,22,36,22,22,38,6,23,36,37,22,6,22,22,38,22,22,22,23,22,0,0,0,0,0,23,23,23,70,22,0,0,0,0,0,0,0,0,0,0,0,15,15,15,15,0,0,28,27,45,21,23,60,52,21,62,40,23,76,60,44,60,74,58,90,90,90,90,52,0,0,121,23,0,37,23,36,22,53,22,68,22,38,22,22,22,22,22,22,22,22,22,23,23,23,23,22,22,22,22,6,6,22,22,22,7,22,22,22,22,38,22,26,22,22,22,22,22,22,22,22,22,22,22,23,22,22,90,90,90,90,90,90,90,90,90,0,36,37,38,23,22,22,23,23,23,23,21,52,52,23,22,24,22,38,22,22,36,38,23,23,23,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,22,22,23,22,22,22,22,23,23,23,23,23,22,22,22,22,22,22,22,22,22,22,6,30,6,22,22,22,6,6,6,6,6,6,23,22,23,22,22,22,22,22,22,22,22,37,22,22,22,22,22,22,22,22,22,22,22,22,6,22,22,22,22,23,23,37,37,61,60,90,74,58,74,76,15,31,90,90,90,90,76,90,38,23,58,90,31,90,90,90,90,22,23,36,37,38,23,22,23,22,23,23,23,23,23,22,37,22,26,59,52,52,52,52,22,22,22,22,22,22,30,29,30,0,15,15,22,28,30,30,39,39,30,60,70,30,30,30,37,30,63,43,29,46,29,29,30,30,13,13,61,39,30,29,30,30,23,22,22,22,22,22,22,22,22,22,22,22,22,22,59,59,22,22,22,22,22,22,22,22,22,22,126,22,0,0,60,30,30,22,22,22,22,22,22,22,22,22,22,22,23,23,22,22,22,22,22,6,6,6,38,38,22,22,22,22,22,22,23,22,22,23,22,22,36,38,22,23,22,22,22,23,23,23,23,23,22,19,22,6,22,21,37,37,37,37,37,54,22,22,38,37,21,44,54,38,38,22,54,21,7,6,20,19,22,22,44,44,44,22,22,37,37,37,37,22,22,22,22,22,23,22,6,22,22,22,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,22,38,37,37,36,21,22,22,21,51,22,6,21,21,21,21,22,38,37,37,21,38,20,22,38,22,22,21,22,37,22,21,21,21,38,37,37,21,21,22,22,38,22,22,22,22,22,22,22,22,22,36,36,38,23,36,36,37,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,75,90,93,47,31,31,15,15,15,15,15,15,15,15,15,15,31,31,31,31,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,63,15,63,15,15,15,15,15,47,15,15,15,15,15,15,15,15,15,15,15,15,0,0,90,127,95,10,95,74,91,90,90,90,74,74,58,95,95,95,95,90,0,0,30,90,90,90,90,0,0,0,15,15,15,15,15,15,15,15,15,15,75,75,120,106,75,90,76,75,45,75,75,61,60,75,106,-119,31,0,0,0,0,0,0,0,0,15,15,75,15,15,0,90,95,94,0,15,15,15,15,15,15,31,30,0,0,30,30,0,0,30,30,15,47,47,15,45,15,15,15,15,30,15,15,15,15,63,15,31,15,79,15,0,31,15,15,15,15,15,15,0,15,0,0,0,31,15,15,15,0,0,90,13,95,12,79,90,91,90,90,0,0,10,10,0,0,15,15,90,75,0,0,0,0,0,0,0,0,95,0,0,0,0,15,15,0,15,31,75,91,94,0,0,60,76,60,45,44,44,45,76,29,45,15,15,59,28,59,45,45,-119,44,61,30,0,0,0,0,0,0,90,90,95,0,15,15,15,14,15,15,0,0,0,0,15,15,0,0,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,0,15,15,15,15,15,15,15,0,15,15,0,15,15,0,15,15,0,0,90,0,79,9,95,74,74,0,0,0,0,92,92,0,0,92,92,94,0,0,0,74,0,0,0,0,0,0,0,15,15,15,15,0,15,0,0,0,0,0,0,0,43,75,75,42,74,45,76,75,43,43,90,90,15,15,46,90,0,0,0,0,0,0,0,0,0,0,0,90,90,95,0,30,15,92,76,75,45,46,45,30,0,30,30,15,0,15,15,73,27,60,93,42,45,42,27,59,60,21,72,39,42,27,75,59,89,75,58,0,43,57,46,62,76,76,57,0,42,43,0,75,60,75,44,42,0,0,90,21,95,10,95,91,91,90,90,94,0,94,94,95,0,95,95,91,0,0,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,45,92,94,0,0,43,75,75,59,60,43,60,43,57,41,0,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,90,95,95,0,27,29,26,26,26,27,42,90,0,0,22,43,0,0,74,44,27,75,75,59,28,75,59,75,59,43,75,57,59,59,59,26,59,26,91,42,0,59,28,42,26,27,27,75,0,27,27,0,42,59,59,43,26,0,0,90,21,92,76,92,75,91,90,74,0,0,10,13,0,0,12,13,90,0,0,0,0,0,0,0,0,93,93,0,0,0,0,59,59,0,29,43,90,93,93,0,0,43,75,75,43,92,60,75,75,74,74,27,59,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,90,45,0,43,11,43,26,27,12,0,0,0,45,45,27,0,27,27,13,27,0,0,0,27,26,0,27,0,11,59,0,0,0,15,27,0,0,0,27,29,72,0,0,0,58,59,75,60,27,29,42,27,12,28,28,14,0,0,0,0,95,94,90,95,75,0,0,0,10,10,14,0,15,15,15,90,0,0,15,0,0,0,0,0,0,95,0,0,0,0,0,0,0,0,0,0,0,0,0,0,90,27,29,45,29,29,30,45,27,30,42,44,29,27,46,15,75,13,15,28,47,0,0,0,0,0,0,95,95,95,0,44,44,25,27,28,13,12,43,0,60,60,60,0,59,59,43,74,26,72,28,59,28,28,59,44,43,27,57,27,27,44,44,57,57,57,43,0,60,60,28,29,28,28,57,44,27,44,0,60,58,61,60,28,0,0,0,92,95,90,74,95,95,95,95,0,74,74,75,0,43,28,43,58,0,0,0,0,0,0,0,92,75,0,28,59,0,0,0,0,0,0,15,47,76,45,0,0,90,75,44,107,75,93,28,92,29,28,0,0,0,0,0,0,0,0,91,-120,106,74,60,92,92,60,0,0,95,95,0,44,44,25,11,11,14,13,43,0,28,28,28,0,28,28,28,58,45,43,12,28,12,12,26,44,43,27,57,27,27,28,59,57,57,57,43,0,28,28,28,28,12,12,57,44,26,27,0,28,26,29,43,76,0,0,90,22,95,90,95,95,95,95,95,0,77,79,79,0,79,79,62,92,0,0,0,0,0,0,0,95,95,0,0,0,0,0,0,0,44,0,15,31,76,61,0,0,90,75,91,60,75,45,28,92,26,28,0,74,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,95,95,0,13,15,27,12,57,44,59,44,0,12,12,15,0,43,45,29,28,29,29,30,28,44,45,28,14,29,74,73,45,44,28,45,28,58,44,44,0,28,28,28,58,60,45,44,59,44,43,59,28,45,28,12,12,0,0,0,108,95,95,95,95,95,95,95,0,10,10,10,0,15,15,15,95,0,0,0,0,0,0,0,0,0,95,0,0,0,0,0,0,0,0,28,29,44,44,0,0,90,44,28,29,58,60,30,74,42,27,45,44,30,60,43,44,0,0,0,30,29,44,59,28,44,29,0,0,95,95,0,60,14,31,31,59,59,59,30,15,15,30,15,42,45,15,29,29,15,0,0,0,12,44,10,26,27,30,42,46,44,14,13,14,29,42,27,44,27,14,44,12,27,92,44,13,0,74,61,61,44,29,44,44,42,57,0,44,0,0,27,11,43,26,29,62,44,0,0,0,95,0,0,0,0,95,95,95,76,76,59,0,59,0,95,10,15,10,15,15,15,95,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,95,95,14,0,0,0,0,0,0,0,0,0,0,0,0,22,21,22,22,22,22,21,22,22,38,22,23,22,22,22,38,22,23,23,22,22,22,22,21,22,38,38,23,23,23,23,22,22,22,21,22,22,22,21,22,23,23,22,23,22,23,21,37,23,21,22,22,22,22,22,22,22,22,0,0,0,0,22,69,55,23,22,22,21,22,22,22,23,23,22,23,22,23,22,22,22,22,22,22,22,22,22,23,22,38,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,22,0,22,0,0,21,22,0,22,0,0,22,0,0,0,0,0,0,22,22,22,6,0,22,22,22,7,6,6,6,0,22,22,22,0,22,0,22,0,0,22,7,0,22,22,38,37,23,37,6,22,22,22,22,22,22,0,23,22,22,0,0,69,38,22,22,6,0,22,0,22,22,23,22,22,22,0,0,22,22,21,22,22,22,22,22,22,22,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,23,12,12,7,22,7,7,22,39,23,52,15,52,38,52,52,22,23,7,37,7,30,31,31,22,37,23,23,53,23,23,39,55,39,39,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,53,23,22,22,23,23,12,46,22,39,23,6,23,23,23,23,23,23,23,23,0,23,23,39,23,23,39,23,39,23,23,39,23,23,23,23,23,23,23,23,23,23,23,39,23,23,39,23,23,39,23,23,23,23,23,23,23,0,0,0,0,22,22,23,22,22,22,23,22,23,6,6,7,7,22,22,22,23,23,23,87,23,22,22,40,23,6,23,0,0,0,0,23,23,23,23,23,23,23,23,0,23,23,22,23,23,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,0,23,23,23,38,22,23,23,44,29,14,44,44,14,14,14,0,44,44,29,22,89,29,75,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,41,41,13,40,41,12,42,9,25,28,8,6,24,26,12,10,10,40,57,25,41,57,57,26,57,10,25,10,40,26,10,6,11,27,44,11,25,25,57,58,14,15,94,95,90,90,90,91,95,90,90,90,90,90,91,95,90,90,94,30,90,90,31,42,57,55,39,73,89,89,72,57,89,68,37,22,92,78,12,57,57,57,57,39,41,95,95,90,91,91,93,75,91,90,90,90,74,94,94,94,90,91,94,94,94,94,94,94,94,29,29,27,91,90,91,90,58,58,58,11,11,11,11,27,27,27,57,28,57,91,95,10,90,90,94,94,94,95,94,95,91,74,94,90,89,73,89,90,74,74,90,90,90,0,0,0,0,123,76,6,6,6,6,21,6,6,6,21,21,6,6,21,6,6,22,6,21,22,6,6,21,22,21,21,21,22,22,22,22,21,6,6,22,22,21,6,6,0,0,0,0,0,0,0,0,0,0,38,38,38,23,38,38,22,23,38,38,23,38,38,23,38,22,23,38,22,22,23,38,23,38,22,38,38,38,39,22,38,38,38,38,38,38,38,22,21,38,38,39,38,38,38,0,0,0,40,40,40,40,40,40,40,40,40,55,25,55,40,41,40,40,40,24,40,45,45,45,45,45,45,45,45,45,45,61,45,45,45,45,30,30,29,30,30,45,45,45,45,76,45,61,61,61,61,61,61,30,46,61,61,61,61,61,61,61,60,60,58,61,42,61,61,62,61,62,29,61,61,61,61,61,55,45,42,44,45,45,45,45,42,43,45,60,45,44,0,0,0,0,0,15,15,-66,-66,-66,-66,-101,-98,-101,-98,46,46,46,43,46,46,43,46,43,46,46,43,-69,44,46,46,46,46,46,43,46,46,46,46,46,46,46,46,45,46,46,46,46,46,46,46,46,46,47,46,46,46,46,47,46,46,46,-98,-98,46,46,46,-117,-117,75,46,59,60,0,0,0,0,0,91,76,77,91,76,77,91,91,76,59,59,60,59,60,60,91,91,60,59,44,106,91,76,91,91,75,91,29,30,45,45,44,30,45,45,45,31,45,45,15,45,30,31,31,15,44,30,45,45,46,45,45,45,61,31,46,46,46,74,45,45,46,91,60,61,44,61,59,61,29,45,45,89,44,29,45,75,29,29,29,29,74,0,0,0,0,0,0,41,45,75,74,74,59,44,30,74,44,44,74,44,58,45,78,60,30,30,60,29,60,60,47,45,14,30,45,30,44,44,29,60,29,30,60,45,60,60,30,58,43,59,44,60,58,60,44,74,44,44,74,60,91,74,77,42,45,45,59,45,59,59,44,60,43,43,60,43,60,43,43,61,0,31,60,60,30,0,0,60,43,43,60,43,44,60,0,44,0,31,60,60,30,0,0,74,44,44,74,61,44,74,77,42,45,45,42,28,28,42,44,28,28,28,28,28,45,45,45,45,45,45,28,28,29,45,45,58,45,45,75,45,75,45,45,29,0,30,61,45,29,0,0,89,91,74,42,90,41,44,42,60,44,44,44,43,26,44,44,74,44,44,74,44,74,74,42,59,29,29,59,29,59,59,44,29,0,30,62,29,29,0,0,27,14,14,27,31,28,27,0,15,0,15,30,31,15,0,0,44,14,44,44,44,14,44,30,75,45,28,59,28,75,75,0,74,44,44,74,61,43,74,77,45,29,29,45,29,29,45,45,74,75,60,91,60,27,44,30,60,45,44,75,44,44,59,78,75,45,44,58,44,60,59,78,43,29,28,43,28,45,43,46,57,44,60,74,60,59,74,74,45,0,45,74,60,45,0,0,75,44,60,75,60,59,74,59,59,29,29,59,29,44,59,62,14,15,15,30,15,13,30,30,75,45,45,75,45,44,75,61,75,45,45,58,44,44,58,61,75,45,11,7,11,75,75,44,60,59,43,43,44,41,43,43,60,60,60,60,60,60,60,60,60,45,44,0,0,0,0,75,60,103,90,90,90,56,89,120,75,59,76,75,75,59,59,59,60,75,75,60,60,60,75,60,75,44,59,75,45,0,0,0,15,15,29,14,44,45,42,30,61,45,60,43,61,44,60,45,52,37,52,52,37,28,22,30,30,22,0,0,0,0,0,0,22,23,23,23,23,38,23,22,22,22,22,23,22,22,22,23,22,22,23,22,22,22,22,22,22,23,22,22,22,23,22,23,22,23,22,22,22,23,23,22,22,23,23,22,23,23,22,22,23,23,22,22,23,23,23,23,23,23,22,23,23,22,22,22,23,23,22,23,23,23,22,23,23,23,23,22,23,22,22,23,22,22,22,23,22,0,0,0,0,0,0,0,0,0,0,0,0,23,23,23,23,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,22,23,23,23,23,22,23,22,21,37,37,23,23,37,37,23,37,36,21,21,21,23,23,22,22,23,23,23,23,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,22,37,51,20,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,21,23,23,23,23,21,21,21,21,21,21,22,21,21,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,37,37,23,23,23,23,21,21,21,21,21,21,22,21,21,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,37,37,21,21,21,21,21,21,22,21,21,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,37,37,37,37,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,37,22,21,22,21,21,21,21,22,21,21,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,36,22,36,22,36,36,36,44,45,44,44,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,37,21,21,21,23,23,23,23,23,23,21,21,21,21,21,21,23,23,36,37,22,23,23,23,23,23,23,23,23,7,23,21,23,23,23,23,23,23,23,23,23,23,23,23,21,23,23,23,23,44,23,23,23,23,23,23,23,23,23,23,23,21,22,21,44,44,44,44,44,44,44,23,21,21,21,21,23,23,23,23,31,31,31,31,31,31,31,23,31,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,21,23,23,23,23,23,21,21,21,21,22,22,22,22,23,23,23,23,23,23,23,23,23,23,22,22,22,22,23,23,22,22,22,22,23,23,22,22,22,22,23,23,23,6,6,23,22,22,22,22,22,22,22,22,22,22,6,22,22,22,6,6,6,6,21,23,23,22,22,22,22,23,23,22,22,6,22,23,23,22,22,6,22,21,22,22,6,6,6,6,23,23,22,22,6,22,22,22,22,22,22,22,6,6,6,44,44,44,44,44,75,75,46,46,46,29,60,60,21,21,21,21,60,60,6,6,6,23,6,6,6,6,6,6,76,59,7,7,7,7,45,28,12,12,12,46,6,6,60,60,60,60,6,6,6,6,60,60,60,60,44,44,44,44,44,44,44,44,44,44,44,44,21,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,23,36,44,31,31,31,31,31,31,31,0,0,0,0,0,0,0,0,0,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,47,13,0,0,0,6,6,5,6,5,6,4,4,4,6,6,4,6,54,20,6,6,6,21,6,6,6,6,6,6,4,6,6,6,36,6,38,6,51,36,6,6,6,4,6,6,6,20,21,51,36,6,6,4,5,21,21,4,6,6,6,6,36,22,6,6,6,6,6,12,6,12,6,6,6,6,51,6,21,6,36,19,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,30,75,30,45,30,30,30,59,30,28,30,30,0,30,76,46,46,93,92,90,0,0,0,0,0,0,0,0,0,0,0,29,29,91,59,59,59,60,44,28,29,41,45,28,60,43,30,30,60,93,92,62,89,60,0,0,0,0,0,0,0,0,0,44,44,75,59,91,61,30,44,92,45,74,60,45,60,43,60,44,74,93,92,0,0,0,0,0,0,0,0,0,0,0,0,30,30,36,44,29,30,13,30,29,30,45,30,30,0,30,30,29,0,93,92,0,0,0,0,0,0,0,0,0,0,0,0,75,90,75,44,74,43,75,46,61,90,91,59,46,45,45,75,75,77,59,90,76,75,76,59,59,61,89,61,90,76,60,60,61,60,44,44,29,91,62,91,75,59,75,76,78,76,77,75,76,91,75,92,15,15,94,92,91,92,92,90,90,90,11,15,15,10,10,10,14,14,90,95,94,90,75,90,92,92,90,94,93,43,15,90,56,24,20,30,15,74,15,90,38,75,0,0,74,59,44,44,77,75,59,46,60,44,0,0,0,0,0,0,20,21,34,7,21,21,36,6,36,21,0,0,0,0,0,0,29,120,74,74,75,14,120,72,60,60,120,15,15,15,15,0,45,45,45,45,45,44,45,45,44,45,0,0,0,0,0,0,60,28,44,60,60,60,60,60,28,61,60,44,60,12,78,78,76,78,75,43,40,24,40,24,56,28,44,44,40,40,44,45,45,40,56,123,60,77,60,28,60,60,24,24,60,28,28,77,75,56,24,24,40,56,40,62,78,40,29,44,24,46,40,75,61,56,24,30,61,62,60,60,77,76,24,45,62,62,78,77,60,44,28,24,46,40,45,60,0,0,0,0,0,0,0,0,60,105,75,44,44,90,14,60,61,45,28,24,40,56,40,40,40,40,61,29,30,46,45,60,56,43,62,60,78,76,45,44,44,77,28,45,62,62,72,24,61,10,62,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,56,9,25,8,27,25,24,24,39,42,24,25,25,24,56,10,8,25,24,24,23,57,40,8,26,38,41,25,0,0,0,75,92,75,94,95,78,79,58,42,95,94,94,0,0,0,0,95,95,90,93,95,95,95,93,95,92,90,74,0,0,0,0,25,0,0,0,20,25,74,75,59,74,44,74,74,58,59,58,21,21,21,6,6,22,6,6,22,37,6,37,6,37,6,7,21,6,22,85,37,37,37,37,37,37,37,37,37,6,0,0,23,21,21,22,21,0,0,0,0,0,0,0,0,0,0,0,75,58,59,90,59,59,58,58,90,75,93,73,74,42,42,90,59,74,59,74,60,60,59,59,75,59,57,59,43,59,75,59,59,73,59,74,73,59,59,74,59,59,0,0,0,0,0,0,95,95,95,95,95,10,10,10,95,95,10,95,95,95,95,95,95,89,58,89,59,59,73,59,95,95,0,0,0,0,0,0,75,76,90,106,75,59,90,44,30,74,0,0,0,0,45,45,76,89,59,59,73,73,89,76,74,89,45,45,30,30,45,45,74,73,59,59,72,74,73,76,74,73,45,45,30,30,45,45,23,60,38,7,60,6,23,43,23,23,23,60,60,60,29,30,29,21,29,29,108,29,29,94,95,10,95,10,0,0,7,21,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,59,74,90,91,95,45,14,44,15,75,61,60,14,46,15,58,15,10,15,30,30,29,29,27,60,28,44,15,15,27,27,28,43,15,30,43,60,45,60,43,44,14,28,44,29,43,14,60,15,27,43,14,90,95,58,58,95,95,75,79,28,31,26,10,31,15,58,63,95,30,15,30,60,60,43,15,0,0,0,0,105,31,46,10,72,75,58,14,44,30,15,15,105,91,90,45,89,-49,89,89,89,107,58,75,36,14,53,90,90,90,90,90,74,91,90,90,21,20,5,5,20,20,20,5,35,0,0,0,90,75,94,58,73,91,61,92,57,58,45,29,92,91,61,44,93,62,60,43,106,45,77,77,46,75,61,90,75,91,45,60,79,94,92,91,90,90,10,95,90,90,95,0,0,0,31,31,75,92,91,92,91,30,91,75,60,91,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,59,57,75,60,45,108,58,60,77,44,107,75,58,59,76,59,60,60,59,74,75,59,75,60,61,44,90,76,59,60,74,74,59,74,61,59,95,95,95,10,10,13,95,95,90,90,90,90,74,90,42,42,10,10,93,90,0,0,0,-120,105,106,91,60,89,105,75,75,75,75,59,106,90,106,0,0,0,59,59,44,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,6,22,22,22,6,22,22,6,22,6,22,6,6,6,6,22,7,6,6,7,22,22,6,6,6,52,52,52,6,36,21,51,37,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,21,22,6,6,22,21,5,21,22,21,21,38,22,38,21,22,22,23,23,23,23,21,38,21,22,22,22,23,22,39,23,23,23,22,22,22,22,6,22,23,23,22,23,22,21,22,21,6,21,21,21,22,22,21,21,38,38,38,21,21,22,21,22,22,23,22,22,22,22,22,6,21,21,22,22,22,22,22,21,20,23,22,22,22,38,38,22,21,22,22,23,22,6,22,23,22,23,22,21,22,22,22,22,23,21,23,22,7,6,23,21,7,23,23,23,23,7,22,6,22,22,22,45,21,21,23,23,23,23,23,21,23,22,38,23,23,22,22,23,22,22,22,22,22,23,23,23,23,22,60,39,22,21,7,22,22,21,21,38,38,21,38,20,21,21,37,21,21,22,21,36,54,23,23,23,23,38,22,21,37,21,22,23,23,38,22,38,38,23,23,22,38,22,22,22,22,22,22,22,6,6,6,22,6,6,7,22,22,23,6,22,6,6,6,7,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,22,6,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,21,22,22,21,22,21,22,22,22,22,22,6,23,22,22,38,38,22,22,22,22,22,22,22,38,22,38,22,38,22,38,22,23,22,23,22,23,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,21,23,21,23,21,23,22,22,22,22,22,22,22,22,22,22,22,23,22,23,21,22,23,22,23,22,23,21,23,21,23,21,21,22,22,23,22,22,22,22,22,22,22,22,21,23,22,22,37,21,21,22,22,21,22,22,22,22,22,22,22,22,23,39,23,38,22,21,22,21,22,6,22,6,22,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,38,38,38,38,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,22,23,22,23,22,23,22,6,7,22,21,22,22,22,22,22,22,22,22,6,6,6,6,6,6,6,6,6,6,22,22,22,22,22,22,0,0,6,6,6,6,6,6,0,0,22,22,22,22,22,22,22,22,6,6,6,6,6,6,6,6,53,53,21,21,21,21,22,22,6,6,6,6,6,6,6,6,22,22,22,22,22,22,0,0,6,6,6,6,6,6,0,0,22,22,22,22,22,22,22,22,0,7,0,7,0,7,0,7,23,23,23,23,23,23,23,23,7,7,7,7,7,7,23,23,22,22,22,22,22,22,53,53,22,22,22,22,23,23,0,0,22,22,22,22,22,22,22,22,6,6,6,6,6,6,6,6,22,22,22,22,22,22,22,22,6,6,6,6,6,6,6,6,23,23,23,23,23,23,23,23,7,7,7,7,7,7,7,7,22,22,22,22,22,0,22,22,22,22,6,6,22,52,53,52,22,22,22,22,22,0,22,22,6,6,6,6,22,21,21,22,21,37,22,22,0,0,22,22,38,38,6,6,0,21,21,22,22,22,22,22,22,22,22,22,23,23,7,7,6,22,22,52,0,0,23,23,23,0,23,23,6,6,7,7,23,52,52,0,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,37,37,22,22,23,7,53,7,52,52,52,52,22,22,22,22,23,23,21,37,52,38,23,52,15,15,15,15,15,15,15,15,7,7,37,7,7,37,7,7,21,70,19,23,37,22,22,22,22,22,23,37,22,70,19,22,22,22,21,6,38,21,23,52,22,23,22,7,22,6,37,15,21,21,51,6,21,51,51,15,15,15,15,15,15,0,0,0,0,0,15,15,15,15,15,15,38,68,0,0,38,38,38,38,38,38,38,38,38,69,52,38,38,36,38,38,38,38,38,38,38,38,38,38,38,69,52,0,21,21,21,22,21,0,0,0,0,0,0,0,0,0,0,0,22,22,22,7,23,23,23,23,23,23,22,22,22,6,23,23,39,7,22,7,7,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,22,6,6,22,22,22,22,7,7,7,22,23,14,30,14,14,7,30,30,46,22,22,46,22,7,61,22,22,22,22,22,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,23,23,6,22,23,23,22,22,6,23,7,7,23,22,6,38,23,7,22,7,23,7,7,23,22,23,23,7,23,22,22,7,7,7,7,22,22,23,23,22,22,21,22,23,22,22,21,22,22,22,7,23,22,23,22,23,22,44,30,61,61,23,44,60,22,22,22,6,60,60,6,22,6,22,23,29,7,21,29,0,0,0,22,22,22,22,22,6,22,22,22,22,22,22,22,53,22,23,23,23,7,7,7,23,23,23,23,22,22,22,23,53,22,23,23,23,23,23,7,23,23,23,23,53,22,23,23,22,22,59,22,22,6,6,7,29,0,0,0,0,0,0,0,22,38,22,38,7,38,22,22,22,22,23,23,23,23,7,38,7,38,23,23,22,38,22,38,38,23,23,23,23,7,7,22,21,38,21,38,23,21,7,7,22,22,22,22,22,22,70,36,22,22,70,36,22,7,22,22,7,22,7,22,22,6,7,23,22,38,22,38,7,38,22,22,22,22,6,23,7,7,38,38,23,38,23,38,22,22,7,23,7,23,23,23,23,23,23,23,23,23,23,38,29,7,7,7,7,44,28,45,29,7,7,45,23,37,22,22,22,23,23,23,22,22,22,22,22,22,22,23,23,22,22,23,23,22,22,38,37,37,23,23,23,22,23,22,22,22,22,52,22,37,22,23,23,22,22,38,23,7,38,23,7,39,39,22,22,22,52,22,22,22,22,22,22,22,23,23,52,22,22,22,22,22,22,22,22,22,22,22,23,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,7,7,37,22,38,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,22,22,23,23,37,37,22,22,23,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,38,38,38,38,22,22,23,23,22,22,38,52,38,38,38,38,38,38,38,22,23,23,22,22,23,23,38,22,38,21,23,23,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,52,7,7,7,27,59,22,59,59,22,25,25,44,42,22,42,22,76,23,6,22,38,38,38,38,53,54,20,54,20,71,20,71,20,22,23,23,23,23,23,23,7,23,22,7,22,20,54,20,54,70,36,22,22,23,23,7,23,22,20,54,7,23,7,23,22,23,7,23,23,22,22,23,38,23,23,23,23,23,23,23,7,7,23,23,23,23,23,23,23,23,23,23,38,23,23,23,23,23,38,38,23,23,23,23,23,38,38,23,38,23,23,23,23,23,23,38,38,38,23,23,23,22,22,37,23,23,22,38,23,23,22,22,54,22,23,22,22,54,23,22,30,30,29,60,38,6,29,29,29,45,59,30,30,44,12,12,12,30,45,45,59,59,59,59,29,30,23,21,29,29,14,29,38,35,38,21,69,21,38,35,38,21,69,21,54,20,54,52,20,54,20,68,7,22,22,30,30,30,30,30,23,0,119,7,7,7,7,38,38,44,44,44,44,44,44,44,44,44,44,44,39,39,29,30,6,68,6,6,6,14,14,14,30,30,30,6,15,15,15,14,14,30,30,29,76,30,29,29,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,30,29,29,30,30,29,30,26,27,27,27,27,27,27,27,28,29,29,29,29,30,30,29,29,27,30,30,25,25,25,25,25,30,22,22,22,23,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,38,38,38,38,38,38,22,7,23,23,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,14,14,14,14,14,14,14,14,14,30,30,30,30,30,30,30,30,30,30,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,73,59,59,60,59,59,60,59,59,12,11,13,13,12,13,13,13,13,13,29,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,15,30,30,30,30,30,30,30,30,30,30,15,15,15,15,15,15,15,15,15,15,30,7,7,68,52,7,7,68,52,6,6,68,52,71,71,55,55,4,4,4,4,71,71,55,55,4,4,4,4,71,71,55,55,55,55,55,55,4,4,4,4,4,4,4,4,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,6,6,68,52,7,53,71,55,55,4,5,5,71,55,55,4,5,5,71,55,55,4,5,5,7,7,7,7,7,7,7,7,7,71,4,4,71,7,7,7,3,68,71,68,3,52,71,52,7,52,7,52,15,15,15,15,15,15,15,15,15,13,11,9,7,5,3,1,-113,14,15,15,15,-17,7,-113,7,15,15,15,15,-113,15,15,23,23,23,23,23,23,23,23,23,23,37,37,23,23,37,37,7,7,22,22,22,22,22,22,37,37,22,22,22,22,22,22,22,22,37,37,22,22,23,23,23,23,22,23,23,23,23,23,23,23,23,23,23,23,20,71,7,7,7,7,20,71,71,20,23,23,22,22,22,22,21,22,22,22,22,23,23,22,22,7,22,22,22,22,7,7,7,7,22,22,22,22,22,37,37,22,23,7,23,15,23,23,23,38,22,7,7,7,21,23,23,23,23,23,23,38,23,45,44,44,29,30,23,23,23,37,23,37,7,37,29,14,29,38,38,22,38,23,7,44,61,7,23,29,29,29,29,29,29,29,29,29,23,7,7,7,23,39,22,38,38,38,23,22,22,23,7,22,23,23,23,23,7,23,23,23,23,22,22,22,23,23,23,23,22,22,23,23,23,23,22,22,23,23,38,23,23,23,38,23,7,20,22,7,7,22,22,22,23,23,15,14,14,14,14,14,14,14,14,15,14,14,12,45,59,59,59,59,59,59,12,12,12,12,29,29,29,29,29,29,22,22,15,30,29,59,14,44,44,29,14,29,14,14,0,0,29,22,44,44,60,75,44,14,6,13,6,6,38,44,61,29,47,44,6,5,6,6,29,22,22,7,6,6,23,0,0,0,45,45,30,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,13,14,14,0,14,14,14,30,0,0,14,31,28,14,28,14,14,29,14,44,14,28,28,14,14,14,14,59,29,29,14,14,14,14,14,15,14,14,0,14,14,14,14,14,14,14,15,14,14,14,14,14,29,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,30,14,14,14,14,0,46,0,62,62,62,62,0,0,0,29,0,120,105,75,74,74,14,14,0,0,30,59,59,14,29,14,30,54,37,38,38,38,38,39,22,23,6,53,36,22,22,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,0,0,0,44,14,44,14,14,14,14,14,14,14,30,30,30,14,14,91,14,30,30,14,14,14,14,15,0,15,30,14,44,30,44,44,14,44,14,14,14,14,14,0,22,44,21,60,60,38,38,6,30,30,36,0,45,0,0,0,76,6,59,22,22,45,45,30,59,59,29,29,30,30,30,121,6,29,14,31,14,31,38,21,37,37,22,22,54,20,70,19,44,44,45,45,14,14,14,13,14,14,13,13,30,13,30,14,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,14,14,31,14,46,30,31,30,89,89,59,59,31,14,31,14,13,14,23,23,30,30,30,30,30,30,30,30,30,31,31,30,30,46,46,45,28,28,28,30,46,30,29,45,45,46,46,46,46,30,30,30,29,29,60,60,37,37,45,45,45,45,45,45,44,44,30,30,30,14,14,14,14,21,30,30,21,21,30,36,30,36,31,14,21,21,31,14,21,21,46,29,21,21,46,29,21,21,30,76,30,76,30,30,30,30,30,30,30,30,76,76,30,14,14,31,14,14,76,30,76,76,30,76,23,23,59,59,38,105,37,22,22,37,37,37,37,37,37,70,19,54,20,54,20,38,21,61,61,61,61,37,37,119,104,46,45,45,46,46,61,61,46,46,46,46,22,22,46,46,46,46,45,45,45,45,59,59,59,59,59,44,59,59,59,59,59,59,61,59,61,61,61,61,29,45,27,27,27,27,27,45,29,29,31,14,59,44,44,6,6,6,6,6,6,6,36,36,91,91,44,59,44,30,45,46,44,30,30,30,30,59,29,29,59,6,91,91,57,57,57,57,57,57,30,21,21,21,23,6,6,6,6,6,29,29,61,61,61,58,59,59,59,30,30,44,59,59,13,74,74,74,74,61,74,74,75,74,74,59,74,74,74,74,74,44,22,37,60,37,21,21,5,21,22,74,21,21,21,21,21,74,74,38,21,21,21,59,75,75,44,14,8,29,29,29,22,22,53,60,75,75,75,75,59,59,37,37,37,37,6,6,75,75,59,59,46,59,59,44,44,45,45,60,77,29,44,44,44,44,59,59,59,59,59,59,44,44,23,23,44,44,5,5,5,5,44,5,5,5,5,5,44,29,30,5,45,59,59,43,60,74,74,74,74,74,74,74,74,75,58,74,74,75,58,5,5,75,58,5,5,21,21,22,22,74,74,74,74,74,74,74,74,75,75,75,75,59,59,45,93,29,59,59,59,59,59,59,59,59,44,59,76,59,59,59,59,59,59,59,59,59,59,30,30,44,44,22,22,22,22,22,22,22,22,44,44,22,22,22,22,30,30,59,59,59,59,22,22,22,22,29,29,59,59,59,59,59,37,59,59,44,43,45,60,45,45,59,59,59,59,59,60,60,21,21,21,21,6,75,74,44,51,44,44,74,74,44,74,75,36,36,44,44,44,44,30,29,6,6,44,44,44,44,30,6,30,30,30,30,60,60,60,60,60,60,60,60,44,30,30,37,37,45,45,44,44,30,30,6,6,6,6,21,21,21,14,14,22,22,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,14,14,30,30,30,30,30,30,0,0,0,29,44,44,61,61,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,21,6,6,6,6,21,6,6,7,6,6,6,7,37,6,61,6,38,6,22,6,6,7,6,6,6,6,6,7,6,6,60,6,6,6,22,6,6,6,45,28,13,7,6,53,6,22,0,21,38,6,6,6,21,6,23,7,6,6,6,6,37,22,44,22,38,22,21,6,6,6,6,6,6,22,38,6,6,21,60,22,6,6,21,22,22,6,45,45,30,6,22,53,22,22,0,22,21,23,22,22,7,7,22,22,23,23,22,22,22,22,22,0,7,6,23,6,21,21,23,6,22,6,38,53,21,0,0,7,7,22,21,22,21,7,6,6,6,22,22,6,6,22,22,7,7,36,36,22,22,7,6,6,21,6,21,7,6,6,6,7,22,22,22,22,22,6,6,7,7,6,6,23,23,7,7,6,6,6,21,22,21,6,22,21,37,6,22,7,7,7,7,6,6,6,6,7,5,37,37,7,7,22,22,6,6,76,75,22,21,22,22,37,37,6,22,7,22,7,23,6,6,22,22,7,7,6,6,22,7,7,14,23,23,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,21,21,6,37,22,37,22,6,6,21,6,43,22,60,22,37,60,38,21,44,7,7,59,21,5,60,60,6,6,22,22,37,5,59,60,21,22,7,44,21,21,21,22,59,0,0,0,0,0,0,0,0,0,0,37,6,6,6,6,6,6,21,21,22,22,22,6,6,21,6,6,6,52,7,21,6,22,6,60,36,21,6,6,37,22,51,6,51,37,37,6,6,21,52,7,6,6,23,21,6,23,22,21,21,36,6,37,6,0,0,0,0,0,0,0,0,0,37,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,46,29,9,10,9,43,43,61,61,43,8,44,44,44,45,30,28,44,29,13,27,30,0,0,0,0,0,0,0,0,0,44,29,29,44,29,26,45,0,60,60,60,60,60,44,28,0,30,30,30,30,30,30,30,0,15,15,15,14,15,14,14,0,60,60,60,43,60,44,60,0,76,60,60,76,60,60,76,0,11,14,14,11,14,11,11,0,42,27,27,25,27,41,25,0,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,22,22,22,22,22,22,22,22,22,6,37,37,37,37,37,37,21,21,5,5,5,21,21,21,29,30,30,31,18,44,44,44,37,22,22,7,22,23,21,21,23,23,37,37,37,37,37,37,22,22,22,5,22,22,22,7,22,52,37,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,59,13,44,59,14,73,27,14,43,-99,91,45,59,44,14,30,14,30,45,28,14,28,5,29,5,14,0,13,44,59,30,14,45,4,13,13,29,30,22,14,5,14,5,44,31,5,14,7,5,44,44,29,14,45,14,14,14,14,14,14,28,29,14,14,14,13,5,29,14,14,12,28,5,44,14,14,14,14,14,-83,6,14,14,14,29,60,21,14,14,30,29,14,14,14,6,7,6,14,13,14,14,14,29,46,14,14,46,14,14,14,14,14,14,14,14,14,0,0,0,0,0,0,0,0,0,0,0,0,15,119,90,25,14,87,15,14,30,30,14,14,45,14,5,30,29,28,29,29,60,29,45,14,108,90,14,30,14,44,29,14,14,14,14,59,14,14,14,30,14,14,14,13,44,29,29,14,46,44,14,29,14,14,30,13,44,28,29,57,14,29,13,14,30,14,14,14,14,14,14,59,46,27,30,14,14,14,30,14,46,14,45,30,14,29,14,14,14,28,14,30,30,14,30,14,14,14,14,14,13,44,14,14,14,44,14,14,60,14,14,14,14,45,14,30,14,14,14,29,30,29,30,29,14,30,30,14,30,45,46,60,14,45,14,14,14,46,29,30,14,29,14,14,14,14,46,29,30,14,14,14,29,44,14,14,14,30,14,14,14,14,46,14,14,14,14,14,45,14,30,14,14,30,14,30,14,14,14,14,30,14,14,13,14,14,29,30,14,14,29,45,29,30,29,30,46,14,14,14,14,14,14,14,30,29,14,46,14,14,14,14,14,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,14,14,14,14,14,14,14,14,14,14,14,0,0,0,0,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,0,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,0,0,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,0,0,0,0,0,44,44,44,45,44,29,44,44,58,29,13,74,89,29,29,57,12,74,43,29,44,60,29,29,29,29,60,59,59,44,60,29,44,29,119,27,44,29,29,30,29,0,0,0,45,29,15,45,14,15,29,30,44,14,30,29,14,30,15,15,44,59,45,30,45,14,59,28,14,45,45,44,29,29,-97,124,-97,124,107,109,107,109,31,31,29,28,31,31,28,28,28,31,31,28,-101,15,14,15,14,15,15,30,15,14,14,29,30,31,45,15,14,15,15,14,14,44,44,14,30,14,30,15,44,30,44,14,14,29,15,29,31,28,28,28,12,28,104,92,0,-120,106,41,41,43,42,41,41,41,41,41,42,42,42,44,41,44,44,74,45,29,29,29,29,29,60,30,30,29,30,29,60,29,29,29,55,39,23,22,23,0,0,0,0,0,0,0,0,41,87,59,14,75,43,43,43,30,44,41,45,106,30,43,77,30,119,90,73,89,60,29,61,43,90,104,90,60,46,44,76,30,45,43,29,0,0,0,0,0,0,0,0,0,0,0,0,22,23,6,37,6,59,22,6,75,6,60,6,5,77,5,59,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,0,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,0,0,0,0,0,0,0,0,0,0,0,0,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,0,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,0,0,0,0,0,0,0,0,0,0,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,27,27,27,44,59,74,74,75,44,74,74,59,59,59,75,59,28,28,28,44,44,59,91,59,59,91,44,59,59,76,74,59,59,59,75,75,74,59,74,74,59,59,59,74,59,74,74,90,74,74,59,75,75,43,29,29,44,58,58,59,59,59,59,59,59,59,75,75,75,75,59,74,74,75,90,58,75,75,59,59,59,44,44,43,59,59,59,59,59,44,59,28,45,60,45,30,30,59,74,74,59,59,74,74,59,43,60,45,45,43,60,60,58,58,59,59,59,74,75,75,75,60,59,59,74,45,59,59,59,44,59,59,59,75,58,58,59,29,29,75,59,59,30,44,44,74,59,59,59,59,59,75,44,45,45,45,60,60,44,60,44,44,74,74,74,74,59,59,59,75,75,75,59,59,59,59,59,60,44,44,60,60,59,59,59,59,59,59,45,59,76,76,75,45,45,60,42,59,60,59,59,75,59,59,75,59,91,91,75,29,60,60,60,58,75,59,59,44,44,59,44,44,59,43,43,43,59,44,44,59,43,59,59,44,59,59,59,44,60,60,60,45,59,59,45,43,43,59,58,58,29,59,59,45,59,59,45,59,59,59,45,45,30,60,60,60,29,74,74,74,44,44,44,59,59,60,75,44,44,59,60,60,44,43,60,60,43,43,75,75,59,59,75,29,75,75,29,74,44,44,29,60,60,59,59,59,59,60,44,44,60,59,59,60,44,44,44,59,45,45,44,74,74,74,74,59,59,59,74,74,44,59,59,59,60,59,59,60,74,74,74,44,44,60,60,60,44,75,59,59,59,45,45,44,74,59,59,59,59,59,60,60,60,44,44,59,59,30,60,44,44,74,44,44,29,45,45,74,74,45,30,44,75,75,59,59,59,44,60,60,60,44,44,75,75,30,30,45,74,74,44,75,59,59,60,59,59,60,59,59,59,43,60,75,75,59,59,60,74,74,44,45,45,44,59,59,59,60,75,75,59,44,44,75,43,60,60,29,29,44,73,73,59,74,73,73,74,60,74,74,45,75,60,60,44,59,74,74,74,74,74,60,59,59,59,29,44,44,29,60,60,60,90,90,44,75,75,44,59,59,59,44,59,59,59,59,29,29,29,59,29,44,44,59,44,74,74,44,59,59,59,59,44,44,90,59,75,59,44,44,44,60,60,60,44,60,60,74,59,59,59,59,59,74,74,59,59,59,59,59,57,74,59,74,74,60,60,60,60,75,75,44,75,44,44,59,44,74,74,73,59,59,59,44,60,59,59,59,59,59,30,30,59,60,60,45,44,44,74,74,59,59,60,59,59,44,44,74,74,59,59,59,75,44,44,59,73,59,59,75,45,45,45,45,44,90,90,59,74,74,74,44,59,59,44,60,60,30,44,60,60,44,44,89,89,59,74,74,60,74,74,59,60,60,60,59,43,60,60,60,44,44,59,60,59,59,44,45,45,74,45,60,60,60,75,75,75,75,75,74,60,60,60,59,59,59,75,60,60,60,60,60,60,59,73,73,45,74,29,29,59,60,60,74,74,89,89,59,29,29,60,73,73,75,45,45,60,59,59,74,30,91,91,59,59,59,59,60,59,59,74,44,44,44,59,59,59,59,45,29,29,59,75,75,74,90,90,74,59,59,60,44,44,90,59,59,59,60,60,59,59,59,75,75,59,75,75,59,59,59,60,90,90,59,75,75,74,57,74,75,74,74,75,59,59,59,74,74,74,60,59,59,59,59,90,90,59,59,29,59,59,59,90,106,59,59,59,44,59,59,59,29,29,59,60,59,59,59,90,90,74,90,59,59,74,75,75,59,59,58,75,43,59,59,59,59,59,59,59,59,60,91,91,60,60,60,60,59,44,44,59,60,59,59,59,74,74,59,60,59,59,59,59,44,44,44,59,59,59,59,59,44,44,44,74,44,59,59,59,44,44,44,59,45,60,60,74,60,60,74,59,59,75,60,60,74,74,74,74,44,44,44,45,14,60,60,29,59,59,59,44,60,60,75,29,29,60,60,60,75,60,60,45,45,59,59,44,74,74,59,28,74,74,60,59,59,59,44,74,74,60,75,75,75,60,60,59,59,59,75,75,75,75,60,45,45,59,74,45,45,29,59,59,75,43,43,76,60,60,60,44,44,29,74,74,60,59,59,59,45,59,60,60,60,74,44,44,75,75,75,60,90,90,59,58,75,60,60,60,74,74,74,59,44,74,74,44,75,75,59,74,43,60,59,74,74,60,91,91,45,60,60,74,43,60,60,45,29,29,75,59,59,59,60,59,59,59,59,59,90,90,75,59,59,60,74,74,74,60,60,45,74,74,59,45,44,44,60,75,59,59,59,59,59,59,59,75,59,59,59,59,59,60,74,74,74,59,59,74,59,59,59,58,44,44,74,59,59,60,45,29,29,59,59,75,75,74,75,75,44,74,74,75,59,59,59,45,59,59,59,59,59,59,29,30,30,74,90,90,75,28,45,59,59,59,74,59,59,59,60,60,73,30,30,30,29,75,75,44,44,58,58,74,59,75,75,75,59,60,60,74,59,59,59,44,74,74,59,60,60,14,74,43,60,60,43,60,44,45,45,74,45,59,59,59,59,59,59,58,59,75,75,75,74,59,59,74,74,74,74,73,73,59,44,44,0,0,0,119,73,90,90,74,90,73,44,60,60,90,105,75,75,74,90,74,74,74,60,59,74,44,74,59,89,59,74,59,59,60,90,89,89,90,59,29,59,29,44,75,58,75,90,89,74,74,44,60,44,45,74,74,59,89,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,44,44,44,44,29,29,76,76,59,59,59,89,89,60,60,29,29,60,60,44,60,74,74,59,59,29,29,44,44,14,44,45,76,45,29,60,61,61,90,60,62,74,30,59,59,44,44,44,59,59,61,61,75,75,90,75,60,30,44,44,43,45,74,29,29,29,74,74,44,44,91,76,89,44,45,30,29,61,44,44,30,45,44,59,44,59,75,74,60,59,59,60,45,75,75,44,45,59,59,59,29,60,44,59,29,74,45,44,30,29,74,29,60,60,60,60,60,75,75,59,30,74,44,74,44,44,58,58,30,30,44,44,29,44,44,44,89,59,74,74,60,88,29,29,44,29,29,74,45,28,28,14,44,28,45,29,60,60,44,44,44,29,30,74,58,59,29,60,60,60,60,59,59,59,59,60,60,60,60,44,89,89,74,59,74,59,44,44,45,29,59,45,30,30,60,60,45,45,29,29,75,75,59,60,59,60,90,75,44,44,75,77,75,77,75,60,60,91,76,29,59,59,75,30,60,30,45,44,30,44,44,74,77,58,59,30,30,29,29,29,76,76,29,29,60,30,59,59,59,59,29,29,29,29,44,44,44,44,44,44,44,44,44,60,59,44,61,45,89,75,29,30,59,30,90,45,29,74,44,44,76,44,61,44,43,30,43,59,-120,93,25,74,75,58,59,90,60,29,91,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,22,22,22,22,22,37,37,6,21,22,22,29,29,23,23,43,43,22,22,22,22,6,22,22,22,23,23,6,23,43,7,0,0,45,60,45,44,45,44,22,22,6,23,45,45,31,22,13,22,14,6,0,0,0,0,0,0,0,0,22,22,22,70,7,23,22,37,30,30,6,6,7,23,7,7,6,6,23,23,23,23,43,60,22,21,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,37,37,37,37,37,37,37,19,19,19,19,19,53,53,53,53,53,54,54,54,54,54,19,54,21,21,21,21,51,51,51,22,22,21,37,22,21,22,22,44,45,22,38,21,21,7,7,21,22,45,45,45,45,45,45,44,44,44,45,44,44,22,22,6,6,22,22,6,6,22,55,6,38,7,7,7,7,45,45,6,6,7,7,7,7,23,23,60,60,22,22,6,22,7,6,22,23,22,21,6,22,6,22,21,38,21,21,22,22,22,22,22,45,44,29,28,59,28,27,6,6,22,22,21,22,22,22,22,38,23,22,21,21,6,6,22,52,37,68,68,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,22,22,38,15,15,15,90,15,15,15,91,15,15,15,15,92,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,95,95,15,90,95,20,20,29,59,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,58,58,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,59,60,61,60,60,0,0,0,0,0,0,0,0,94,94,8,9,8,8,8,8,8,9,8,8,8,8,8,8,8,8,24,10,8,8,8,8,8,8,8,8,8,8,8,8,8,8,24,8,8,8,8,8,8,9,8,8,8,8,8,8,8,8,8,8,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,92,0,0,0,0,0,0,0,0,0,-120,105,75,106,75,73,106,76,90,90,90,74,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,45,45,59,76,59,60,60,60,60,61,44,45,59,45,44,60,59,45,75,60,60,45,59,44,76,75,75,44,44,60,45,59,29,60,45,60,45,45,90,74,59,58,58,90,58,58,60,-120,58,58,29,58,59,45,75,59,44,76,29,29,45,45,60,29,43,44,60,61,45,29,59,10,10,10,94,10,10,10,94,94,94,94,94,94,0,0,0,0,0,0,0,0,0,0,0,60,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47,27,74,61,60,60,59,46,60,46,44,44,44,47,29,45,31,31,31,60,62,44,60,46,46,46,28,45,14,44,30,30,45,45,30,76,61,45,30,45,45,93,95,95,95,94,95,10,10,94,93,95,10,93,95,0,0,0,0,0,0,0,0,0,59,43,45,93,45,60,30,44,30,77,44,29,90,95,0,0,75,43,46,46,29,57,76,77,45,45,0,0,43,-120,105,91,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,0,0,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,0,0,0,0,0,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,22,22,22,22,22,22,0,0,0,0,0,0,0,0,0,0,0,0,23,23,23,23,7,0,0,0,0,0,53,22,23,6,45,45,45,45,77,13,45,45,23,23,23,23,23,23,23,23,23,22,22,22,36,38,0,22,36,22,22,38,0,23,0,37,22,0,22,22,0,22,22,22,23,22,36,23,22,22,22,38,7,22,23,5,7,22,23,5,7,22,23,5,7,22,23,5,7,22,23,5,7,22,23,5,7,22,23,5,7,22,23,5,7,22,23,5,7,22,23,5,7,22,23,5,7,22,23,5,7,22,23,22,23,22,23,22,23,22,23,22,23,6,7,6,7,6,7,6,7,6,7,6,7,6,7,6,7,22,23,22,23,5,7,37,39,37,23,5,7,22,23,6,7,23,23,23,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,23,6,7,22,23,22,23,22,23,7,22,23,22,23,22,23,22,23,5,7,5,7,21,23,22,23,23,23,23,23,7,7,23,23,21,23,6,22,23,6,22,23,5,7,6,6,23,22,22,6,22,22,23,23,23,22,22,6,23,22,22,6,23,22,22,22,23,22,23,22,22,23,23,23,23,45,23,45,23,23,7,45,22,23,23,22,23,6,23,7,7,7,20,22,22,22,20,22,22,6,22,22,6,6,21,59,59,23,23,23,21,22,22,22,22,22,23,22,22,22,22,22,23,22,22,22,21,23,23,22,22,22,23,23,23,22,22,23,22,22,22,22,22,21,23,7,23,7,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,39,7,39,7,7,21,22,22,22,7,23,23,23,23,23,23,23,23,23,23,23,23,23,6,6,6,6,7,5,5,5,6,7,6,6,6,6,7,6,6,7,6,7,6,7,13,13,13,7,13,13,11,13,13,13,11,23,7,6,5,6,5,6,6,6,4,6,4,6,6,6,6,5,5,5,5,6,6,6,6,6,6,5,5,5,6,7,7,7,6,6,6,6,6,7,7,7,7,7,7,7,7,7,15,15,15,15,7,7,7,7,7,7,7,7,7,7,23,23,22,22,22,22,30,30,30,30,22,22,23,23,22,22,23,23,23,23,23,23,7,23,7,7,7,7,23,23,23,23,23,23,31,31,31,31,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,13,13,13,13,13,13,7,15,15,15,15,15,15,7,7,55,55,30,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,31,13,13,13,13,29,13,31,13,31,31,15,15,15,15,14,14,31,14,15,15,31,15,14,15,31,14,31,14,15,15,14,31,14,14,15,31,47,14,15,47,15,15,31,13,31,31,63,47,47,12,31,31,13,47,12,13,13,15,13,13,13,13,0,0,13,14,14,12,31,31,12,15,31,31,63,14,15,15,15,15,15,31,31,15,15,15,15,15,15,15,31,31,31,31,31,31,31,15,13,12,15,15,12,15,12,31,31,47,15,15,15,31,15,15,15,14,15,15,0,0,0,0,0,0,0,0,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,30,44,46,29,62,30,15,30,30,30,15,31,46,15,0,0,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,-51,-84,-83,-51,-51,-35,-115,45,45,-51,0,0,0,0,0,0,23,6,23,6,23,6,7,0,0,0,0,0,0,0,0,0,120,-120,-120,17,2,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,-100,-99,30,30,46,30,15,15,46,30,15,104,104,120,0,104,120,104,-120,89,120,120,121,104,121,104,89,105,104,89,105,121,104,105,0,105,89,105,105,0,0,0,0,37,7,38,53,37,0,37,7,21,7,37,7,21,7,37,7,37,23,7,36,23,22,23,53,23,22,23,5,7,68,39,22,23,5,7,38,39,22,23,5,7,22,23,5,7,22,23,6,7,22,23,5,7,22,23,5,7,22,23,22,23,22,23,22,23,23,23,6,7,23,23,6,7,23,23,6,7,23,23,6,7,22,23,6,7,22,23,6,7,22,23,6,7,22,23,6,7,22,23,5,7,22,23,5,7,22,23,6,7,22,23,5,7,38,39,6,7,22,23,5,7,37,39,6,7,22,23,22,23,22,23,5,7,6,7,22,23,38,23,38,23,0,0,15,0,87,90,44,59,29,30,104,-100,54,59,61,52,46,52,29,60,91,76,60,61,76,59,59,60,59,120,120,44,45,61,75,45,46,60,44,60,60,76,46,45,74,77,61,60,29,29,45,77,46,61,60,44,45,45,30,44,44,60,-116,29,55,89,15,104,76,75,74,75,74,75,75,76,121,73,59,104,61,76,75,75,75,91,91,58,76,59,45,59,76,75,-116,-120,55,45,60,60,19,54,37,20,52,22,22,22,22,22,22,22,22,37,22,37,7,6,22,7,7,22,7,22,7,22,7,23,7,6,22,22,7,7,7,54,7,7,22,7,22,7,22,22,7,7,7,37,7,6,6,7,7,22,22,38,7,39,22,22,23,36,36,15,22,21,23,22,39,39,22,21,37,22,6,6,6,6,7,6,21,22,6,23,21,7,21,21,7,21,22,22,22,6,0,0,0,36,36,36,36,36,21,0,0,36,21,21,22,6,21,0,0,21,21,37,22,37,21,0,0,22,36,68,0,0,0,59,47,45,60,-120,61,46,0,51,22,38,22,38,22,22,0,0,0,0,0,0,0,0,0,0,15,15,15,15,22,15,15],"non-unicode":"ÀÁÂÈÊËÍÓÔÕÚßãõğİıŒœŞşŴŵžȇ\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜø£Ø׃áíóúñѪº¿®¬½¼¡«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀αβΓπΣσμτΦΘΩδ∞∅∈∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■\u0000"}
diff --git a/sponge/src/main/java/com/griefdefender/internal/provider/GDActor.java b/sponge/src/main/java/com/griefdefender/internal/provider/GDActor.java
new file mode 100644
index 0000000..ccefac6
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/internal/provider/GDActor.java
@@ -0,0 +1,231 @@
+/*
+ * This file is part of GriefPrevention, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (C) sk89q <http://www.sk89q.com>
+ * Copyright (C) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.internal.provider;
+
+import com.griefdefender.GriefDefenderPlugin;
+import com.sk89q.util.StringUtil;
+import com.sk89q.worldedit.Vector;
+import com.sk89q.worldedit.WorldVector;
+import com.sk89q.worldedit.entity.BaseEntity;
+import com.sk89q.worldedit.extension.platform.AbstractPlayerActor;
+import com.sk89q.worldedit.extent.inventory.BlockBag;
+import com.sk89q.worldedit.internal.LocalWorldAdapter;
+import com.sk89q.worldedit.internal.cui.CUIEvent;
+import com.sk89q.worldedit.session.SessionKey;
+import com.sk89q.worldedit.util.Location;
+import io.netty.buffer.Unpooled;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.network.PacketBuffer;
+import net.minecraft.network.play.server.SPacketCustomPayload;
+import net.minecraft.util.EnumHand;
+import net.minecraft.util.text.TextComponentString;
+import net.minecraft.util.text.TextFormatting;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.world.World;
+
+import java.nio.charset.Charset;
+import java.util.UUID;
+
+import javax.annotation.Nullable;
+
+public class GDActor extends AbstractPlayerActor {
+
+    private final EntityPlayerMP player;
+    public static final Charset UTF_8_CHARSET = Charset.forName("UTF-8");
+    public static final String CUI_PLUGIN_CHANNEL = "WECUI";
+
+    public GDActor(Player player) {
+        this.player = (EntityPlayerMP) player;
+    }
+
+    @Override
+    public UUID getUniqueId() {
+        return this.player.getUniqueID();
+    }
+
+    @Override
+    public int getItemInHand() {
+        ItemStack is = this.player.getHeldItem(EnumHand.MAIN_HAND);
+        return is == null ? 0 : Item.getIdFromItem(is.getItem());
+    }
+
+    @Override
+    public String getName() {
+        return this.player.getName();
+    }
+
+    @Override
+    public BaseEntity getState() {
+        throw new UnsupportedOperationException("Cannot create a state from this object");
+    }
+
+    @Override
+    public Location getLocation() {
+        return new Location(
+                this.getWorld(),
+                new Vector(this.player.posX, this.player.posY, this.player.posZ),
+                this.player.rotationYaw,
+                this.player.rotationPitch);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Override
+    public WorldVector getPosition() {
+        return new WorldVector(LocalWorldAdapter.adapt(this.getWorld()), this.player.posX, this.player.posY, this.player.posZ);
+    }
+
+    @Override
+    public com.sk89q.worldedit.world.World getWorld() {
+        return GriefDefenderPlugin.getInstance().worldEditProvider.getWorld((World) this.player.getEntityWorld()) ;
+    }
+
+    @Override
+    public double getPitch() {
+        return this.player.rotationPitch;
+    }
+
+    @Override
+    public double getYaw() {
+        return this.player.rotationYaw;
+    }
+
+    @Override
+    public void giveItem(int type, int amt) {
+        this.player.inventory.addItemStackToInventory(new ItemStack(Item.getItemById(type), amt, 0));
+    }
+
+    @Override
+    public void dispatchCUIEvent(CUIEvent event) {
+        String[] params = event.getParameters();
+        String send = event.getTypeId();
+        if (params.length > 0) {
+            send = send + "|" + StringUtil.joinString(params, "|");
+        }
+
+        PacketBuffer buffer = new PacketBuffer(Unpooled.copiedBuffer(send.getBytes(UTF_8_CHARSET)));
+        SPacketCustomPayload packet = new SPacketCustomPayload(CUI_PLUGIN_CHANNEL, buffer);
+        this.player.connection.sendPacket(packet);
+    }
+
+    @Override
+    public void printRaw(String msg) {
+        for (String part : msg.split("\n")) {
+            this.player.sendMessage(new TextComponentString(part));
+        }
+    }
+
+    @Override
+    public void printDebug(String msg) {
+        sendColorized(msg, TextFormatting.GRAY);
+    }
+
+    @Override
+    public void print(String msg) {
+        sendColorized(msg, TextFormatting.LIGHT_PURPLE);
+    }
+
+    @Override
+    public void printError(String msg) {
+        sendColorized(msg, TextFormatting.RED);
+    }
+
+    private void sendColorized(String msg, TextFormatting formatting) {
+        for (String part : msg.split("\n")) {
+            TextComponentString component = new TextComponentString(part);
+            component.getStyle().setColor(formatting);
+            this.player.sendMessage(component);
+        }
+    }
+
+    @Override
+    public void setPosition(Vector pos, float pitch, float yaw) {
+        this.player.connection.setPlayerLocation(pos.getX(), pos.getY(), pos.getZ(), yaw, pitch);
+    }
+
+    @Override
+    public String[] getGroups() {
+        return new String[]{};
+    }
+
+    @Override
+    public BlockBag getInventoryBlockBag() {
+        return null;
+    }
+
+    @Override
+    public boolean hasPermission(String perm) {
+        return ((Player) this.player).hasPermission(perm);
+    }
+
+    @Nullable
+    @Override
+    public <T> T getFacet(Class<? extends T> cls) {
+        return null;
+    }
+
+    @Override
+    public SessionKey getSessionKey() {
+        return new SessionKeyImpl(this.player.getUniqueID(), this.player.getName());
+    }
+
+    private static class SessionKeyImpl implements SessionKey {
+        // If not static, this will leak a reference
+
+        private final UUID uuid;
+        private final String name;
+
+        private SessionKeyImpl(UUID uuid, String name) {
+            this.uuid = uuid;
+            this.name = name;
+        }
+
+        @Override
+        public UUID getUniqueId() {
+            return this.uuid;
+        }
+
+        @Nullable
+        @Override
+        public String getName() {
+            return this.name;
+        }
+
+        @Override
+        public boolean isActive() {
+            return Sponge.getServer().getPlayer(this.uuid).isPresent();
+        }
+
+        @Override
+        public boolean isPersistent() {
+            return true;
+        }
+
+    }
+
+}
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/internal/provider/WorldEditProvider.java b/sponge/src/main/java/com/griefdefender/internal/provider/WorldEditProvider.java
new file mode 100644
index 0000000..76ae676
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/internal/provider/WorldEditProvider.java
@@ -0,0 +1,359 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.internal.provider;
+
+import com.flowpowered.math.vector.Vector3i;
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GDTimings;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.GriefDefender;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.ClaimResult;
+import com.griefdefender.api.claim.ClaimResultType;
+import com.griefdefender.api.permission.option.type.CreateModeType;
+import com.griefdefender.api.permission.option.type.CreateModeTypes;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.claim.GDClaimManager;
+import com.griefdefender.claim.GDClaimSchematic;
+import com.griefdefender.command.CommandHelper;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.event.GDCauseStackManager;
+import com.griefdefender.internal.provider.worldedit.cui.MultiSelectionColors;
+import com.griefdefender.internal.provider.worldedit.cui.event.MultiSelectionClearEvent;
+import com.griefdefender.internal.provider.worldedit.cui.event.MultiSelectionColorEvent;
+import com.griefdefender.internal.provider.worldedit.cui.event.MultiSelectionCuboidEvent;
+import com.griefdefender.internal.provider.worldedit.cui.event.MultiSelectionGridEvent;
+import com.griefdefender.internal.provider.worldedit.cui.event.MultiSelectionPointEvent;
+import com.griefdefender.internal.util.BlockUtil;
+import com.griefdefender.storage.FileStorage;
+import com.griefdefender.util.PlayerUtil;
+import com.sk89q.worldedit.IncompleteRegionException;
+import com.sk89q.worldedit.LocalSession;
+import com.sk89q.worldedit.Vector;
+import com.sk89q.worldedit.WorldEdit;
+import com.sk89q.worldedit.regions.Region;
+import com.sk89q.worldedit.regions.RegionSelector;
+import com.sk89q.worldedit.regions.selector.CuboidRegionSelector;
+import com.sk89q.worldedit.world.World;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import net.kyori.text.format.TextColor;
+import org.spongepowered.api.data.DataContainer;
+import org.spongepowered.api.data.persistence.DataFormats;
+import org.spongepowered.api.data.persistence.DataTranslators;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.world.Location;
+import org.spongepowered.api.world.schematic.Schematic;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.zip.GZIPInputStream;
+
+public class WorldEditProvider {
+
+    private final WorldEdit worldEditService;
+    private Map<UUID, GDActor> worldEditPlayers = new HashMap<>();
+    private final Map<UUID, Path> schematicWorldMap = new HashMap<>();
+
+    public WorldEditProvider() {
+        this.worldEditService = WorldEdit.getInstance();
+    }
+
+    public WorldEdit getWorldEditService() {
+        return this.worldEditService;
+    }
+
+    public LocalSession getLocalSession(String playerName) {
+        return WorldEdit.getInstance().getSessionManager().findByName(playerName);
+    }
+
+    public World getWorld(String playerName) {
+        final LocalSession session = getLocalSession(playerName);
+        if (session == null) {
+            return null;
+        }
+
+        return session.getSelectionWorld();
+    }
+
+    public World getWorld(org.spongepowered.api.world.World spongeWorld) {
+        for (World world : this.worldEditService.getServer().getWorlds()) {
+            if (world.getName().equals(spongeWorld.getName())) {
+                return world;
+            }
+        }
+
+        return null;
+    }
+
+    public RegionSelector getRegionSelector(Player player) {
+        final LocalSession session = getLocalSession(player.getName());
+        if (session == null) {
+            return null;
+        }
+
+        World world = session.getSelectionWorld();
+        if (world == null) {
+            world = this.getWorld(player.getWorld());
+        }
+        return session.getRegionSelector(world);
+    }
+
+    public Vector createVector(Vector3i point) {
+        return new Vector(point.getX(), point.getY(), point.getZ());
+    }
+
+    public GDActor getOrCreateActor(Player player) {
+        if (this.worldEditPlayers.containsKey(player.getUniqueId())) {
+            return this.worldEditPlayers.get(player.getUniqueId());
+        }
+
+        final GDActor actor = new GDActor(player);
+        this.worldEditPlayers.put(player.getUniqueId(), actor);
+        return actor;
+    }
+
+    public void removePlayer(Player player) {
+        this.worldEditPlayers.remove(player.getUniqueId());
+    }
+
+    public void visualizeClaim(Claim claim, Player player, GDPlayerData playerData, boolean investigating) {
+        this.visualizeClaim(claim, claim.getLesserBoundaryCorner(), claim.getGreaterBoundaryCorner(), player, playerData, investigating);
+    }
+
+    public void visualizeClaim(Claim claim, Vector3i pos1, Vector3i pos2, Player player, GDPlayerData playerData, boolean investigating) {
+        // revert any current visuals if investigating
+        if (investigating) {
+            this.revertVisuals(player, playerData, null);
+        }
+        final LocalSession session = this.getLocalSession(player.getName());
+        if (session == null || !session.hasCUISupport()) {
+            return;
+        }
+        final Vector point1 = this.createVector(pos1);
+        final Vector point2 = this.createVector(pos2);
+        final CuboidRegionSelector regionSelector = new CuboidRegionSelector(session.getSelectionWorld(), point1, point2);
+        final GDActor actor = this.getOrCreateActor(player);
+        session.setRegionSelector(this.getWorld(player.getWorld()), regionSelector);
+        actor.dispatchCUIEvent(new MultiSelectionCuboidEvent(claim.getUniqueId()));
+        actor.dispatchCUIEvent(new MultiSelectionPointEvent(0, point1, regionSelector.getArea()));
+        if (playerData.claimResizing != null) {
+            actor.dispatchCUIEvent(new MultiSelectionPointEvent(1));
+        } else {
+            actor.dispatchCUIEvent(new MultiSelectionPointEvent(1, point2, regionSelector.getArea()));
+        }
+
+        if (investigating || playerData.lastShovelLocation == null) {
+            actor.dispatchCUIEvent(new MultiSelectionColorEvent(MultiSelectionColors.RED, MultiSelectionColors.getClaimColor(claim), "", ""));
+        }
+        actor.dispatchCUIEvent(new MultiSelectionGridEvent(10));
+    }
+
+    public void visualizeClaims(Set<Claim> claims, Player player, GDPlayerData playerData, boolean investigating) {
+        for (Claim claim : claims) {
+            if (((GDClaim) claim).children.size() > 0) {
+                visualizeClaims(claim.getChildren(true), player, playerData, investigating);
+            }
+            final LocalSession session = this.getLocalSession(player.getName());
+            if (session == null || !session.hasCUISupport()) {
+                return;
+            }
+            final Vector point1 = this.createVector(claim.getLesserBoundaryCorner());
+            final Vector point2 = this.createVector(claim.getGreaterBoundaryCorner());
+            final CuboidRegionSelector regionSelector = new CuboidRegionSelector(session.getSelectionWorld(), point1, point2);
+            final GDActor actor = this.getOrCreateActor(player);
+            //session.setRegionSelector(this.getWorld(player.getWorld()), regionSelector);
+            actor.dispatchCUIEvent(new MultiSelectionCuboidEvent(claim.getUniqueId()));
+            actor.dispatchCUIEvent(new MultiSelectionPointEvent(0, point1, regionSelector.getArea()));
+            if (playerData.claimResizing != null) {
+                actor.dispatchCUIEvent(new MultiSelectionPointEvent(1));
+            } else {
+                actor.dispatchCUIEvent(new MultiSelectionPointEvent(1, point2, regionSelector.getArea()));
+            }
+            if (investigating) {
+                actor.dispatchCUIEvent(new MultiSelectionColorEvent(MultiSelectionColors.RED, MultiSelectionColors.getClaimColor(claim), "", ""));
+            }
+            actor.dispatchCUIEvent(new MultiSelectionGridEvent(10));
+        }
+    }
+
+    public void revertVisuals(Player player, GDPlayerData playerData, UUID claimUniqueId) {
+        final LocalSession session = this.getLocalSession(player.getName());
+        if (session == null || !session.hasCUISupport()) {
+            return;
+        }
+        final World world = this.getWorld(player.getWorld());
+        final RegionSelector region = session.getRegionSelector(world);
+        final GDActor actor = this.getOrCreateActor(player);
+        region.clear();
+        session.dispatchCUISelection(actor);
+        if (claimUniqueId != null) {
+            actor.dispatchCUIEvent(new MultiSelectionClearEvent(claimUniqueId));
+        } else {
+            actor.dispatchCUIEvent(new MultiSelectionClearEvent());
+        }
+    }
+
+    public void stopVisualDrag(Player player) {
+        final GDActor actor = this.getOrCreateActor(player);
+        actor.dispatchCUIEvent(new MultiSelectionClearEvent(player.getUniqueId()));
+    }
+
+    public void sendVisualDrag(Player player, GDPlayerData playerData, Vector3i pos) {
+        final LocalSession session = this.getLocalSession(player.getName());
+        if (session == null || !session.hasCUISupport()) {
+            return;
+        }
+
+        final Location<org.spongepowered.api.world.World> location = BlockUtil.getInstance().getTargetBlock(player, playerData, 60, true).orElse(null);
+        Vector point1 = null;
+        if (playerData.claimResizing != null) {
+            // get opposite corner
+            final GDClaim claim = playerData.claimResizing;
+            final int x = playerData.lastShovelLocation.getBlockX() == claim.lesserBoundaryCorner.getX() ? claim.greaterBoundaryCorner.getX() : claim.lesserBoundaryCorner.getX();
+            final int y = playerData.lastShovelLocation.getBlockY() == claim.lesserBoundaryCorner.getY() ? claim.greaterBoundaryCorner.getY() : claim.lesserBoundaryCorner.getY();
+            final int z = playerData.lastShovelLocation.getBlockZ() == claim.lesserBoundaryCorner.getZ() ? claim.greaterBoundaryCorner.getZ() : claim.lesserBoundaryCorner.getZ();
+            point1 = new Vector(x, y, z);
+        } else {
+            point1 = this.createVector(pos);
+        }
+        Vector point2 = null;
+        if (location == null) {
+            point2 = new Vector(player.getLocation().getBlockX(), player.getLocation().getBlockY(), player.getLocation().getBlockZ());
+        } else {
+            point2 = this.createVector(location.getBlockPosition());
+        }
+
+        final CuboidRegionSelector regionSelector = new CuboidRegionSelector(session.getSelectionWorld(), point1, point2);
+        final GDActor actor = this.getOrCreateActor(player);
+        actor.dispatchCUIEvent(new MultiSelectionCuboidEvent(player.getUniqueId()));
+        actor.dispatchCUIEvent(new MultiSelectionPointEvent(0, point1, regionSelector.getArea()));
+        actor.dispatchCUIEvent(new MultiSelectionPointEvent(1));
+    }
+
+    public boolean hasCUISupport(Player player) {
+        return hasCUISupport(player.getName());
+    }
+
+    public boolean hasCUISupport(String name) {
+        final LocalSession session = this.getLocalSession(name);
+        if (session == null || !session.hasCUISupport()) {
+            return false;
+        }
+
+        return true;
+    }
+
+    public void createClaim(Player player) {
+        RegionSelector regionSelector = null;
+        Region region = null;
+        try {
+            regionSelector = GriefDefenderPlugin.getInstance().worldEditProvider.getRegionSelector(player);
+            region = regionSelector.getRegion();
+        } catch (IncompleteRegionException e) {
+            TextAdapter.sendComponent(player, TextComponent.of("Could not find a worldedit selection.", TextColor.RED));
+            return;
+        }
+
+        final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        final int minY = playerData.getClaimCreateMode() == CreateModeTypes.VOLUME ? region.getMinimumPoint().getBlockY() : 0;
+        final int maxY = playerData.getClaimCreateMode() == CreateModeTypes.VOLUME ? region.getMaximumPoint().getBlockY() : 255;
+        final Vector3i lesser = new Vector3i(region.getMinimumPoint().getBlockX(), minY, region.getMinimumPoint().getBlockZ());
+        final Vector3i greater = new Vector3i(region.getMaximumPoint().getBlockX(), maxY, region.getMaximumPoint().getBlockZ());
+        GDCauseStackManager.getInstance().pushCause(player);
+        final ClaimResult result = GriefDefender.getRegistry().createBuilder(Claim.Builder.class)
+            .bounds(lesser, greater)
+            .cuboid(playerData.getClaimCreateMode() == CreateModeTypes.VOLUME)
+            .owner(player.getUniqueId())
+            .sizeRestrictions(true)
+            .type(PlayerUtil.getInstance().getClaimTypeFromShovel(playerData.shovelMode))
+            .world(player.getWorld().getUniqueId())
+            .build();
+        GDCauseStackManager.getInstance().popCause();
+        if (result.successful()) {
+            final Claim claim = result.getClaim().get();
+            final GDClaimManager claimManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(player.getWorld().getUniqueId());
+            claimManager.addClaim(claim, true);
+            final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.CREATE_SUCCESS, ImmutableMap.of(
+                    "type", ((GDClaim) claim).getFriendlyNameType(true)));
+            GriefDefenderPlugin.sendMessage(player, message);
+        } else {
+            if (result.getResultType() == ClaimResultType.OVERLAPPING_CLAIM) {
+                GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().CREATE_OVERLAP_SHORT);
+                Set<Claim> claims = new HashSet<>();
+                claims.add(result.getClaim().get());
+                CommandHelper.showClaims(player, claims, 0, true);
+                GDTimings.PLAYER_HANDLE_SHOVEL_ACTION.stopTimingIfSync();
+            }
+        }
+    }
+
+    public Map<UUID, Path> getSchematicWorldMap() {
+        return this.schematicWorldMap;
+    }
+
+    public void loadSchematics(org.spongepowered.api.world.World world) {
+        GDClaimManager claimWorldManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(world.getUniqueId());
+        GriefDefenderPlugin.getInstance().executor.execute(() -> {
+            for (Claim claim : claimWorldManager.getWorldClaims()) {
+                Path path = this.schematicWorldMap.get(claim.getWorldUniqueId()).resolve(claim.getUniqueId().toString());
+                if (!Files.exists(path)) {
+                    try {
+                        Files.createDirectories(path);
+                    } catch (IOException e) {
+                        e.printStackTrace();
+                    }
+                    return;
+                }
+        
+                File files[] = path.toFile().listFiles();
+                for (File file : files) {
+                    DataContainer schematicData = null;
+                    Schematic schematic = null;
+                    try {
+                        schematicData = DataFormats.NBT.readFrom(new GZIPInputStream(new FileInputStream(file)));
+                        schematic = DataTranslators.SCHEMATIC.translate(schematicData);
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                        continue;
+                    }
+                    GDClaimSchematic claimSchematic = new GDClaimSchematic(claim, schematic, schematic.METADATA_NAME);
+                    ((GDClaim) claim).schematics.put(file.getName(), claimSchematic);
+                }
+            }
+        });
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/internal/provider/worldedit/cui/MultiSelectionColors.java b/sponge/src/main/java/com/griefdefender/internal/provider/worldedit/cui/MultiSelectionColors.java
new file mode 100644
index 0000000..abe0168
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/internal/provider/worldedit/cui/MultiSelectionColors.java
@@ -0,0 +1,48 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.internal.provider.worldedit.cui;
+
+import com.griefdefender.api.claim.Claim;
+
+public class MultiSelectionColors {
+
+    public static final String RED = "#990000";
+    public static final String GREEN = "#5AC25A";
+    public static final String YELLOW = "#EAEA32";
+    public static final String GRAY = "#D2D2D2";
+
+    public static String getClaimColor(Claim claim) {
+        if (claim.isSubdivision()) {
+            return GRAY;
+        }
+        if (claim.isAdminClaim()) {
+            return RED;
+        }
+        if (claim.isTown()) {
+            return GREEN;
+        }
+        return YELLOW;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/internal/provider/worldedit/cui/MultiSelectionType.java b/sponge/src/main/java/com/griefdefender/internal/provider/worldedit/cui/MultiSelectionType.java
new file mode 100644
index 0000000..51023b5
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/internal/provider/worldedit/cui/MultiSelectionType.java
@@ -0,0 +1,36 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.internal.provider.worldedit.cui;
+
+public class MultiSelectionType {
+
+    public static final String COLOR = "+col";
+    public static final String GRID = "+grid";
+    public static final String GRID_CULL = "cull";
+    public static final String POINT = "+p";
+    public static final String SELECTION = "+s";
+    public static final String SELECTION_CLEAR = "+s|clear";
+    public static final String SELECTION_CUBOID = "+s|cuboid";
+}
diff --git a/sponge/src/main/java/com/griefdefender/internal/provider/worldedit/cui/event/MultiSelectionClearEvent.java b/sponge/src/main/java/com/griefdefender/internal/provider/worldedit/cui/event/MultiSelectionClearEvent.java
new file mode 100644
index 0000000..ca7813b
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/internal/provider/worldedit/cui/event/MultiSelectionClearEvent.java
@@ -0,0 +1,54 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.internal.provider.worldedit.cui.event;
+
+import com.griefdefender.internal.provider.worldedit.cui.MultiSelectionType;
+import com.sk89q.worldedit.internal.cui.CUIEvent;
+
+import java.util.UUID;
+
+public class MultiSelectionClearEvent implements CUIEvent {
+
+    private final String[] parameters;
+
+    public MultiSelectionClearEvent() {
+        this.parameters = new String[] {};
+    }
+
+    public MultiSelectionClearEvent(UUID uniqueId) {
+        this.parameters = new String[] {
+                uniqueId.toString()};
+    }
+    @Override
+    public String[] getParameters() {
+        return this.parameters;
+    }
+
+    @Override
+    public String getTypeId() {
+        return MultiSelectionType.SELECTION_CLEAR;
+    }
+
+}
diff --git a/sponge/src/main/java/com/griefdefender/internal/provider/worldedit/cui/event/MultiSelectionColorEvent.java b/sponge/src/main/java/com/griefdefender/internal/provider/worldedit/cui/event/MultiSelectionColorEvent.java
new file mode 100644
index 0000000..67c108e
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/internal/provider/worldedit/cui/event/MultiSelectionColorEvent.java
@@ -0,0 +1,60 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.internal.provider.worldedit.cui.event;
+
+import com.griefdefender.internal.provider.worldedit.cui.MultiSelectionType;
+import com.sk89q.worldedit.internal.cui.CUIEvent;
+
+public class MultiSelectionColorEvent implements CUIEvent {
+
+    private final String edgeColor;
+    private final String gridColor;
+    private final String p1Color;
+    private final String p2Color;
+    private final String[] parameters;
+
+    public MultiSelectionColorEvent(String edgeColor, String gridColor, String p1Color, String p2Color) {
+        this.edgeColor = edgeColor;
+        this.gridColor = gridColor;
+        this.p1Color = p1Color;
+        this.p2Color = p2Color;
+        this.parameters = new String[] {
+                this.edgeColor,
+                this.gridColor,
+                this.p1Color,
+                this.p2Color};
+    }
+
+    @Override
+    public String getTypeId() {
+        return MultiSelectionType.COLOR;
+    }
+
+    @Override
+    public String[] getParameters() {
+        return this.parameters;
+    }
+
+}
diff --git a/sponge/src/main/java/com/griefdefender/internal/provider/worldedit/cui/event/MultiSelectionCuboidEvent.java b/sponge/src/main/java/com/griefdefender/internal/provider/worldedit/cui/event/MultiSelectionCuboidEvent.java
new file mode 100644
index 0000000..8316ce6
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/internal/provider/worldedit/cui/event/MultiSelectionCuboidEvent.java
@@ -0,0 +1,54 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.internal.provider.worldedit.cui.event;
+
+import com.griefdefender.internal.provider.worldedit.cui.MultiSelectionType;
+import com.sk89q.worldedit.internal.cui.CUIEvent;
+
+import java.util.UUID;
+
+public class MultiSelectionCuboidEvent implements CUIEvent {
+
+    protected final UUID uniqueId;
+    private final String[] parameters;
+
+    public MultiSelectionCuboidEvent(UUID uniqueId) {
+        this.uniqueId = uniqueId;
+        this.parameters = new String[] {
+                String.valueOf(this.uniqueId)
+            };
+    }
+
+    @Override
+    public String getTypeId() {
+        return MultiSelectionType.SELECTION_CUBOID;
+    }
+
+    @Override
+    public String[] getParameters() {
+        return this.parameters;
+    }
+
+}
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/internal/provider/worldedit/cui/event/MultiSelectionGridEvent.java b/sponge/src/main/java/com/griefdefender/internal/provider/worldedit/cui/event/MultiSelectionGridEvent.java
new file mode 100644
index 0000000..65dabc0
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/internal/provider/worldedit/cui/event/MultiSelectionGridEvent.java
@@ -0,0 +1,49 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.internal.provider.worldedit.cui.event;
+
+import com.griefdefender.internal.provider.worldedit.cui.MultiSelectionType;
+import com.sk89q.worldedit.internal.cui.CUIEvent;
+
+public class MultiSelectionGridEvent implements CUIEvent {
+
+    final String[] parameters;
+
+    public MultiSelectionGridEvent(double spacing) {
+        this.parameters = new String[] { String.valueOf(spacing),
+                MultiSelectionType.GRID_CULL};
+    }
+
+    @Override
+    public String[] getParameters() {
+        return this.parameters;
+    }
+
+    @Override
+    public String getTypeId() {
+        return MultiSelectionType.GRID;
+    }
+
+}
diff --git a/sponge/src/main/java/com/griefdefender/internal/provider/worldedit/cui/event/MultiSelectionPointEvent.java b/sponge/src/main/java/com/griefdefender/internal/provider/worldedit/cui/event/MultiSelectionPointEvent.java
new file mode 100644
index 0000000..3bd30aa
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/internal/provider/worldedit/cui/event/MultiSelectionPointEvent.java
@@ -0,0 +1,65 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.internal.provider.worldedit.cui.event;
+
+import com.griefdefender.internal.provider.worldedit.cui.MultiSelectionType;
+import com.sk89q.worldedit.Vector;
+import com.sk89q.worldedit.internal.cui.CUIEvent;
+
+public class MultiSelectionPointEvent implements CUIEvent {
+
+    private final String TILDE = "~";
+    private final String[] parameters;
+
+    // Used to force WECUI to follow player EYE
+    public MultiSelectionPointEvent(int id) {
+        this.parameters = new String[] { 
+                String.valueOf(id),
+                TILDE,
+                TILDE,
+                TILDE,
+                String.valueOf(0)};
+    }
+
+    public MultiSelectionPointEvent(int id, Vector pos, int area) {
+        this.parameters = new String[] { 
+                String.valueOf(id),
+                String.valueOf(pos.getX()),
+                String.valueOf(pos.getY()),
+                String.valueOf(pos.getZ()),
+                String.valueOf(area)};
+    }
+
+    @Override
+    public String getTypeId() {
+        return MultiSelectionType.POINT;
+    }
+
+    @Override
+    public String[] getParameters() {
+        return this.parameters;
+    }
+
+}
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/internal/registry/BlockTypeRegistryModule.java b/sponge/src/main/java/com/griefdefender/internal/registry/BlockTypeRegistryModule.java
new file mode 100644
index 0000000..19614f7
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/internal/registry/BlockTypeRegistryModule.java
@@ -0,0 +1,69 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) SpongePowered <https://www.spongepowered.org>
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.internal.registry;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.collect.ImmutableList;
+import org.spongepowered.api.block.BlockType;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Optional;
+
+// internal
+public final class BlockTypeRegistryModule {
+
+    protected final Map<String, GDBlockType> blockTypeMappings = new HashMap<>();
+    private final org.spongepowered.common.registry.type.BlockTypeRegistryModule SPONGE_REGISTRY = org.spongepowered.common.registry.type.BlockTypeRegistryModule.getInstance();
+
+    public static BlockTypeRegistryModule getInstance() {
+        return Holder.INSTANCE;
+    }
+
+    public Optional<GDBlockType> getById(String id) {
+        if (!checkNotNull(id).contains(":")) {
+            id = "minecraft:" + id;
+        }
+        return Optional.ofNullable(this.blockTypeMappings.get(id.toLowerCase(Locale.ENGLISH)));
+    }
+
+    public Collection<GDBlockType> getAll() {
+        return ImmutableList.copyOf(this.blockTypeMappings.values());
+    }
+
+    public void registerDefaults() {
+        for (BlockType type : SPONGE_REGISTRY.getAll()) {
+            this.blockTypeMappings.put(type.getId(), new GDBlockType(type));
+        }
+    }
+
+    private static final class Holder {
+
+        static final BlockTypeRegistryModule INSTANCE = new BlockTypeRegistryModule();
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/internal/registry/EntityTypeRegistryModule.java b/sponge/src/main/java/com/griefdefender/internal/registry/EntityTypeRegistryModule.java
new file mode 100644
index 0000000..dcf7e87
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/internal/registry/EntityTypeRegistryModule.java
@@ -0,0 +1,90 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.internal.registry;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import com.google.common.collect.ImmutableList;
+import net.minecraft.entity.EnumCreatureType;
+import org.spongepowered.api.entity.EntityType;
+import org.spongepowered.api.entity.living.Living;
+import org.spongepowered.common.entity.SpongeEntityType;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Optional;
+
+public final class EntityTypeRegistryModule {
+
+    protected final Map<String, GDEntityType> entityTypeMappings = new HashMap<>();
+    public final BiMap<String, EnumCreatureType> SPAWN_TYPES = HashBiMap.create();
+    private final org.spongepowered.common.registry.type.entity.EntityTypeRegistryModule SPONGE_REGISTRY = org.spongepowered.common.registry.type.entity.EntityTypeRegistryModule.getInstance();
+
+    public static EntityTypeRegistryModule getInstance() {
+        return Holder.INSTANCE;
+    }
+
+    public Optional<GDEntityType> getById(String id) {
+        if (!checkNotNull(id).contains(":")) {
+            id = "minecraft:" + id;
+        }
+        return Optional.ofNullable(this.entityTypeMappings.get(id.toLowerCase(Locale.ENGLISH)));
+    }
+
+    public Collection<GDEntityType> getAll() {
+        return ImmutableList.copyOf(this.entityTypeMappings.values());
+    }
+
+    public String getFriendlyCreatureTypeName(Living entity) {
+        return SPAWN_TYPES.inverse().get(((SpongeEntityType) entity.getType()).getEnumCreatureType());
+    }
+
+    public String getFriendlyCreatureTypeName(EnumCreatureType type) {
+        return SPAWN_TYPES.inverse().get(type);
+    }
+
+    public EnumCreatureType getCreatureTypeByName(String name) {
+        return SPAWN_TYPES.get(name);
+    }
+
+    public void registerDefaults() {
+        for (EntityType type : SPONGE_REGISTRY.getAll()) {
+            this.entityTypeMappings.put(type.getId(), new GDEntityType(type));
+        }
+        SPAWN_TYPES.put("animal", EnumCreatureType.CREATURE);
+        SPAWN_TYPES.put("ambient", EnumCreatureType.AMBIENT);
+        SPAWN_TYPES.put("aquatic", EnumCreatureType.WATER_CREATURE);
+        SPAWN_TYPES.put("monster", EnumCreatureType.MONSTER);
+    }
+
+    private static final class Holder {
+
+        static final EntityTypeRegistryModule INSTANCE = new EntityTypeRegistryModule();
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/internal/registry/GDBlockType.java b/sponge/src/main/java/com/griefdefender/internal/registry/GDBlockType.java
new file mode 100644
index 0000000..45dc80f
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/internal/registry/GDBlockType.java
@@ -0,0 +1,50 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.internal.registry;
+
+import com.griefdefender.api.CatalogType;
+import org.spongepowered.api.block.BlockType;
+
+public class GDBlockType implements CatalogType {
+
+    private BlockType type;
+
+    public GDBlockType(BlockType type) {
+        this.type = type;
+    }
+
+    @Override
+    public String getName() {
+        return this.type.getName();
+    }
+
+    public String getId() {
+        return this.type.getId();
+    }
+
+    public BlockType getType() {
+        return this.type;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/internal/registry/GDEntityType.java b/sponge/src/main/java/com/griefdefender/internal/registry/GDEntityType.java
new file mode 100644
index 0000000..df6c08e
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/internal/registry/GDEntityType.java
@@ -0,0 +1,104 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.internal.registry;
+
+import com.griefdefender.api.CatalogType;
+import com.griefdefender.api.permission.Context;
+import net.minecraft.entity.EnumCreatureType;
+import org.spongepowered.api.entity.EntityType;
+import org.spongepowered.common.entity.SpongeEntityType;
+
+import javax.annotation.Nullable;
+
+public class GDEntityType implements CatalogType {
+
+    public final int entityTypeId;
+    public final String modId;
+    public final EntityType type;
+    public EnumCreatureType creatureType;
+
+    public GDEntityType(EntityType type) {
+        this.type = type;
+        this.modId = ((SpongeEntityType) this.type).modId;
+        this.entityTypeId = ((SpongeEntityType) type).entityTypeId;
+        this.creatureType = ((SpongeEntityType) type).getEnumCreatureType();
+    }
+
+    @Override
+    public String getName() {
+        return this.type.getName();
+    }
+
+    public String getId() {
+        return this.type.getId();
+    }
+
+    public String getModId() {
+        return this.modId;
+    }
+
+    @Nullable
+    public String getEnumCreatureTypeId() {
+        if (this.creatureType == null) {
+            return null;
+        }
+        switch (this.creatureType) {
+            case CREATURE:
+                return this.modId + ":animal";
+            case WATER_CREATURE:
+                return this.modId + ":aquatic";
+            default:
+                break;
+        }
+        return this.modId + ":" + this.creatureType.name().toLowerCase();
+    }
+
+    @Nullable
+    public Context getEnumCreatureTypeContext(boolean isSource) {
+        if (this.creatureType == null) {
+            return null;
+        }
+
+        final String contextKey = isSource ? "source" : "target";
+        switch (this.creatureType) {
+            case CREATURE:
+                return new Context(contextKey, "#" + this.modId + ":animal");
+            case WATER_CREATURE:
+                return new Context(contextKey, "#" + this.modId + ":aquatic");
+            default:
+                break;
+        }
+        return new Context(contextKey, "#" + this.modId + ":" + this.creatureType.name().toLowerCase());
+    }
+
+    @Nullable
+    public EnumCreatureType getEnumCreatureType() {
+        return this.creatureType;
+    }
+
+    public void setEnumCreatureType(EnumCreatureType type) {
+        this.creatureType = type;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/internal/registry/GDItemType.java b/sponge/src/main/java/com/griefdefender/internal/registry/GDItemType.java
new file mode 100644
index 0000000..a9493e3
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/internal/registry/GDItemType.java
@@ -0,0 +1,50 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.internal.registry;
+
+import com.griefdefender.api.CatalogType;
+import org.spongepowered.api.item.ItemType;
+
+public class GDItemType implements CatalogType {
+
+    private ItemType type;
+
+    public GDItemType(ItemType type) {
+        this.type = type;
+    }
+
+    @Override
+    public String getName() {
+        return this.type.getName();
+    }
+
+    public String getId() {
+        return this.type.getId();
+    }
+
+    public ItemType getType() {
+        return this.type;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/internal/registry/ItemTypeRegistryModule.java b/sponge/src/main/java/com/griefdefender/internal/registry/ItemTypeRegistryModule.java
new file mode 100644
index 0000000..8f60776
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/internal/registry/ItemTypeRegistryModule.java
@@ -0,0 +1,68 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.internal.registry;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.collect.ImmutableList;
+import org.spongepowered.api.item.ItemType;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Optional;
+
+public final class ItemTypeRegistryModule {
+
+    protected final Map<String, GDItemType> itemTypeMappings = new HashMap<>();
+    private final org.spongepowered.common.registry.type.ItemTypeRegistryModule SPONGE_REGISTRY = org.spongepowered.common.registry.type.ItemTypeRegistryModule.getInstance();
+
+    public static ItemTypeRegistryModule getInstance() {
+        return Holder.INSTANCE;
+    }
+
+    public Optional<GDItemType> getById(String id) {
+        if (!checkNotNull(id).contains(":")) {
+            id = "minecraft:" + id;
+        }
+        return Optional.ofNullable(this.itemTypeMappings.get(id.toLowerCase(Locale.ENGLISH)));
+    }
+
+    public Collection<GDItemType> getAll() {
+        return ImmutableList.copyOf(this.itemTypeMappings.values());
+    }
+
+    public void registerDefaults() {
+        for (ItemType type : SPONGE_REGISTRY.getAll()) {
+            this.itemTypeMappings.put(type.getId(), new GDItemType(type));
+        }
+    }
+
+    private static final class Holder {
+
+        static final ItemTypeRegistryModule INSTANCE = new ItemTypeRegistryModule();
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/internal/util/BlockUtil.java b/sponge/src/main/java/com/griefdefender/internal/util/BlockUtil.java
new file mode 100644
index 0000000..63c6b49
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/internal/util/BlockUtil.java
@@ -0,0 +1,485 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.internal.util;
+
+import com.flowpowered.math.vector.Vector3i;
+import com.google.common.collect.Maps;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.ClaimBlockSystem;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.claim.GDClaimManager;
+import com.griefdefender.util.BlockPosCache;
+import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
+import net.minecraft.block.Block;
+import net.minecraft.block.state.IBlockState;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.network.play.server.SPacketBlockChange;
+import net.minecraft.network.play.server.SPacketChunkData;
+import net.minecraft.server.management.PlayerChunkMapEntry;
+import net.minecraft.util.ClassInheritanceMultiMap;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.ChunkPos;
+import net.minecraft.world.MinecraftException;
+import net.minecraft.world.WorldServer;
+import net.minecraft.world.chunk.Chunk;
+import net.minecraft.world.gen.ChunkProviderServer;
+import org.spongepowered.api.block.BlockSnapshot;
+import org.spongepowered.api.block.BlockState;
+import org.spongepowered.api.block.BlockTypes;
+import org.spongepowered.api.data.property.block.MatterProperty;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.util.Direction;
+import org.spongepowered.api.util.blockray.BlockRay;
+import org.spongepowered.api.util.blockray.BlockRayHit;
+import org.spongepowered.api.world.LocatableBlock;
+import org.spongepowered.api.world.Location;
+import org.spongepowered.api.world.World;
+import org.spongepowered.common.bridge.world.chunk.ChunkBridge;
+import org.spongepowered.common.bridge.world.chunk.ChunkProviderBridge;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+public class BlockUtil {
+
+    private static BlockUtil instance;
+
+    static {
+        instance = new BlockUtil();
+    }
+
+    public static BlockUtil getInstance() {
+        return instance;
+    }
+
+    public static final Direction[] CARDINAL_SET = {
+            Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST
+        };
+    public static final Direction[] ORDINAL_SET = {
+            Direction.NORTH, Direction.NORTHEAST, Direction.EAST, Direction.SOUTHEAST,
+            Direction.SOUTH, Direction.SOUTHWEST, Direction.WEST, Direction.NORTHWEST,
+        };
+    private static final int NUM_XZ_BITS = 4;
+    private static final int NUM_SHORT_Y_BITS = 8;
+    private static final short XZ_MASK = 0xF;
+    private static final short Y_SHORT_MASK = 0xFF;
+
+    public static final Map<Integer, BlockPosCache> ENTITY_BLOCK_CACHE = new Int2ObjectArrayMap<>();
+    private static final Map<BlockState, Integer> BLOCKSTATE_META_CACHE = Maps.newHashMap();
+    public static boolean POPULATING_CHUNK = false;
+
+    public String posToString(Location<World> location) {
+        return posToString(location.getBlockPosition());
+    }
+
+    public String posToString(Vector3i pos) {
+        return posToString(pos.getX(), pos.getY(), pos.getZ());
+    }
+
+    public String posToString(int x, int y, int z) {
+        StringBuilder stringBuilder = new StringBuilder();
+        stringBuilder.append(x);
+        stringBuilder.append(";");
+        stringBuilder.append(y);
+        stringBuilder.append(";");
+        stringBuilder.append(z);
+
+        return stringBuilder.toString();
+    }
+
+    public Vector3i posFromString(String pos) throws Exception {
+        String[] elements = pos.split(";");
+
+        if (elements.length < 3) {
+            throw new Exception("Invalid position " + pos + "");
+        }
+
+        String xString = elements[0];
+        String yString = elements[1];
+        String zString = elements[2];
+
+        int x = Integer.parseInt(xString);
+        int y = Integer.parseInt(yString);
+        int z = Integer.parseInt(zString);
+
+        return new Vector3i(x, y, z);
+    }
+
+    public boolean clickedClaimCorner(GDClaim claim, Vector3i clickedPos) {
+        int clickedX = clickedPos.getX();
+        int clickedY = clickedPos.getY();
+        int clickedZ = clickedPos.getZ();
+        int lesserX = claim.getLesserBoundaryCorner().getX();
+        int lesserY = claim.getLesserBoundaryCorner().getY();
+        int lesserZ = claim.getLesserBoundaryCorner().getZ();
+        int greaterX = claim.getGreaterBoundaryCorner().getX();
+        int greaterY = claim.getGreaterBoundaryCorner().getY();
+        int greaterZ = claim.getGreaterBoundaryCorner().getZ();
+        if ((clickedX == lesserX || clickedX == greaterX) && (clickedZ == lesserZ || clickedZ == greaterZ)
+                && (!claim.isCuboid() || (clickedY == lesserY || clickedY == greaterY))) {
+            return true;
+        }
+
+        return false;
+    }
+
+    public int getBlockStateMeta(BlockState state) {
+        Integer meta = BLOCKSTATE_META_CACHE.get(state);
+        if (meta == null) {
+            Block mcBlock = (net.minecraft.block.Block) state.getType();
+            meta = mcBlock.getMetaFromState((IBlockState) state);
+            BLOCKSTATE_META_CACHE.put(state, meta);
+        }
+        return meta;
+    }
+
+    public int getClaimBlockCost(World world, Vector3i lesser, Vector3i greater, boolean cuboid) {
+        final int claimWidth = greater.getX() - lesser.getX() + 1;
+        final int claimHeight = greater.getY() - lesser.getY() + 1;
+        final int claimLength = greater.getZ() - lesser.getZ() + 1;
+        if (GriefDefenderPlugin.CLAIM_BLOCK_SYSTEM == ClaimBlockSystem.AREA) {
+            return claimWidth * claimLength;
+        }
+
+        return claimLength * claimWidth * claimHeight;
+    }
+
+    public long asLong(int x, int z) {
+        return (long)x & 4294967295L | ((long)z & 4294967295L) << 32;
+    }
+
+    /**
+     * Serialize this BlockPos into a short value
+     */
+    public short blockPosToShort(Vector3i pos) {
+        short serialized = (short) setNibble(0, pos.getX() & XZ_MASK, 0, NUM_XZ_BITS);
+        serialized = (short) setNibble(serialized, pos.getY() & Y_SHORT_MASK, 1, NUM_SHORT_Y_BITS);
+        serialized = (short) setNibble(serialized, pos.getZ() & XZ_MASK, 3, NUM_XZ_BITS);
+        return serialized;
+    }
+
+    private int setNibble(int num, int data, int which, int bitsToReplace) {
+        return (num & ~(bitsToReplace << (which * 4)) | (data << (which * 4)));
+    }
+
+    public void restoreClaim(GDClaim claim) {
+        if (claim.isAdminClaim()) {
+            return;
+        }
+
+        // it's too expensive to do this for huge claims
+        if (claim.getClaimBlocks() > (GriefDefenderPlugin.CLAIM_BLOCK_SYSTEM == ClaimBlockSystem.VOLUME ? 2560000 : 10000)) {
+            return;
+        }
+
+        ArrayList<org.spongepowered.api.world.Chunk> chunks = claim.getChunks();
+        final List<net.minecraft.world.chunk.Chunk> regeneratedChunks = new ArrayList<>();
+        final ChunkProviderServer chunkProviderServer = (ChunkProviderServer) ((WorldServer) claim.getWorld()).getChunkProvider();
+        for (org.spongepowered.api.world.Chunk chunk : chunks) {
+            final Vector3i min = chunk.getBlockMin();
+            final Vector3i max = chunk.getBlockMax();
+            if (claim.contains(min, true) && claim.contains(max, true)) {
+                BlockUtil.getInstance().regenerateChunk((net.minecraft.world.chunk.Chunk) chunk);
+            } else {
+                regeneratedChunks.add((net.minecraft.world.chunk.Chunk) chunk);
+            }
+        }
+
+        for (net.minecraft.world.chunk.Chunk chunk : regeneratedChunks) {
+            final net.minecraft.world.chunk.Chunk newChunk = chunkProviderServer.chunkGenerator.generateChunk(chunk.x, chunk.z);
+            final net.minecraft.world.WorldServer world = (WorldServer) chunk.getWorld();
+            List<BlockPos> changedPositions = new ArrayList<>();
+            if (newChunk != null) {
+                final int cx = newChunk.x << 4;
+                final int cz = newChunk.z << 4;
+                for(int xx = cx; xx < cx + 16; xx++) {
+                    for(int zz = cz; zz < cz + 16; zz++) {
+                        for(int yy = 0; yy < 256; yy++) {
+                            final BlockPos blockPos = new BlockPos(xx, yy, zz);
+                            if (claim.contains(xx, yy, zz, true, null, false)) {
+                                final IBlockState newState = newChunk.getBlockState(blockPos);
+                                final IBlockState oldState = chunk.getBlockState(blockPos);
+                                if (oldState == newState) {
+                                    continue;
+                                }
+                                world.removeTileEntity(blockPos);
+                                chunk.setBlockState(blockPos, newState);
+                                changedPositions.add(blockPos);
+                            }
+                        }
+                    }
+                }
+
+                final PlayerChunkMapEntry playerMapChunkEntry = ((WorldServer) chunk.getWorld()).getPlayerChunkMap().getEntry(chunk.x, chunk.z);
+                if (playerMapChunkEntry != null) {
+                    List<EntityPlayerMP> chunkPlayers = playerMapChunkEntry.players;
+                    for (EntityPlayerMP playerMP: chunkPlayers) {
+                        for (BlockPos pos : changedPositions) {
+                            playerMP.connection.sendPacket(new SPacketBlockChange(world, pos));
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private void saveChunkData(ChunkProviderServer chunkProviderServer, Chunk chunkIn)
+    {
+        try
+        {
+            chunkIn.setLastSaveTime(chunkIn.getWorld().getTotalWorldTime());
+            chunkProviderServer.chunkLoader.saveChunk(chunkIn.getWorld(), chunkIn);
+        }
+        catch (IOException ioexception)
+        {
+            //LOGGER.error((String)"Couldn\'t save chunk", (Throwable)ioexception);
+        }
+        catch (MinecraftException minecraftexception)
+        {
+            //LOGGER.error((String)"Couldn\'t save chunk; already in use by another instance of Minecraft?", (Throwable)minecraftexception);
+        }
+        try
+        {
+            chunkProviderServer.chunkLoader.saveExtraChunkData(chunkIn.getWorld(), chunkIn);
+        }
+        catch (Exception exception)
+        {
+            //LOGGER.error((String)"Couldn\'t save entities", (Throwable)exception);
+        }
+    }
+
+    private void unloadChunk(net.minecraft.world.chunk.Chunk chunk) {
+        ChunkProviderServer chunkProviderServer = (ChunkProviderServer) chunk.getWorld().getChunkProvider();
+
+        boolean saveChunk = false;
+        if (chunk.needsSaving(true)) {
+            saveChunk = true;
+        }
+
+        chunk.onUnload();
+
+        if (saveChunk) {
+            saveChunkData(chunkProviderServer, chunk);
+        }
+
+        chunkProviderServer.loadedChunks.remove(ChunkPos.asLong(chunk.x, chunk.z));
+        ((ChunkBridge) chunk).bridge$setScheduledForUnload(-1);
+    }
+
+    public boolean regenerateChunk(Chunk chunk) {
+        // Before unloading the chunk, we copy its entities then clear.
+        // This ensures that they don't get unloaded when we unload the chunk,
+        // since we want to keep them.
+        //
+        // We explicitly do *not* clear any tileentity-related things - we
+        // want those to be saved and unloaded, since the new chunk
+        // will have completely different blocks
+        List<Entity> entityList = new ArrayList<>();
+        for (ClassInheritanceMultiMap<Entity> multiEntityList : chunk.getEntityLists()) {
+            entityList.addAll(multiEntityList);
+        }
+
+        for (Entity entity : entityList) {
+            chunk.removeEntity(entity);
+        }
+
+        unloadChunk(chunk);
+        ChunkProviderServer chunkProviderServer = (ChunkProviderServer) chunk.getWorld().getChunkProvider();
+        Chunk newChunk = chunkProviderServer.chunkGenerator.generateChunk(chunk.x, chunk.z);
+        PlayerChunkMapEntry playerChunk = ((WorldServer) chunk.getWorld()).getPlayerChunkMap().getEntry(chunk.x, chunk.z);
+        if (playerChunk != null) {
+            playerChunk.chunk = newChunk;
+        }
+
+        if (newChunk != null) {
+            WorldServer world = (WorldServer) newChunk.getWorld();
+            world.getChunkProvider().loadedChunks.put(ChunkPos.asLong(newChunk.x, newChunk.z), newChunk);
+            newChunk.onLoad();
+            POPULATING_CHUNK = true;
+            newChunk.populate(world.getChunkProvider(), world.getChunkProvider().chunkGenerator);
+            POPULATING_CHUNK = false;
+            for (Entity entity: entityList) {
+                newChunk.addEntity(entity);
+            }
+            refreshChunk(newChunk);
+        }
+
+        return newChunk != null;
+    }
+
+    public boolean refreshChunk(Chunk chunk) {
+        int x = chunk.x;
+        int z = chunk.z;
+        WorldServer world = (WorldServer) chunk.getWorld();
+        ChunkProviderBridge chunkProviderServer = (ChunkProviderBridge) world.getChunkProvider();
+        if (chunkProviderServer.bridge$getLoadedChunkWithoutMarkingActive(x, z) == null) {
+            return false;
+        }
+
+        List<EntityPlayerMP> chunkPlayers = ((WorldServer) chunk.getWorld()).getPlayerChunkMap().getEntry(chunk.x, chunk.z).players;
+
+        // We deliberately send two packets, to avoid sending a 'fullChunk' packet
+        // (a changedSectionFilter of 65535). fullChunk packets cause the client to
+        // completely overwrite its current chunk with a new chunk instance. This causes
+        // weird issues, such as making any entities in that chunk invisible (until they leave it
+        // for a new chunk)
+        for (EntityPlayerMP playerMP: chunkPlayers) {
+            playerMP.connection.sendPacket(new SPacketChunkData(chunk, 65534));
+            playerMP.connection.sendPacket(new SPacketChunkData(chunk, 1));
+            /*for (ClassInheritanceMultiMap<Entity> multiEntityList : chunk.getEntityLists()) {
+                for (Entity entity : multiEntityList) {
+                    if (entity != playerMP) {
+                        final EntityTracker entityTracker = world.getEntityTracker();
+                        final EntityTrackerEntry lookup = entityTracker.trackedEntityHashTable.lookup(entity.getEntityId());
+                        Packet<?> newPacket = lookup.createSpawnPacket();
+                        playerMP.connection.sendPacket(newPacket);
+                    }
+                }
+            }*/
+        }
+
+        return true;
+    }
+
+    private int directionToIndex(Direction direction) {
+        switch (direction) {
+            case NORTH:
+            case NORTHEAST:
+            case NORTHWEST:
+                return 0;
+            case SOUTH:
+            case SOUTHEAST:
+            case SOUTHWEST:
+                return 1;
+            case EAST:
+                return 2;
+            case WEST:
+                return 3;
+            default:
+                throw new IllegalArgumentException("Unexpected direction");
+        }
+    }
+
+    public Optional<Location<World>> getTargetBlock(Player player, GDPlayerData playerData, int maxDistance, boolean ignoreAir) throws IllegalStateException {
+        BlockRay<World> blockRay = BlockRay.from(player).distanceLimit(maxDistance).build();
+        GDClaim claim = null;
+        if (playerData.visualClaimId != null) {
+            claim = (GDClaim) GriefDefenderPlugin.getInstance().dataStore.getClaim(player.getWorld().getUniqueId(), playerData.visualClaimId);
+        }
+
+        while (blockRay.hasNext()) {
+            BlockRayHit<World> blockRayHit = blockRay.next();
+            if (claim != null) {
+                for (Vector3i corner : claim.getVisualizer().getVisualCorners()) {
+                    if (corner.equals(blockRayHit.getBlockPosition())) {
+                        return Optional.of(blockRayHit.getLocation());
+                    }
+                }
+            }
+            if (ignoreAir) {
+                if (blockRayHit.getLocation().getBlockType() != BlockTypes.TALLGRASS) {
+                    return Optional.of(blockRayHit.getLocation());
+                }
+            } else {
+                if (blockRayHit.getLocation().getBlockType() != BlockTypes.AIR &&
+                        blockRayHit.getLocation().getBlockType() != BlockTypes.TALLGRASS) {
+                    return Optional.of(blockRayHit.getLocation());
+                }
+            }
+        }
+
+        return Optional.empty();
+    }
+
+    public boolean isLiquidSource(Object source) {
+        if (source instanceof BlockSnapshot) {
+            final BlockSnapshot blockSnapshot = (BlockSnapshot) source;
+            MatterProperty matterProperty = blockSnapshot.getState().getProperty(MatterProperty.class).orElse(null);
+            if (matterProperty != null && matterProperty.getValue() == MatterProperty.Matter.LIQUID) {
+                return true;
+            }
+        }
+        if (source instanceof LocatableBlock) {
+            final LocatableBlock locatableBlock = (LocatableBlock) source;
+            MatterProperty matterProperty = locatableBlock.getBlockState().getProperty(MatterProperty.class).orElse(null);
+            if (matterProperty != null && matterProperty.getValue() == MatterProperty.Matter.LIQUID) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public Set<Claim> getNearbyClaims(Location<World> location) {
+        return getNearbyClaims(location, 50);
+    }
+
+    public Set<Claim> getNearbyClaims(Location<World> location, int blockDistance) {
+        Set<Claim> claims = new HashSet<>();
+        GDClaimManager claimWorldManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(location.getExtent().getUniqueId());
+        if (claimWorldManager == null) {
+            return claims;
+        }
+
+        org.spongepowered.api.world.Chunk lesserChunk = location.getExtent().getChunkAtBlock(location.sub(blockDistance, 0, blockDistance).getBlockPosition()).orElse(null);
+        org.spongepowered.api.world.Chunk greaterChunk = location.getExtent().getChunkAtBlock(location.add(blockDistance, 0, blockDistance).getBlockPosition()).orElse(null);
+
+        if (lesserChunk != null && greaterChunk != null) {
+            for (int chunkX = lesserChunk.getPosition().getX(); chunkX <= greaterChunk.getPosition().getX(); chunkX++) {
+                for (int chunkZ = lesserChunk.getPosition().getZ(); chunkZ <= greaterChunk.getPosition().getZ(); chunkZ++) {
+                    org.spongepowered.api.world.Chunk chunk = location.getExtent().getChunk(chunkX, 0, chunkZ).orElse(null);
+                    if (chunk != null) {
+                        Set<Claim> claimsInChunk = claimWorldManager.getInternalChunksToClaimsMap().get(ChunkPos.asLong(chunkX, chunkZ));
+                        if (claimsInChunk != null) {
+                            for (Claim claim : claimsInChunk) {
+                                final GDClaim gpClaim = (GDClaim) claim;
+                                if (gpClaim.parent == null && !claims.contains(claim)) {
+                                    claims.add(claim);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        return claims;
+    }
+
+    public boolean isBlockWater(BlockState state) {
+        Optional<MatterProperty> matterProperty = state.getProperty(MatterProperty.class);
+        if (matterProperty.isPresent() && matterProperty.get().getValue() == MatterProperty.Matter.LIQUID) {
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/internal/util/NMSUtil.java b/sponge/src/main/java/com/griefdefender/internal/util/NMSUtil.java
new file mode 100644
index 0000000..f6ddffd
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/internal/util/NMSUtil.java
@@ -0,0 +1,388 @@
+package com.griefdefender.internal.util;
+
+import com.flowpowered.math.vector.Vector3i;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.claim.ClaimTypes;
+import com.griefdefender.api.permission.flag.Flag;
+import com.griefdefender.api.permission.flag.Flags;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.internal.EntityRemovalListener;
+import com.griefdefender.permission.GDPermissionManager;
+import com.griefdefender.util.EntityUtils;
+import net.minecraft.block.BlockBasePressurePlate;
+import net.minecraft.block.state.IBlockState;
+import net.minecraft.entity.EnumCreatureType;
+import net.minecraft.entity.item.EntityItem;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.inventory.ContainerChest;
+import net.minecraft.inventory.ContainerPlayer;
+import net.minecraft.inventory.IInventory;
+import net.minecraft.item.ItemBlock;
+import net.minecraft.item.ItemFood;
+import net.minecraft.util.math.AxisAlignedBB;
+import net.minecraft.util.math.RayTraceResult;
+import net.minecraft.util.math.Vec3d;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.block.BlockState;
+import org.spongepowered.api.block.BlockType;
+import org.spongepowered.api.command.CommandMapping;
+import org.spongepowered.api.data.key.Keys;
+import org.spongepowered.api.entity.Entity;
+import org.spongepowered.api.entity.EntityType;
+import org.spongepowered.api.entity.Item;
+import org.spongepowered.api.entity.living.Living;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.entity.living.player.User;
+import org.spongepowered.api.event.Event;
+import org.spongepowered.api.event.cause.Cause;
+import org.spongepowered.api.event.cause.entity.damage.DamageType;
+import org.spongepowered.api.item.ItemType;
+import org.spongepowered.api.item.inventory.Container;
+import org.spongepowered.api.item.inventory.ItemStack;
+import org.spongepowered.api.item.inventory.ItemStackSnapshot;
+import org.spongepowered.api.plugin.PluginContainer;
+import org.spongepowered.api.profile.GameProfile;
+import org.spongepowered.api.service.user.UserStorageService;
+import org.spongepowered.api.world.Location;
+import org.spongepowered.api.world.World;
+import org.spongepowered.common.SpongeImplHooks;
+import org.spongepowered.common.entity.SpongeEntityType;
+import org.spongepowered.common.item.inventory.custom.CustomInventory;
+import org.spongepowered.common.util.SpongeUsernameCache;
+
+import java.util.Optional;
+import java.util.Set;
+import java.util.UUID;
+
+public class NMSUtil {
+
+    public static final BiMap<String, EnumCreatureType> SPAWN_TYPES = HashBiMap.create();
+
+    static {
+        SPAWN_TYPES.put("ambient", EnumCreatureType.AMBIENT);
+        SPAWN_TYPES.put("animal", EnumCreatureType.CREATURE);
+        SPAWN_TYPES.put("aquatic", EnumCreatureType.WATER_CREATURE);
+        SPAWN_TYPES.put("monster", EnumCreatureType.MONSTER);
+    }
+
+    private static NMSUtil instance;
+
+    static {
+        instance = new NMSUtil();
+    }
+
+    public static NMSUtil getInstance() {
+        return instance;
+    }
+
+    public void addEntityRemovalListener(World world) {
+        ((net.minecraft.world.WorldServer) world).addEventListener(new EntityRemovalListener());
+    }
+
+    public boolean isTransparent(BlockState state) {
+        final IBlockState iblockstate = (IBlockState)(Object) state;
+        return !iblockstate.isOpaqueCube();
+    }
+
+    public int getItemDamage(ItemStack stack) {
+        return ((net.minecraft.item.ItemStack)(Object) stack).getItemDamage();
+    }
+
+    public String getEntityId(Entity targetEntity, boolean isSource) {
+        net.minecraft.entity.Entity mcEntity = null;
+        if (targetEntity instanceof net.minecraft.entity.Entity) {
+            mcEntity = (net.minecraft.entity.Entity) targetEntity;
+        }
+        String id = "";
+        if (mcEntity != null && mcEntity instanceof EntityItem) {
+            EntityItem mcItem = (EntityItem) mcEntity;
+            net.minecraft.item.ItemStack itemStack = mcItem.getItem();
+            if (itemStack != null && itemStack.getItem() != null) {
+                ItemType itemType = (ItemType) itemStack.getItem();
+                id = itemType.getId() + "." + itemStack.getItemDamage();
+            }
+        } else {
+            if (targetEntity.getType() != null) {
+                id = targetEntity.getType().getId();
+            }
+            
+        }
+
+        if (mcEntity != null && id.contains("unknown") && SpongeImplHooks.isFakePlayer(mcEntity)) {
+            final String modId = SpongeImplHooks.getModIdFromClass(mcEntity.getClass());
+            id = modId + ":fakeplayer_" + EntityUtils.getFriendlyName(mcEntity).toLowerCase();
+        } else if (id.equals("unknown:unknown") && targetEntity instanceof EntityPlayer) {
+            id = "minecraft:player";
+        }
+
+        if (mcEntity != null && targetEntity instanceof Living) {
+            String[] parts = id.split(":");
+            if (parts.length > 1) {
+                final String modId = parts[0];
+                String name = parts[1];
+                if (modId.equalsIgnoreCase("pixelmon") && modId.equalsIgnoreCase(name)) {
+                    name = EntityUtils.getFriendlyName(mcEntity).toLowerCase();
+                    GDPermissionManager.getInstance().populateEventSourceTarget(modId + ":" + name, isSource);
+                    if (isSource) {
+                        id = modId + ":" + name;
+                        return id;
+                    }
+                }
+                if (!isSource) {
+                    for (EnumCreatureType type : EnumCreatureType.values()) {
+                        if (SpongeImplHooks.isCreatureOfType(mcEntity, type)) {
+                            id = modId + ":" + SPAWN_TYPES.inverse().get(type) + ":" + name;
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+
+        if (targetEntity instanceof Item) {
+            id = ((Item) targetEntity).getItemType().getId();
+        }
+
+        return id;
+    }
+    
+    public int getEntityMinecraftId(Entity entity) {
+        return ((net.minecraft.entity.Entity) entity).getEntityId();
+    }
+
+    public String getEntityName(Entity entity) {
+        return ((net.minecraft.entity.Entity) entity).getName();
+    }
+
+    // Fallback option for creating user's from usernamecache.json
+    public User createUserFromCache(UUID uuid) {
+        final String username = SpongeUsernameCache.getLastKnownUsername(uuid);
+        if (username != null) {
+            return Sponge.getGame().getServiceManager().provide(UserStorageService.class).get().get(GameProfile.of(uuid, username)).orElse(null);
+        }
+        return null;
+    }
+
+    public User createUserFromCache(String username) {
+        final UUID uuid = SpongeUsernameCache.getLastKnownUUID(username);
+        if (username != null) {
+            return Sponge.getGame().getServiceManager().provide(UserStorageService.class).get().get(GameProfile.of(uuid, username)).orElse(null);
+        }
+        return null;
+    }
+
+    public String getItemStackId(ItemStack stack) {
+        if (stack.getType() instanceof ItemBlock) {
+            ItemBlock itemBlock = (ItemBlock) stack.getType();
+            net.minecraft.item.ItemStack nmsStack = (net.minecraft.item.ItemStack)(Object) stack;
+            BlockState blockState = ((BlockState) itemBlock.getBlock().getStateFromMeta(nmsStack.getItemDamage()));
+            return blockState.getType().getId();
+        }
+
+        return stack.getType().getId();
+    }
+
+    public String getItemStackMeta(ItemStack stack) {
+        return String.valueOf(((net.minecraft.item.ItemStack)(Object) stack).getItemDamage());
+    }
+
+    public int getPlayerBlockReachDistance(Player player) {
+        return (int) SpongeImplHooks.getBlockReachDistance((EntityPlayerMP) player);
+    }
+
+    public boolean isBlockPressurePlate(BlockType type) {
+        return type instanceof BlockBasePressurePlate;
+    }
+
+    public boolean isEntityMonster(Entity entity) {
+        return SpongeImplHooks.isCreatureOfType((net.minecraft.entity.Entity) entity, EnumCreatureType.MONSTER);
+    }
+
+    public boolean isEntityAnimal(Entity entity) {
+        return SpongeImplHooks.isCreatureOfType((net.minecraft.entity.Entity) entity, EnumCreatureType.CREATURE);
+    }
+
+    public boolean isFakePlayer(Entity entity) {
+        if (!(entity instanceof EntityPlayer)) {
+            return false;
+        }
+        return SpongeImplHooks.isFakePlayer((EntityPlayer) entity);
+    }
+
+    public boolean isContainerCustomInventory(Container container) {
+        if (container instanceof ContainerChest) {
+            return ((ContainerChest) container).getLowerChestInventory() instanceof CustomInventory;
+        }
+        return false;
+    }
+
+    public boolean isItemFood(ItemType type) {
+        return type instanceof ItemFood;
+    }
+
+    public boolean isItemBlock(ItemStack stack) {
+        return stack instanceof ItemBlock;
+    }
+
+    public boolean containsInventory(Object object) {
+        return object instanceof IInventory;
+    }
+
+    public boolean containsContainerPlayer(Cause cause) {
+        return cause.containsType(ContainerPlayer.class);
+    }
+
+    public void closePlayerScreen(Player player) {
+        ((EntityPlayerMP) player).closeScreen();
+    }
+
+
+    public RayTraceResult rayTracePlayerEyes(EntityPlayerMP player) {
+        double distance = SpongeImplHooks.getBlockReachDistance(player) + 1;
+        Vec3d startPos = new Vec3d(player.posX, player.posY + player.getEyeHeight(), player.posZ);
+        Vec3d endPos = startPos.add(new Vec3d(player.getLookVec().x * distance, player.getLookVec().y * distance, player.getLookVec().z * distance));
+        return player.world.rayTraceBlocks(startPos, endPos);
+    }
+
+    public Vec3d rayTracePlayerEyeHitVec(EntityPlayerMP player) {
+        RayTraceResult result = rayTracePlayerEyes(player);
+        return result == null ? null : result.hitVec;
+    }
+
+    public String getEntitySpawnFlag(Flag flag, String target) {
+        if (flag.getName().contains("entity") || flag == Flags.ITEM_SPAWN || flag == Flags.ENTER_CLAIM || flag == Flags.EXIT_CLAIM) {
+            String claimFlag = flag.getName();
+            // first check for valid SpawnType
+            String[] parts = target.split(":");
+            EnumCreatureType type = SPAWN_TYPES.get(parts[1]);
+            if (type != null) {
+                claimFlag += "." + parts[0] + "." + SPAWN_TYPES.inverse().get(type);
+                return claimFlag;
+            } else {
+                Optional<EntityType> entityType = Sponge.getRegistry().getType(EntityType.class, target);
+                if (entityType.isPresent()) {
+                    SpongeEntityType spongeEntityType = (SpongeEntityType) entityType.get();
+                    if (spongeEntityType.getEnumCreatureType() != null) {
+                        String creatureType = SPAWN_TYPES.inverse().get(spongeEntityType.getEnumCreatureType());
+                        if (parts[1].equalsIgnoreCase("pixelmon")) {
+                            claimFlag += "." + parts[0] + ".animal";
+                        } else {
+                            claimFlag += "." + parts[0] + "." + creatureType + "." + parts[1];
+                        }
+                        return claimFlag;
+                    } else {
+                        claimFlag += "." + parts[0] + "." + parts[1];
+                        return claimFlag;
+                    }
+                }
+                // Unfortunately this is required until Pixelmon registers their entities correctly in FML
+                if (target.contains("pixelmon")) {
+                    // If target was not found in registry, assume its a pixelmon animal
+                    if (parts[1].equalsIgnoreCase("pixelmon")) {
+                        claimFlag += "." + parts[0] + ".animal";
+                    } else {
+                        claimFlag += "." + parts[0] + ".animal." + parts[1];
+                    }
+                    return claimFlag;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    public void populateTabComplete() {
+        for (BlockType blockType : Sponge.getRegistry().getAllOf(BlockType.class)) {
+            String modId = blockType.getId().split(":")[0].toLowerCase();
+            if (modId.equals("none")) {
+                continue;
+            }
+            final String blockTypeId = blockType.getId().toLowerCase();
+            if (!GriefDefenderPlugin.ID_MAP.contains(modId + ":any")) {
+                GriefDefenderPlugin.ID_MAP.add(modId + ":any");
+            }
+            GriefDefenderPlugin.ID_MAP.add(blockTypeId);
+        }
+
+        for (EntityType entityType : Sponge.getRegistry().getAllOf(EntityType.class)) {
+            String modId = entityType.getId().split(":")[0].toLowerCase();
+            if (modId.equals("none")) {
+                continue;
+            }
+            final String entityTypeId = entityType.getId().toLowerCase();
+            if (!GriefDefenderPlugin.ID_MAP.contains(modId + ":any")) {
+                GriefDefenderPlugin.ID_MAP.add(modId + ":any");
+            }
+            if (!GriefDefenderPlugin.ID_MAP.contains(entityTypeId)) {
+                GriefDefenderPlugin.ID_MAP.add(entityTypeId);
+            }
+            if (!GriefDefenderPlugin.ID_MAP.contains(modId + ":animal") && Living.class.isAssignableFrom(entityType.getEntityClass())) {
+                GriefDefenderPlugin.ID_MAP.add(modId + ":ambient");
+                GriefDefenderPlugin.ID_MAP.add(modId + ":animal");
+                GriefDefenderPlugin.ID_MAP.add(modId + ":aquatic");
+                GriefDefenderPlugin.ID_MAP.add(modId + ":monster");
+            }
+        }
+
+        for (ItemType itemType : Sponge.getRegistry().getAllOf(ItemType.class)) {
+            String modId = itemType.getId().split(":")[0].toLowerCase();
+            if (modId.equals("none")) {
+                continue;
+            }
+            final String itemTypeId = itemType.getId().toLowerCase();
+            if (!GriefDefenderPlugin.ID_MAP.contains(modId + ":any")) {
+                GriefDefenderPlugin.ID_MAP.add(modId + ":any");
+            }
+            if (!GriefDefenderPlugin.ID_MAP.contains(itemTypeId)) {
+                GriefDefenderPlugin.ID_MAP.add(itemTypeId);
+            }
+            GriefDefenderPlugin.ITEM_IDS.add(itemTypeId);
+        }
+
+        for (DamageType damageType : Sponge.getRegistry().getAllOf(DamageType.class)) {
+            String damageTypeId = damageType.getId().toLowerCase();
+            if (!damageType.getId().contains(":")) {
+                damageTypeId = "minecraft:" + damageTypeId;
+            }
+            if (!GriefDefenderPlugin.ID_MAP.contains(damageType.getId())) {
+                GriefDefenderPlugin.ID_MAP.add(damageTypeId);
+            }
+        }
+        // commands
+        Set<? extends CommandMapping> commandList = Sponge.getCommandManager().getCommands();
+        for (CommandMapping command : commandList) {
+            PluginContainer pluginContainer = Sponge.getCommandManager().getOwner(command).orElse(null);
+            if (pluginContainer != null) {
+                for (String alias : command.getAllAliases()) {
+                    String[] parts = alias.split(":");
+                    if (parts.length > 1) {
+                        GriefDefenderPlugin.ID_MAP.add(alias);
+                    }
+                }
+            }
+        }
+    }
+
+    public GDClaim createClaimFromCenter(Location<World> center, float radius) {
+        AxisAlignedBB aabb = new AxisAlignedBB(VecHelper.toBlockPos(center));
+        aabb = aabb.grow(radius);
+        final Vector3i lesser = new Vector3i(aabb.minX, aabb.minY, aabb.minZ);
+        final Vector3i greater = new Vector3i(aabb.maxX, aabb.maxY, aabb.maxZ);
+        return new GDClaim(center.getExtent(), lesser, greater, ClaimTypes.BASIC, false);
+    }
+
+    public ItemStack getActiveItem(Player player) {
+        return this.getActiveItem(player, null);
+    }
+
+    public ItemStack getActiveItem(Player player, Event currentEvent) {
+        final ItemStackSnapshot snapshot = player.get(Keys.ACTIVE_ITEM).orElse(null);
+        if (snapshot != null) {
+            return snapshot.createStack();
+        }
+        return null;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/internal/util/VecHelper.java b/sponge/src/main/java/com/griefdefender/internal/util/VecHelper.java
new file mode 100644
index 0000000..55d15d0
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/internal/util/VecHelper.java
@@ -0,0 +1,202 @@
+/*
+ * This file is part of Sponge, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) SpongePowered <https://www.spongepowered.org>
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.internal.util;
+
+import com.flowpowered.math.vector.Vector2i;
+import com.flowpowered.math.vector.Vector3d;
+import com.flowpowered.math.vector.Vector3i;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.ChunkPos;
+import net.minecraft.util.math.Rotations;
+import net.minecraft.util.math.Vec3d;
+import net.minecraft.util.math.Vec3i;
+import org.spongepowered.api.world.Location;
+import org.spongepowered.api.world.World;
+
+public final class VecHelper {
+
+    public static Location<World> toLocation(World world, Vector3i pos) {
+        return new Location<World>(world, pos.getX(), pos.getY(), pos.getZ());
+    }
+
+    public static Location<World> toLocation(net.minecraft.world.World world, BlockPos pos) {
+        return new Location<World>((World) world, pos.getX(), pos.getY(), pos.getZ());
+    }
+
+    // === Flow Vector3d --> BlockPos ===
+
+    public static BlockPos toBlockPos(Vector3d vector) {
+        if (vector == null) {
+            return null;
+        }
+        return new BlockPos(vector.getX(), vector.getY(), vector.getZ());
+    }
+
+    // === Flow Vector3i --> BlockPos ===
+
+    public static BlockPos toBlockPos(Vector3i vector) {
+        if (vector == null) {
+            return null;
+        }
+        return new BlockPos(vector.getX(), vector.getY(), vector.getZ());
+    }
+
+    // === Location<World> --> Vector3i ===
+    public static Vector3i toVector3i(Location<World> location) {
+        if (location == null) {
+            return null;
+        }
+        return new Vector3i(location.getBlockX(), location.getBlockY(), location.getBlockZ());
+    }
+    // === SpongeAPI Location<World> --> BlockPos ===
+    public static BlockPos toBlockPos(Location<World> location) {
+        if (location == null) {
+            return null;
+        }
+        return new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ());
+    }
+    // === MC BlockPos --> Flow Vector3i ==
+
+    public static Vector3i toVector3i(BlockPos pos) {
+        if (pos == null) {
+            return null;
+        }
+        return new Vector3i(pos.getX(), pos.getY(), pos.getZ());
+    }
+
+    // === MC BlockPos --> Flow Vector3d ==
+
+    public static Vector3d toVector3d(BlockPos pos) {
+        if (pos == null) {
+            return null;
+        }
+        return new Vector3d(pos.getX(), pos.getY(), pos.getZ());
+    }
+
+    // === Rotations --> Flow Vector ===
+
+    public static Vector3d toVector3d(Rotations rotation) {
+        if (rotation == null) {
+            return null;
+        }
+        return new Vector3d(rotation.getX(), rotation.getY(), rotation.getZ());
+    }
+
+    // === MC Vec3i --> Flow Vector3i ===
+
+    public static Vector3i toVector3i(Vec3i vector) {
+        if (vector == null) {
+            return null;
+        }
+        return new Vector3i(vector.getX(), vector.getY(), vector.getZ());
+    }
+
+    // === Flow Vector3i --> MC Vec3i ===
+
+    public static Vec3i toVec3i(Vector3i vector) {
+        if (vector == null) {
+            return null;
+        }
+        return new Vec3i(vector.getX(), vector.getY(), vector.getZ());
+    }
+
+    // === Flow Vector3d --> MC Vec3d ===
+
+    public static Vec3d toVec3d(Vector3d vector) {
+        if (vector == null) {
+            return null;
+        }
+        return new Vec3d(vector.getX(), vector.getY(), vector.getZ());
+    }
+
+    // === MC BlockPos --> MC Vec3d
+
+    public static Vec3d toVec3d(BlockPos pos) {
+        if (pos == null) {
+            return null;
+        }
+        return new Vec3d(pos.getX(), pos.getY(), pos.getZ());
+    }
+
+    // === MC ChunkCoordIntPair ---> Flow Vector3i ===
+
+    public static Vector3i toVec3i(ChunkPos pos) {
+        if (pos == null) {
+            return null;
+        }
+        return new Vector3i(pos.x, 0, pos.z);
+    }
+
+    // === Flow Vector3i --> MC ChunkCoordIntPair ===
+
+    public static ChunkPos toChunkPos(Vector3i vector) {
+        if (vector == null) {
+            return null;
+        }
+        return new ChunkPos(vector.getX(), vector.getZ());
+    }
+
+    // === MC Vec3 --> flow Vector3d ==
+
+    public static Vector3d toVector3d(Vec3d vector) {
+        if (vector == null) {
+            return null;
+        }
+        return new Vector3d(vector.x, vector.y, vector.z);
+    }
+
+    // === Flow Vector3d --> MC Vec3 ==
+
+    public static Vec3i toVec3i(Vector3d vector) {
+        if (vector == null) {
+            return null;
+        }
+        return new Vec3i(vector.getX(), vector.getY(), vector.getZ());
+    }
+
+    // === Flow Vector --> Rotations ===
+    public static Rotations toRotation(Vector3d vector) {
+        if (vector == null) {
+            return null;
+        }
+        return new Rotations((float) vector.getX(), (float) vector.getY(), (float) vector.getZ());
+    }
+
+    public static boolean inBounds(int x, int y, Vector2i min, Vector2i max) {
+        return x >= min.getX() && x <= max.getX() && y >= min.getY() && y <= max.getY();
+    }
+
+    public static boolean inBounds(int x, int y, int z, Vector3i min, Vector3i max) {
+        return x >= min.getX() && x <= max.getX() && y >= min.getY() && y <= max.getY() && z >= min.getZ() && z <= max.getZ();
+    }
+
+    public static boolean inBounds(Vector3d pos, Vector3i min, Vector3i max) {
+        return inBounds(pos.getX(), pos.getY(), pos.getZ(), min, max);
+    }
+
+    public static boolean inBounds(double x, double y, double z, Vector3i min, Vector3i max) {
+        return x >= min.getX() && x <= max.getX() && y >= min.getY() && y <= max.getY() && z >= min.getZ() && z <= max.getZ();
+    }
+}
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/internal/visual/ClaimVisual.java b/sponge/src/main/java/com/griefdefender/internal/visual/ClaimVisual.java
new file mode 100644
index 0000000..f775596
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/internal/visual/ClaimVisual.java
@@ -0,0 +1,559 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.internal.visual;
+
+import com.flowpowered.math.vector.Vector3i;
+import com.griefdefender.GDBootstrap;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.ClaimType;
+import com.griefdefender.api.claim.ClaimTypes;
+import com.griefdefender.api.permission.option.type.CreateModeTypes;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.internal.util.BlockUtil;
+import com.griefdefender.internal.util.NMSUtil;
+import com.griefdefender.task.ClaimVisualApplyTask;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.block.BlockSnapshot;
+import org.spongepowered.api.block.BlockState;
+import org.spongepowered.api.block.BlockType;
+import org.spongepowered.api.block.BlockTypes;
+import org.spongepowered.api.data.Transaction;
+import org.spongepowered.api.data.property.block.MatterProperty;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.util.Direction;
+import org.spongepowered.api.world.Location;
+import org.spongepowered.api.world.World;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+public class ClaimVisual {
+
+    public static GDClaimVisualType ADMIN;
+    public static GDClaimVisualType BASIC;
+    public static GDClaimVisualType DEFAULT;
+    public static GDClaimVisualType ERROR;
+    public static GDClaimVisualType SUBDIVISION;
+    public static GDClaimVisualType RESTORENATURE;
+    public static GDClaimVisualType TOWN;
+
+    private List<Transaction<BlockSnapshot>> visualTransactions;
+    private List<Transaction<BlockSnapshot>> newVisuals;
+    private List<Vector3i> corners;
+    private GDClaimVisualType type;
+    private GDClaim claim;
+    private Vector3i lesserBoundaryCorner;
+    private Vector3i greaterBoundaryCorner;
+    private boolean cuboidVisual = false;
+    private boolean isPlayerInWater = false;
+    private int sx, sy, sz;
+    private int bx, by, bz;
+    private int minx, minz;
+    private int maxx, maxz;
+    private BlockType cornerMaterial;
+    private BlockType accentMaterial;
+    private BlockType fillerMaterial; // used for 3d cuboids
+    private BlockSnapshot.Builder snapshotBuilder;
+    public boolean displaySubdivisions = false;
+    private int STEP = 10;
+
+    static {
+        ADMIN = new GDClaimVisualType("griefdefender:admin", "admin");
+        BASIC = new GDClaimVisualType("griefdefender:basic", "basic");
+        DEFAULT = new GDClaimVisualType("griefdefender:default", "default");
+        ERROR = new GDClaimVisualType("griefdefender:error", "error");
+        RESTORENATURE = new GDClaimVisualType("griefdefender:restorenature", "restorenature");
+        SUBDIVISION = new GDClaimVisualType("griefdefender:subdivision", "subdivision");
+        TOWN = new GDClaimVisualType("griefdefender:town", "town");
+    }
+
+    public ClaimVisual(GDClaimVisualType type) {
+        initBlockVisualTypes(type);
+        this.type = type;
+        this.snapshotBuilder = Sponge.getGame().getRegistry().createBuilder(BlockSnapshot.Builder.class);
+        this.visualTransactions = new ArrayList<Transaction<BlockSnapshot>>();
+        this.newVisuals = new ArrayList<>();
+        this.corners = new ArrayList<>();
+    }
+
+    public ClaimVisual(GDClaim claim, GDClaimVisualType type) {
+        this(claim.lesserBoundaryCorner, claim.greaterBoundaryCorner, type);
+        this.claim = claim;
+    }
+
+    public ClaimVisual(Vector3i lesserBoundaryCorner, Vector3i greaterBoundaryCorner, GDClaimVisualType type) {
+        initBlockVisualTypes(type);
+        this.lesserBoundaryCorner = lesserBoundaryCorner;
+        this.greaterBoundaryCorner = greaterBoundaryCorner;
+        this.type = type;
+        this.snapshotBuilder = Sponge.getGame().getRegistry().createBuilder(BlockSnapshot.Builder.class);
+        this.visualTransactions = new ArrayList<Transaction<BlockSnapshot>>();
+        this.newVisuals = new ArrayList<>();
+        this.corners = new ArrayList<>();
+    }
+
+    public void initBlockVisualTypes(GDClaimVisualType type) {
+        if (type == BASIC) {
+            cornerMaterial = BASIC.getVisualCornerBlock();
+            accentMaterial = BASIC.getVisualAccentBlock();
+            fillerMaterial = BASIC.getVisualFillerBlock();
+        } else if (type == ADMIN) {
+            cornerMaterial = ADMIN.getVisualCornerBlock();
+            accentMaterial = ADMIN.getVisualAccentBlock();
+            fillerMaterial = ADMIN.getVisualFillerBlock();
+        } else if (type == SUBDIVISION) {
+            cornerMaterial = SUBDIVISION.getVisualCornerBlock();
+            accentMaterial = SUBDIVISION.getVisualAccentBlock();
+            fillerMaterial = SUBDIVISION.getVisualFillerBlock();
+        } else if (type == RESTORENATURE) {
+            cornerMaterial = RESTORENATURE.getVisualCornerBlock();
+            accentMaterial = RESTORENATURE.getVisualAccentBlock();
+        } else if (type == TOWN) {
+            cornerMaterial = TOWN.getVisualCornerBlock();
+            accentMaterial = TOWN.getVisualAccentBlock();
+            fillerMaterial = TOWN.getVisualFillerBlock();
+        } else {
+            cornerMaterial = DEFAULT.getVisualCornerBlock();
+            accentMaterial = DEFAULT.getVisualAccentBlock();
+            fillerMaterial = DEFAULT.getVisualFillerBlock();
+        }
+    }
+
+    public static GDClaimVisualType getClaimVisualType(GDClaim claim) {
+        ClaimType type = claim.getType();
+        if (type != null) {
+            if (type == ClaimTypes.ADMIN) {
+                return ADMIN;
+            } else if (type == ClaimTypes.TOWN) {
+                return TOWN;
+            } else if (type == ClaimTypes.SUBDIVISION) {
+                return SUBDIVISION;
+            }
+        }
+
+        return BASIC;
+    }
+
+    public GDClaim getClaim() {
+        return this.claim;
+    }
+
+    public void apply(Player player) {
+        this.apply(player, true);
+    }
+
+    public void apply(Player player, boolean resetActive) {
+        GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+
+        // if he has any current visualization, clear it first
+        //playerData.revertActiveVisual(player);
+
+        if (player.isOnline() && this.visualTransactions.size() > 0
+                && this.visualTransactions.get(0).getOriginal().getLocation().get().getExtent().equals(player.getWorld())) {
+            Sponge.getGame().getScheduler().createTaskBuilder().delayTicks(1L)
+                    .execute(new ClaimVisualApplyTask(player, playerData, this, resetActive)).submit(GDBootstrap.getInstance());
+            //GriefDefenderPlugin.getInstance().executor.execute(new VisualizationApplicationTask(player, playerData, this, resetActive));
+        }
+    }
+
+    public void revert(Player player) {
+        if (!player.isOnline()) {
+            return;
+        }
+
+        GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+
+        int minx = player.getLocation().getBlockX() - 100;
+        int minz = player.getLocation().getBlockZ() - 100;
+        int maxx = player.getLocation().getBlockX() + 100;
+        int maxz = player.getLocation().getBlockZ() + 100;
+
+        if (!this.cuboidVisual) {
+            this.removeElementsOutOfRange(this.visualTransactions, minx, minz, maxx, maxz);
+        }
+
+        for (int i = 0; i < this.visualTransactions.size(); i++) {
+            BlockSnapshot snapshot = this.visualTransactions.get(i).getOriginal();
+
+            if (i == 0) {
+                if (!player.getWorld().equals(snapshot.getLocation().get().getExtent())) {
+                    return;
+                }
+            }
+
+            player.sendBlockChange(snapshot.getPosition(), snapshot.getState());
+        }
+
+        playerData.visualBlocks = null;
+        if (playerData.visualRevertTask != null) {
+            playerData.visualRevertTask.cancel();
+        }
+    }
+
+    public static ClaimVisual fromClick(Location<World> location, int height, GDClaimVisualType visualizationType, Player player, GDPlayerData playerData) {
+        ClaimVisual visualization = new ClaimVisual(visualizationType);
+        visualization.cornerMaterial = GriefDefenderPlugin.getInstance().createVisualBlock.getType();
+        BlockSnapshot blockClicked =
+                visualization.snapshotBuilder.from(location).blockState(visualization.cornerMaterial.getDefaultState()).build();
+        visualization.visualTransactions.add(new Transaction<BlockSnapshot>(blockClicked.getLocation().get().createSnapshot(), blockClicked));
+        if (GriefDefenderPlugin.getInstance().worldEditProvider != null && playerData.getClaimCreateMode() == CreateModeTypes.VOLUME && GriefDefenderPlugin.getGlobalConfig().getConfig().visual.hideDrag2d) {
+            GriefDefenderPlugin.getInstance().worldEditProvider.sendVisualDrag(player, playerData, location.getBlockPosition());
+        }
+        return visualization;
+    }
+
+    public void resetVisuals() {
+        this.visualTransactions.clear();
+        this.newVisuals.clear();
+    }
+
+    public void createClaimBlockVisualWithType(GDClaim claim, int height, Location<World> locality, GDPlayerData playerData, GDClaimVisualType visualType) {
+        this.type = visualType;
+        this.claim = claim;
+        this.addClaimElements(height, locality, playerData);
+    }
+
+    public void createClaimBlockVisuals(int height, Location<World> locality, GDPlayerData playerData) {
+        if (this.visualTransactions.size() != 0) {
+            return;
+        }
+
+        this.addClaimElements(height, locality, playerData);
+    }
+
+    public GDClaimVisualType getType() {
+        return this.type;
+    }
+
+    public void setType(GDClaimVisualType type) {
+        this.type = type;
+    }
+
+    private void addClaimElements(int height, Location<World> locality, GDPlayerData playerData) {
+        this.initBlockVisualTypes(type);
+        Vector3i lesser = this.claim.getLesserBoundaryCorner();
+        Vector3i greater = this.claim.getGreaterBoundaryCorner();
+        World world = locality.getExtent();
+        boolean liquidTransparent = locality.getBlock().getType().getProperty(MatterProperty.class).isPresent() ? false : true;
+
+        this.sx = lesser.getX();
+        this.sy = this.useCuboidVisual() ? lesser.getY() : 0;
+        this.sz = lesser.getZ();
+        this.bx = greater.getX();
+        this.by = this.useCuboidVisual() ? greater.getY() : 0;
+        this.bz = greater.getZ();
+        this.minx = this.claim.cuboid ? this.sx : locality.getBlockX() - 75;
+        this.minz = this.claim.cuboid ? this.sz : locality.getBlockZ() - 75;
+        this.maxx = this.claim.cuboid ? this.bx : locality.getBlockX() + 75;
+        this.maxz = this.claim.cuboid ? this.bz : locality.getBlockZ() + 75;
+
+        if (this.sx == this.bx && this.sy == this.by && this.sz == this.bz) {
+            BlockSnapshot blockClicked =
+                    snapshotBuilder.from(new Location<World>(world, this.sx, this.sy, this.sz)).blockState(this.cornerMaterial.getDefaultState()).build();
+            visualTransactions.add(new Transaction<BlockSnapshot>(blockClicked.getLocation().get().createSnapshot(), blockClicked));
+            return;
+        }
+
+        // check CUI support
+        if (GriefDefenderPlugin.getInstance().worldEditProvider != null && playerData != null
+                && GriefDefenderPlugin.getInstance().worldEditProvider.hasCUISupport(playerData.getPlayerName())) {
+            playerData.showVisualFillers = false;
+            STEP = 0;
+        }
+
+        // Check if player is in water
+        final Player player = Sponge.getCauseStackManager().getCurrentCause().first(Player.class).orElse(null);
+        if (player != null) {
+            if (BlockUtil.getInstance().isBlockWater(player.getLocation().getBlock())) {
+                this.isPlayerInWater = true;
+            } else {
+                this.isPlayerInWater = false;
+            }
+        } else {
+            this.isPlayerInWater = false;
+        }
+
+        if (this.useCuboidVisual()) {
+            this.addVisuals3D(claim, playerData);
+        } else {
+            this.addVisuals2D(claim, height);
+        }
+    }
+
+    public void addVisuals3D(GDClaim claim, GDPlayerData playerData) {
+        final World world = claim.getWorld();
+
+        this.addTopLine(world, this.sy, this.cornerMaterial, this.accentMaterial);
+        this.addTopLine(world, this.by, this.cornerMaterial, this.accentMaterial);
+        this.addBottomLine(world, this.sy, this.cornerMaterial, this.accentMaterial);
+        this.addBottomLine(world, this.by, this.cornerMaterial, this.accentMaterial);
+        this.addLeftLine(world, this.sy, this.cornerMaterial, this.accentMaterial);
+        this.addLeftLine(world, this.by, this.cornerMaterial, this.accentMaterial);
+        this.addRightLine(world, this.sy, this.cornerMaterial, this.accentMaterial);
+        this.addRightLine(world, this.by, this.cornerMaterial, this.accentMaterial);
+        // don't show corners while subdividing
+        if (playerData == null || (playerData.claimSubdividing == null)) {
+            // top corners
+            this.addCorners(world, this.by - 1, this.accentMaterial);
+            // bottom corners
+            this.addCorners(world, this.sy + 1, this.accentMaterial);
+        }
+
+        if (STEP != 0 && (playerData == null || playerData.showVisualFillers)) {
+            for (int y = this.sy + STEP; y < this.by - STEP / 2; y += STEP) {
+                this.addTopLine(world, y, fillerMaterial, fillerMaterial);
+            }
+            for (int y = this.sy + STEP; y < this.by - STEP / 2; y += STEP) {
+                this.addBottomLine(world, y, fillerMaterial, fillerMaterial);
+            }
+            for (int y = this.sy + STEP; y < this.by - STEP / 2; y += STEP) {
+                this.addLeftLine(world, y, fillerMaterial, fillerMaterial);
+            }
+            for (int y = this.sy + STEP; y < this.by - STEP / 2; y += STEP) {
+                this.addRightLine(world, y, fillerMaterial, fillerMaterial);
+            }
+        }
+        this.visualTransactions.addAll(newVisuals);
+    }
+
+    public void addVisuals2D(GDClaim claim, int height) {
+        final World world = claim.getWorld();
+        this.addTopLine(world, height, this.cornerMaterial, this.accentMaterial);
+        this.addBottomLine(world, height, this.cornerMaterial, this.accentMaterial);
+        this.addLeftLine(world, height, this.cornerMaterial, this.accentMaterial);
+        this.addRightLine(world, height, this.cornerMaterial, this.accentMaterial);
+
+        this.removeElementsOutOfRange(this.newVisuals, this.minx, this.minz, this.maxx, this.maxz);
+
+        for (int i = 0; i < this.newVisuals.size(); i++) {
+            final BlockSnapshot element = this.newVisuals.get(i).getFinal();
+            if (!claim.contains(element.getPosition())) {
+                this.newVisuals.remove(i);
+            }
+        }
+
+        ArrayList<Transaction<BlockSnapshot>> actualElements = new ArrayList<Transaction<BlockSnapshot>>();
+        for (Transaction<BlockSnapshot> element : this.newVisuals) {
+            Location<World> tempLocation = element.getFinal().getLocation().get();
+            Location<World> visibleLocation =
+                    getVisibleLocation(tempLocation.getExtent(), tempLocation.getBlockX(), height, tempLocation.getBlockZ());
+            element = new Transaction<BlockSnapshot>(element.getOriginal().withLocation(visibleLocation).withState(visibleLocation.getBlock()),
+                    element.getFinal().withLocation(visibleLocation));
+            height = element.getFinal().getPosition().getY();
+            actualElements.add(element);
+        }
+
+        this.visualTransactions.addAll(actualElements);
+    }
+
+    public void addCorners(World world, int y, BlockType accentMaterial) {
+        BlockSnapshot corner1 =
+                this.snapshotBuilder.from(new Location<World>(world, this.sx, y, this.bz)).blockState(accentMaterial.getDefaultState()).build();
+        this.newVisuals.add(new Transaction<BlockSnapshot>(corner1.getLocation().get().createSnapshot(), corner1));
+        BlockSnapshot corner2 =
+                this.snapshotBuilder.from(new Location<World>(world, this.bx, y, this.bz)).blockState(accentMaterial.getDefaultState()).build();
+        this.newVisuals.add(new Transaction<BlockSnapshot>(corner2.getLocation().get().createSnapshot(), corner2));
+        BlockSnapshot corner3 =
+                this.snapshotBuilder.from(new Location<World>(world, this.bx, y, this.sz)).blockState(accentMaterial.getDefaultState()).build();
+        this.newVisuals.add(new Transaction<BlockSnapshot>(corner3.getLocation().get().createSnapshot(), corner3));
+        BlockSnapshot corner4 =
+                this.snapshotBuilder.from(new Location<World>(world, this.sx, y, this.sz)).blockState(accentMaterial.getDefaultState()).build();
+        this.newVisuals.add(new Transaction<BlockSnapshot>(corner4.getLocation().get().createSnapshot(), corner4));
+    }
+
+    public void addTopLine(World world, int y, BlockType cornerMaterial, BlockType accentMaterial) {
+        BlockSnapshot topVisualBlock1 =
+                this.snapshotBuilder.from(new Location<World>(world, this.sx, y, this.bz)).blockState(cornerMaterial.getDefaultState()).build();
+        this.newVisuals.add(new Transaction<BlockSnapshot>(topVisualBlock1.getLocation().get().createSnapshot(), topVisualBlock1));
+        this.corners.add(topVisualBlock1.getPosition());
+        BlockSnapshot topVisualBlock2 =
+                this.snapshotBuilder.from(new Location<World>(world, this.sx + 1, y, this.bz)).blockState(accentMaterial.getDefaultState()).build();
+        this.newVisuals.add(new Transaction<BlockSnapshot>(topVisualBlock2.getLocation().get().createSnapshot(), topVisualBlock2));
+        BlockSnapshot topVisualBlock3 =
+                this.snapshotBuilder.from(new Location<World>(world, this.bx - 1, y, this.bz)).blockState(accentMaterial.getDefaultState()).build();
+        this.newVisuals.add(new Transaction<BlockSnapshot>(topVisualBlock3.getLocation().get().createSnapshot(), topVisualBlock3));
+
+        if (STEP != 0) {
+            for (int x = this.sx + STEP; x < this.bx - STEP / 2; x += STEP) {
+                if ((y != 0 && x >= this.sx && x <= this.bx) || (x > this.minx && x < this.maxx)) {
+                    BlockSnapshot visualBlock =
+                            this.snapshotBuilder.from(new Location<World>(world, x, y, this.bz)).blockState(accentMaterial.getDefaultState()).build();
+                    this.newVisuals.add(new Transaction<BlockSnapshot>(visualBlock.getLocation().get().createSnapshot(), visualBlock));
+                }
+            }
+        }
+    }
+
+    public void addBottomLine(World world, int y, BlockType cornerMaterial, BlockType accentMaterial) {
+        BlockSnapshot bottomVisualBlock1 =
+                this.snapshotBuilder.from(new Location<World>(world, this.sx + 1, y, this.sz)).blockState(accentMaterial.getDefaultState()).build();
+        this.newVisuals.add(new Transaction<BlockSnapshot>(bottomVisualBlock1.getLocation().get().createSnapshot(), bottomVisualBlock1));
+        this.corners.add(bottomVisualBlock1.getPosition());
+        BlockSnapshot bottomVisualBlock2 =
+                this.snapshotBuilder.from(new Location<World>(world, this.bx - 1, y, this.sz)).blockState(accentMaterial.getDefaultState()).build();
+        this.newVisuals.add(new Transaction<BlockSnapshot>(bottomVisualBlock2.getLocation().get().createSnapshot(), bottomVisualBlock2));
+
+        if (STEP != 0) {
+            for (int x = this.sx + STEP; x < this.bx - STEP / 2; x += STEP) {
+                if ((y != 0 && x >= this.sx && x <= this.bx) || (x > this.minx && x < this.maxx)) {
+                    BlockSnapshot visualBlock =
+                            this.snapshotBuilder.from(new Location<World>(world, x, y, this.sz)).blockState(accentMaterial.getDefaultState()).build();
+                    this.newVisuals.add(new Transaction<BlockSnapshot>(visualBlock.getLocation().get().createSnapshot(), visualBlock));
+                }
+            }
+        }
+    }
+
+    public void addLeftLine(World world, int y, BlockType cornerMaterial, BlockType accentMaterial) {
+        BlockSnapshot leftVisualBlock1 =
+                this.snapshotBuilder.from(new Location<World>(world, this.sx, y, this.sz)).blockState(cornerMaterial.getDefaultState()).build();
+        this.newVisuals.add(new Transaction<BlockSnapshot>(leftVisualBlock1.getLocation().get().createSnapshot(), leftVisualBlock1));
+        this.corners.add(leftVisualBlock1.getPosition());
+        BlockSnapshot leftVisualBlock2 =
+                this.snapshotBuilder.from(new Location<World>(world, this.sx, y, this.sz + 1)).blockState(accentMaterial.getDefaultState()).build();
+        this.newVisuals.add(new Transaction<BlockSnapshot>(leftVisualBlock2.getLocation().get().createSnapshot(), leftVisualBlock2));
+        BlockSnapshot leftVisualBlock3 =
+                this.snapshotBuilder.from(new Location<World>(world, this.sx, y, this.bz - 1)).blockState(accentMaterial.getDefaultState()).build();
+        this.newVisuals.add(new Transaction<BlockSnapshot>(leftVisualBlock3.getLocation().get().createSnapshot(), leftVisualBlock3));
+
+        if (STEP != 0) {
+            for (int z = this.sz + STEP; z < this.bz - STEP / 2; z += STEP) {
+                if ((y != 0 && z >= this.sz && z <= this.bz) || (z > this.minz && z < this.maxz)) {
+                    BlockSnapshot visualBlock =
+                            this.snapshotBuilder.from(new Location<World>(world, this.sx, y, z)).blockState(accentMaterial.getDefaultState()).build();
+                    this.newVisuals.add(new Transaction<BlockSnapshot>(visualBlock.getLocation().get().createSnapshot(), visualBlock));
+               }
+            }
+        }
+    }
+
+    public void addRightLine(World world, int y, BlockType cornerMaterial, BlockType accentMaterial) {
+        BlockSnapshot rightVisualBlock1 =
+                this.snapshotBuilder.from(new Location<World>(world, this.bx, y, this.sz)).blockState(cornerMaterial.getDefaultState()).build();
+        this.newVisuals.add(new Transaction<BlockSnapshot>(rightVisualBlock1.getLocation().get().createSnapshot(), rightVisualBlock1));
+        this.corners.add(rightVisualBlock1.getPosition());
+        BlockSnapshot rightVisualBlock2 =
+                this.snapshotBuilder.from(new Location<World>(world, this.bx, y, this.sz + 1)).blockState(accentMaterial.getDefaultState()).build();
+        this.newVisuals.add(new Transaction<BlockSnapshot>(rightVisualBlock2.getLocation().get().createSnapshot(), rightVisualBlock2));
+        if (STEP != 0) {
+            for (int z = this.sz + STEP; z < this.bz - STEP / 2; z += STEP) {
+                if ((y != 0 && z >= this.sz && z <= this.bz) || (z > this.minz && z < this.maxz)) {
+                    BlockSnapshot visualBlock =
+                            this.snapshotBuilder.from(new Location<World>(world, this.bx, y, z)).blockState(accentMaterial.getDefaultState()).build();
+                    this.newVisuals.add(new Transaction<BlockSnapshot>(visualBlock.getLocation().get().createSnapshot(), visualBlock));
+                }
+            }
+        }
+        BlockSnapshot rightVisualBlock3 =
+                this.snapshotBuilder.from(new Location<World>(world, this.bx, y, this.bz - 1)).blockState(accentMaterial.getDefaultState()).build();
+        this.newVisuals.add(new Transaction<BlockSnapshot>(rightVisualBlock3.getLocation().get().createSnapshot(), rightVisualBlock3));
+        BlockSnapshot rightVisualBlock4 =
+                this.snapshotBuilder.from(new Location<World>(world, this.bx, y, this.bz)).blockState(cornerMaterial.getDefaultState()).build();
+        this.newVisuals.add(new Transaction<BlockSnapshot>(rightVisualBlock4.getLocation().get().createSnapshot(), rightVisualBlock4));
+        this.corners.add(rightVisualBlock4.getPosition());
+    }
+
+    public List<Transaction<BlockSnapshot>> getVisualTransactions() {
+        return this.visualTransactions;
+    }
+
+    public List<Vector3i> getVisualCorners() {
+        return this.corners;
+    }
+
+    private void removeElementsOutOfRange(List<Transaction<BlockSnapshot>> elements, int minx, int minz, int maxx, int maxz) {
+        for (int i = 0; i < elements.size(); i++) {
+            Location<World> location = elements.get(i).getFinal().getLocation().get();
+            if (location.getX() < minx || location.getX() > maxx || location.getZ() < minz || location.getZ() > maxz) {
+                elements.remove(i);
+            }
+        }
+    }
+
+    private Location<World> getVisibleLocation(World world, int x, int y, int z) {
+        Location<World> location = world.getLocation(x, y, z);
+        Direction direction = (isTransparent(location.getBlock())) ? Direction.DOWN : Direction.UP;
+
+        while (location.getPosition().getY() >= 1 &&
+                location.getPosition().getY() < world.getDimension().getBuildHeight() - 1 &&
+                (!isTransparent(location.getRelative(Direction.UP).getBlock())
+                        || isTransparent(location.getBlock()))) {
+            location = location.getRelative(direction);
+        }
+
+        return location;
+    }
+
+    private boolean isTransparent(BlockState state) {
+        if (state.getType() == BlockTypes.SNOW_LAYER) {
+            return false;
+        }
+
+        if (!this.isPlayerInWater && BlockUtil.getInstance().isBlockWater(state)) {
+            return false;
+        }
+        return NMSUtil.getInstance().isTransparent(state);
+    }
+
+    public static ClaimVisual fromClaims(Set<Claim> claims, int height, Location<World> locality, GDPlayerData playerData, ClaimVisual visualization) {
+        if (visualization == null) {
+            visualization = new ClaimVisual(BASIC);
+        }
+
+        for (Claim claim : claims) {
+            GDClaim gpClaim = (GDClaim) claim;
+            if (!gpClaim.children.isEmpty()) {
+                fromClaims(gpClaim.children, height, locality, playerData, visualization);
+            }
+            if (gpClaim.claimVisual != null && !gpClaim.claimVisual.visualTransactions.isEmpty()) {
+                visualization.visualTransactions.addAll(gpClaim.getVisualizer().visualTransactions);
+            } else {
+                visualization.createClaimBlockVisualWithType(gpClaim, height, locality, playerData, ClaimVisual.getClaimVisualType(gpClaim));
+            }
+        }
+
+        return visualization;
+    }
+
+    private boolean useCuboidVisual() {
+        if (this.claim.cuboid) {
+            return true;
+        }
+
+        final GDPlayerData ownerData = this.claim.getOwnerPlayerData();
+        if (ownerData != null && (this.claim.getOwnerMinClaimLevel() > 0 || this.claim.getOwnerMaxClaimLevel() < 255)) {
+            return true;
+        }
+        // Claim could of been created with different min/max levels, so check Y values
+        if (this.claim.getLesserBoundaryCorner().getY() > 0 || this.claim.getGreaterBoundaryCorner().getY() < 255) {
+            return true;
+        }
+
+        return false;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/internal/visual/GDClaimVisualType.java b/sponge/src/main/java/com/griefdefender/internal/visual/GDClaimVisualType.java
new file mode 100644
index 0000000..629f030
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/internal/visual/GDClaimVisualType.java
@@ -0,0 +1,155 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.internal.visual;
+
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.CatalogType;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.block.BlockType;
+
+public class GDClaimVisualType implements CatalogType {
+
+    private final String id;
+    private final String name;
+    public BlockType visualAccentBlock;
+    public BlockType visualCornerBlock;
+    public BlockType visualFillerBlock;
+
+    public GDClaimVisualType(String id, String name) {
+        this.id = id.toLowerCase();
+        this.name = name.toLowerCase();
+    }
+
+    public BlockType getVisualAccentBlock() {
+        if (this.visualAccentBlock == null) {
+            this.initVisualBlocks();
+        }
+        return this.visualAccentBlock;
+    }
+
+    public BlockType getVisualCornerBlock() {
+        if (this.visualCornerBlock == null) {
+            this.initVisualBlocks();
+        }
+        return this.visualCornerBlock;
+    }
+
+    public BlockType getVisualFillerBlock() {
+        if (this.visualFillerBlock == null) {
+            this.initVisualBlocks();
+        }
+        return this.visualFillerBlock;
+    }
+
+    private void initVisualBlocks() {
+        if (this == ClaimVisual.ADMIN) {
+            this.visualAccentBlock = Sponge.getRegistry().getType(BlockType.class, GriefDefenderPlugin.getGlobalConfig().getConfig().visual.visualAdminAccentBlock).orElse(null);
+            if (this.visualAccentBlock == null) {
+                this.visualAccentBlock = Sponge.getRegistry().getType(BlockType.class, "minecraft:pumpkin").get();
+            }
+            this.visualCornerBlock = Sponge.getRegistry().getType(BlockType.class, GriefDefenderPlugin.getGlobalConfig().getConfig().visual.visualAdminCornerBlock).orElse(null);
+            if (this.visualCornerBlock == null) {
+                this.visualCornerBlock = Sponge.getRegistry().getType(BlockType.class, "minecraft:glowstone").get();
+            }
+            this.visualFillerBlock = Sponge.getRegistry().getType(BlockType.class, GriefDefenderPlugin.getGlobalConfig().getConfig().visual.visualAdminFillerBlock).orElse(null);
+            if (this.visualFillerBlock == null) {
+                this.visualFillerBlock = Sponge.getRegistry().getType(BlockType.class, "minecraft:pumpkin").get();
+            }
+        } else if (this == ClaimVisual.BASIC) {
+            this.visualAccentBlock = Sponge.getRegistry().getType(BlockType.class, GriefDefenderPlugin.getGlobalConfig().getConfig().visual.visualBasicAccentBlock).orElse(null);
+            if (this.visualAccentBlock == null) {
+                this.visualAccentBlock = Sponge.getRegistry().getType(BlockType.class, "minecraft:gold_block").get();
+            }
+            this.visualCornerBlock = Sponge.getRegistry().getType(BlockType.class, GriefDefenderPlugin.getGlobalConfig().getConfig().visual.visualBasicCornerBlock).orElse(null);
+            if (this.visualCornerBlock == null) {
+                this.visualCornerBlock = Sponge.getRegistry().getType(BlockType.class, "minecraft:glowstone").get();
+            }
+            this.visualFillerBlock = Sponge.getRegistry().getType(BlockType.class, GriefDefenderPlugin.getGlobalConfig().getConfig().visual.visualBasicFillerBlock).orElse(null);
+            if (this.visualFillerBlock == null) {
+                this.visualFillerBlock = Sponge.getRegistry().getType(BlockType.class, "minecraft:gold_block").get();
+            }
+        } else if (this == ClaimVisual.RESTORENATURE) {
+            this.visualAccentBlock = Sponge.getRegistry().getType(BlockType.class, GriefDefenderPlugin.getGlobalConfig().getConfig().visual.visualNatureAccentBlock).orElse(null);
+            if (this.visualAccentBlock == null) {
+                this.visualAccentBlock = Sponge.getRegistry().getType(BlockType.class, "minecraft:diamond_block").get();
+            }
+            this.visualCornerBlock = Sponge.getRegistry().getType(BlockType.class, GriefDefenderPlugin.getGlobalConfig().getConfig().visual.visualNatureCornerBlock).orElse(null);
+            if (this.visualCornerBlock == null) {
+                this.visualCornerBlock = Sponge.getRegistry().getType(BlockType.class, "minecraft:diamond_block").get();
+            }
+            this.visualFillerBlock = Sponge.getRegistry().getType(BlockType.class, "minecraft:diamond_block").get();
+        } else if (this == ClaimVisual.SUBDIVISION) {
+            this.visualAccentBlock = Sponge.getRegistry().getType(BlockType.class, GriefDefenderPlugin.getGlobalConfig().getConfig().visual.visualSubdivisionAccentBlock).orElse(null);
+            if (this.visualAccentBlock == null) {
+                this.visualAccentBlock = Sponge.getRegistry().getType(BlockType.class, "minecraft:white_wool").orElse(null);
+                if (this.visualAccentBlock == null) {
+                    this.visualAccentBlock = Sponge.getRegistry().getType(BlockType.class, "minecraft:wool").get();
+                }
+            }
+            this.visualCornerBlock = Sponge.getRegistry().getType(BlockType.class, GriefDefenderPlugin.getGlobalConfig().getConfig().visual.visualSubdivisionCornerBlock).orElse(null);
+            if (this.visualCornerBlock == null) {
+                this.visualCornerBlock = Sponge.getRegistry().getType(BlockType.class, "minecraft:iron_block").get();
+            }
+            this.visualFillerBlock = Sponge.getRegistry().getType(BlockType.class, GriefDefenderPlugin.getGlobalConfig().getConfig().visual.visualSubdivisionFillerBlock).orElse(null);
+            if (this.visualFillerBlock == null) {
+                this.visualFillerBlock = Sponge.getRegistry().getType(BlockType.class, "minecraft:white_wool").get();
+                if (this.visualFillerBlock == null) {
+                    this.visualFillerBlock = Sponge.getRegistry().getType(BlockType.class, "minecraft:wool").get();
+                }
+            }
+        } else if (this == ClaimVisual.TOWN) {
+            this.visualAccentBlock = Sponge.getRegistry().getType(BlockType.class, GriefDefenderPlugin.getGlobalConfig().getConfig().visual.visualTownAccentBlock).orElse(null);
+            if (this.visualAccentBlock == null) {
+                this.visualAccentBlock = Sponge.getRegistry().getType(BlockType.class, "minecraft:emerald_block").get();
+            }
+            this.visualCornerBlock = Sponge.getRegistry().getType(BlockType.class, GriefDefenderPlugin.getGlobalConfig().getConfig().visual.visualTownCornerBlock).orElse(null);
+            if (this.visualCornerBlock == null) {
+                this.visualCornerBlock = Sponge.getRegistry().getType(BlockType.class, "minecraft:glowstone").get();
+            }
+            this.visualFillerBlock = Sponge.getRegistry().getType(BlockType.class, GriefDefenderPlugin.getGlobalConfig().getConfig().visual.visualTownFillerBlock).orElse(null);
+            if (this.visualFillerBlock == null) {
+                this.visualFillerBlock = Sponge.getRegistry().getType(BlockType.class, "minecraft:emerald_block").get();
+            }
+        } else { // DEFAULT
+            this.visualAccentBlock = Sponge.getRegistry().getType(BlockType.class, "minecraft:netherrack").get();
+            this.visualCornerBlock = Sponge.getRegistry().getType(BlockType.class, "minecraft:redstone_ore").get();
+            this.visualFillerBlock = this.visualAccentBlock;
+        }
+    }
+
+    @Override
+    public String getId() {
+        return this.id;
+    }
+
+    @Override
+    public String getName() {
+        return this.name;
+    }
+
+    public String toString() {
+        return this.name;
+    }
+}
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/listener/BlockEventHandler.java b/sponge/src/main/java/com/griefdefender/listener/BlockEventHandler.java
new file mode 100644
index 0000000..30c83af
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/listener/BlockEventHandler.java
@@ -0,0 +1,989 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.listener;
+
+import com.flowpowered.math.vector.Vector3i;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.reflect.TypeToken;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GDTimings;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.GriefDefender;
+import com.griefdefender.api.Tristate;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.ClaimResult;
+import com.griefdefender.api.claim.ClaimTypes;
+import com.griefdefender.api.claim.TrustTypes;
+import com.griefdefender.api.permission.flag.Flags;
+import com.griefdefender.api.permission.option.Options;
+import com.griefdefender.cache.EventResultCache;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.claim.GDClaimManager;
+import com.griefdefender.configuration.GriefDefenderConfig;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.event.GDCauseStackManager;
+import com.griefdefender.internal.util.BlockUtil;
+import com.griefdefender.internal.util.NMSUtil;
+import com.griefdefender.internal.visual.ClaimVisual;
+import com.griefdefender.permission.GDPermissionManager;
+import com.griefdefender.permission.GDPermissions;
+import com.griefdefender.permission.flag.GDFlags;
+import com.griefdefender.storage.BaseStorage;
+import com.griefdefender.util.BlockPosCache;
+import com.griefdefender.util.CauseContextHelper;
+import net.kyori.text.Component;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.block.BlockSnapshot;
+import org.spongepowered.api.block.BlockState;
+import org.spongepowered.api.block.BlockType;
+import org.spongepowered.api.block.BlockTypes;
+import org.spongepowered.api.block.tileentity.Piston;
+import org.spongepowered.api.block.tileentity.TileEntity;
+import org.spongepowered.api.block.tileentity.carrier.Chest;
+import org.spongepowered.api.data.Transaction;
+import org.spongepowered.api.data.key.Keys;
+import org.spongepowered.api.entity.Entity;
+import org.spongepowered.api.entity.FallingBlock;
+import org.spongepowered.api.entity.Item;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.entity.living.player.User;
+import org.spongepowered.api.event.Listener;
+import org.spongepowered.api.event.Order;
+import org.spongepowered.api.event.block.ChangeBlockEvent;
+import org.spongepowered.api.event.block.CollideBlockEvent;
+import org.spongepowered.api.event.block.NotifyNeighborBlockEvent;
+import org.spongepowered.api.event.block.tileentity.ChangeSignEvent;
+import org.spongepowered.api.event.cause.Cause;
+import org.spongepowered.api.event.cause.EventContext;
+import org.spongepowered.api.event.cause.EventContextKeys;
+import org.spongepowered.api.event.filter.cause.Root;
+import org.spongepowered.api.event.world.ExplosionEvent;
+import org.spongepowered.api.text.Text;
+import org.spongepowered.api.util.Direction;
+import org.spongepowered.api.world.LocatableBlock;
+import org.spongepowered.api.world.Location;
+import org.spongepowered.api.world.World;
+import org.spongepowered.api.world.explosion.Explosion;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+//event handlers related to blocks
+public class BlockEventHandler {
+
+    private int lastBlockPreTick = -1;
+    private boolean lastBlockPreCancelled = false;
+
+    // convenience reference to singleton datastore
+    private final BaseStorage dataStore;
+
+    // constructor
+    public BlockEventHandler(BaseStorage dataStore) {
+        this.dataStore = dataStore;
+    }
+
+    @Listener(order = Order.FIRST, beforeModifications = true)
+    public void onBlockPre(ChangeBlockEvent.Pre event) {
+        lastBlockPreTick = Sponge.getServer().getRunningTimeTicks();
+        if (GriefDefenderPlugin.isSourceIdBlacklisted("block-pre", event.getSource(), event.getLocations().get(0).getExtent().getProperties())) {
+            return;
+        }
+
+        final World world = event.getLocations().get(0).getExtent();
+        if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(world.getUniqueId())) {
+            GDTimings.BLOCK_PRE_EVENT.stopTimingIfSync();
+            return;
+        }
+
+        final Cause cause = event.getCause();
+        final EventContext context = event.getContext();
+        final User user = CauseContextHelper.getEventUser(event);
+        final boolean hasFakePlayer = context.containsKey(EventContextKeys.FAKE_PLAYER);
+
+        if (user != null) {
+            if (context.containsKey(EventContextKeys.PISTON_RETRACT)) {
+                return;
+            }
+        }
+
+        final LocatableBlock locatableBlock = cause.first(LocatableBlock.class).orElse(null);
+        final TileEntity tileEntity = cause.first(TileEntity.class).orElse(null);
+        Entity sourceEntity = null;
+        // Always use TE as source if available
+        final Object source = tileEntity != null ? tileEntity : cause.root();
+        Location<World> sourceLocation = locatableBlock != null ? locatableBlock.getLocation() : tileEntity != null ? tileEntity.getLocation() : null;
+        if (sourceLocation == null && source instanceof Entity) {
+            // check entity
+            sourceEntity = ((Entity) source);
+            sourceLocation = sourceEntity.getLocation();
+        }
+        final boolean pistonExtend = context.containsKey(EventContextKeys.PISTON_EXTEND);
+        final boolean isLiquidSource = context.containsKey(EventContextKeys.LIQUID_FLOW);
+        final boolean isFireSource = isLiquidSource ? false : context.containsKey(EventContextKeys.FIRE_SPREAD);
+        final boolean isLeafDecay = context.containsKey(EventContextKeys.LEAVES_DECAY);
+        if (!GDFlags.LEAF_DECAY && isLeafDecay) {
+            return;
+        }
+        if (!GDFlags.LIQUID_FLOW && isLiquidSource) {
+            return;
+        }
+        if (!GDFlags.BLOCK_SPREAD && isFireSource) {
+            return;
+        }
+
+        lastBlockPreCancelled = false;
+        final boolean isForgePlayerBreak = context.containsKey(EventContextKeys.PLAYER_BREAK);
+        GDTimings.BLOCK_PRE_EVENT.startTimingIfSync();
+        // Handle player block breaks separately
+        if (isForgePlayerBreak && !hasFakePlayer && source instanceof Player) {
+            final Player player = (Player) source;
+            GDClaim targetClaim = null;
+            final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getPlayerData(world, player.getUniqueId());
+            for (Location<World> location : event.getLocations()) {
+                if (GriefDefenderPlugin.isTargetIdBlacklisted(Flags.BLOCK_BREAK.getName(), location.getBlock(), world.getProperties())) {
+                   GDTimings.BLOCK_PRE_EVENT.stopTimingIfSync();
+                   return;
+                }
+
+                targetClaim = this.dataStore.getClaimAt(location, targetClaim);
+                if (location.getBlockType() == BlockTypes.AIR) {
+                    continue;
+                }
+                if (!checkSurroundings(event, location, player, playerData, targetClaim)) {
+                    event.setCancelled(true);
+                    GDTimings.BLOCK_PRE_EVENT.stopTimingIfSync();
+                    return;
+                }
+
+                // check overrides
+                final Tristate result = GDPermissionManager.getInstance().getFinalPermission(event, location, targetClaim, GDPermissions.BLOCK_BREAK, source, location.getBlock(), player, TrustTypes.BUILDER, true);
+                if (result != Tristate.TRUE) {
+                    final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.PERMISSION_BUILD,
+                            ImmutableMap.of(
+                            "player", targetClaim.getOwnerName()));
+                    GriefDefenderPlugin.sendClaimDenyMessage(targetClaim, player, message);
+                    event.setCancelled(true);
+                    lastBlockPreCancelled = true;
+                    GDTimings.BLOCK_PRE_EVENT.stopTimingIfSync();
+                    return;
+                }
+            }
+
+            GDTimings.BLOCK_PRE_EVENT.stopTimingIfSync();
+            return;
+        }
+
+        if (sourceLocation != null) {
+            GDPlayerData playerData = null;
+            if (user != null) {
+                playerData = GriefDefenderPlugin.getInstance().dataStore.getPlayerData(world, user.getUniqueId());
+            }
+            GDClaim sourceClaim = this.dataStore.getClaimAt(sourceLocation);
+            GDClaim targetClaim = null;
+            List<Location<World>> sourceLocations = event.getLocations();
+            if (pistonExtend) {
+                // check next block in extend direction
+                sourceLocations = new ArrayList<>(event.getLocations());
+                Location<World> location = sourceLocations.get(sourceLocations.size() - 1);
+                final Direction direction = locatableBlock.getLocation().getBlock().get(Keys.DIRECTION).get();
+                final Location<World> dirLoc = location.getBlockRelative(direction);
+                sourceLocations.add(dirLoc);
+            }
+            for (Location<World> location : sourceLocations) {
+                // Mods such as enderstorage will send chest updates to itself
+                // We must ignore cases like these to avoid issues with mod
+                if (tileEntity != null) {
+                    if (location.getPosition().equals(tileEntity.getLocation().getPosition())) {
+                        continue;
+                    }
+                }
+
+                final BlockState blockState = location.getBlock();
+                targetClaim = this.dataStore.getClaimAt(location, targetClaim);
+                // If a player successfully interacted with a block recently such as a pressure plate, ignore check
+                // This fixes issues such as pistons not being able to extend
+                if (user != null && !isForgePlayerBreak && playerData != null && playerData.eventResultCache != null && playerData.eventResultCache.checkEventResultCache(targetClaim, "block-pre") == Tristate.TRUE) {
+                    GDPermissionManager.getInstance().addEventLogEntry(event, location, source, blockState, user, GDPermissions.BLOCK_BREAK, playerData.eventResultCache.lastTrust, Tristate.TRUE);
+                    continue;
+                }
+                if (user != null && targetClaim.isUserTrusted(user, TrustTypes.BUILDER)) {
+                    GDPermissionManager.getInstance().addEventLogEntry(event, location, source, blockState, user, GDPermissions.BLOCK_BREAK, TrustTypes.BUILDER.getName().toLowerCase(), Tristate.TRUE);
+                    continue;
+                }
+                if (sourceClaim.getOwnerUniqueId().equals(targetClaim.getOwnerUniqueId()) && user == null && sourceEntity == null && !isFireSource && !isLeafDecay) {
+                    GDPermissionManager.getInstance().addEventLogEntry(event, location, source, blockState, user, GDPermissions.BLOCK_BREAK, "owner", Tristate.TRUE);
+                    continue;
+                }
+                if (user != null && pistonExtend) {
+                    if (targetClaim.isUserTrusted(user, TrustTypes.ACCESSOR)) {
+                        GDPermissionManager.getInstance().addEventLogEntry(event, location, source, blockState, user, GDPermissions.BLOCK_BREAK, TrustTypes.ACCESSOR.getName().toLowerCase(), Tristate.TRUE);
+                        continue;
+                    }
+                }
+                if (isLeafDecay) {
+                    if (GDPermissionManager.getInstance().getFinalPermission(event, location, targetClaim, GDPermissions.LEAF_DECAY, source, blockState, user) == Tristate.FALSE) {
+                        event.setCancelled(true);
+                        GDTimings.BLOCK_PRE_EVENT.stopTimingIfSync();
+                        return;
+                    }
+                } else if (isFireSource) {
+                    if (GDPermissionManager.getInstance().getFinalPermission(event, location, targetClaim, GDPermissions.BLOCK_SPREAD, source, blockState, user) == Tristate.FALSE) {
+                        event.setCancelled(true);
+                        GDTimings.BLOCK_PRE_EVENT.stopTimingIfSync();
+                        return;
+                    }
+                } else if (isLiquidSource) {
+                    if (GDPermissionManager.getInstance().getFinalPermission(event, location, targetClaim, GDPermissions.LIQUID_FLOW, source, blockState, user) == Tristate.FALSE) {
+                        event.setCancelled(true);
+                        lastBlockPreCancelled = true;
+                        GDTimings.BLOCK_PRE_EVENT.stopTimingIfSync();
+                        return;
+                    }
+                    continue;
+                } else if (GDPermissionManager.getInstance().getFinalPermission(event, location, targetClaim, GDPermissions.BLOCK_BREAK, source, blockState, user) == Tristate.FALSE) {
+                    // PRE events can be spammy so we need to avoid sending player messages here.
+                    event.setCancelled(true);
+                    lastBlockPreCancelled = true;
+                    GDTimings.BLOCK_PRE_EVENT.stopTimingIfSync();
+                    return;
+                }
+            }
+        } else if (user != null) {
+            final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getPlayerData(world, user.getUniqueId());
+            GDClaim targetClaim = null;
+            for (Location<World> location : event.getLocations()) {
+                // Mods such as enderstorage will send chest updates to itself
+                // We must ignore cases like these to avoid issues with mod
+                if (tileEntity != null) {
+                    if (location.getPosition().equals(tileEntity.getLocation().getPosition())) {
+                        continue;
+                    }
+                }
+
+                final BlockState blockState = location.getBlock();
+                targetClaim = this.dataStore.getClaimAt(location, targetClaim);
+                // If a player successfully interacted with a block recently such as a pressure plate, ignore check
+                // This fixes issues such as pistons not being able to extend
+                if (!isForgePlayerBreak && playerData != null && playerData.eventResultCache != null && playerData.eventResultCache.checkEventResultCache(targetClaim, "block-pre") == Tristate.TRUE) {
+                    GDPermissionManager.getInstance().addEventLogEntry(event, location, source, blockState, user, GDPermissions.BLOCK_BREAK, playerData.eventResultCache.lastTrust, Tristate.TRUE);
+                    continue;
+                }
+                if (targetClaim.isUserTrusted(user, TrustTypes.BUILDER)) {
+                    GDPermissionManager.getInstance().addEventLogEntry(event, location, source, blockState, user, GDPermissions.BLOCK_BREAK, TrustTypes.BUILDER.getName().toLowerCase(), Tristate.TRUE);
+                    continue;
+                }
+
+                if (isFireSource) {
+                    if (GDPermissionManager.getInstance().getFinalPermission(event, location, targetClaim, GDPermissions.BLOCK_SPREAD, source, blockState, user) == Tristate.FALSE) {
+                        event.setCancelled(true);
+                        GDTimings.BLOCK_PRE_EVENT.stopTimingIfSync();
+                        return;
+                    }
+                } else if (isLiquidSource) {
+                    if (GDPermissionManager.getInstance().getFinalPermission(event, location, targetClaim, GDPermissions.LIQUID_FLOW, source, blockState, user) == Tristate.FALSE) {
+                        event.setCancelled(true);
+                        lastBlockPreCancelled = true;
+                        GDTimings.BLOCK_PRE_EVENT.stopTimingIfSync();
+                        return;
+                    }
+                    continue;
+                } else if (GDPermissionManager.getInstance().getFinalPermission(event, location, targetClaim, GDPermissions.BLOCK_BREAK, source, blockState, user) == Tristate.FALSE) {
+                    event.setCancelled(true);
+                    lastBlockPreCancelled = true;
+                    GDTimings.BLOCK_PRE_EVENT.stopTimingIfSync();
+                    return;
+                }
+            }
+        }
+        GDTimings.BLOCK_PRE_EVENT.stopTimingIfSync();
+    }
+
+    // Handle fluids flowing into claims
+    @Listener(order = Order.FIRST, beforeModifications = true)
+    public void onBlockNotify(NotifyNeighborBlockEvent event) {
+        LocatableBlock locatableBlock = event.getCause().first(LocatableBlock.class).orElse(null);
+        TileEntity tileEntity = event.getCause().first(TileEntity.class).orElse(null);
+        Location<World> sourceLocation = locatableBlock != null ? locatableBlock.getLocation() : tileEntity != null ? tileEntity.getLocation() : null;
+        GDClaim sourceClaim = null;
+        GDPlayerData playerData = null;
+        if (sourceLocation != null) {
+            if (GriefDefenderPlugin.isSourceIdBlacklisted("block-notify", event.getSource(), sourceLocation.getExtent().getProperties())) {
+                return;
+            }
+        }
+
+        final User user = CauseContextHelper.getEventUser(event);
+        if (user == null) {
+            return;
+        }
+        if (sourceLocation == null) {
+            Player player = event.getCause().first(Player.class).orElse(null);
+            if (player == null) {
+                return;
+            }
+
+            sourceLocation = player.getLocation();
+            playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+            sourceClaim = this.dataStore.getClaimAtPlayer(playerData, player.getLocation());
+        } else {
+            playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(sourceLocation.getExtent(), user.getUniqueId());
+            sourceClaim = this.dataStore.getClaimAt(sourceLocation, playerData.lastClaim.get());
+        }
+
+        if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(sourceLocation.getExtent().getUniqueId())) {
+            return;
+        }
+
+        GDTimings.BLOCK_NOTIFY_EVENT.startTimingIfSync();
+        Iterator<Direction> iterator = event.getNeighbors().keySet().iterator();
+        GDClaim targetClaim = null;
+        while (iterator.hasNext()) {
+            Direction direction = iterator.next();
+            Location<World> location = sourceLocation.getBlockRelative(direction);
+            Vector3i pos = location.getBlockPosition();
+            targetClaim = this.dataStore.getClaimAt(location, targetClaim);
+            if (sourceClaim.isWilderness() && targetClaim.isWilderness()) {
+                if (playerData != null) {
+                    playerData.eventResultCache = new EventResultCache(targetClaim, "block-notify", Tristate.TRUE);
+                }
+                continue;
+            } else if (!sourceClaim.isWilderness() && targetClaim.isWilderness()) {
+                if (playerData != null) {
+                    playerData.eventResultCache = new EventResultCache(targetClaim, "block-notify", Tristate.TRUE);
+                }
+                continue;
+            } else if (sourceClaim.getUniqueId().equals(targetClaim.getUniqueId())) {
+                if (playerData != null) {
+                    playerData.eventResultCache = new EventResultCache(targetClaim, "block-notify", Tristate.TRUE);
+                }
+                continue;
+            } else {
+                if (playerData.eventResultCache != null && playerData.eventResultCache.checkEventResultCache(targetClaim, "block-notify") == Tristate.TRUE) {
+                    continue;
+                }
+                // Needed to handle levers notifying doors to open etc.
+                if (targetClaim.isUserTrusted(user, TrustTypes.ACCESSOR)) {
+                    if (playerData != null) {
+                        playerData.eventResultCache = new EventResultCache(targetClaim, "block-notify", Tristate.TRUE, TrustTypes.ACCESSOR.getName().toLowerCase());
+                    }
+                    continue;
+                }
+            }
+
+            // no claim crossing unless trusted
+            iterator.remove();
+        }
+        GDTimings.BLOCK_NOTIFY_EVENT.stopTimingIfSync();
+    }
+
+    @Listener(order = Order.FIRST, beforeModifications = true)
+    public void onBlockCollide(CollideBlockEvent event, @Root Entity source) {
+        if (event instanceof CollideBlockEvent.Impact) {
+            return;
+        }
+        // ignore falling blocks
+        if (!GDFlags.COLLIDE_BLOCK || source instanceof FallingBlock) {
+            return;
+        }
+        if (GriefDefenderPlugin.isSourceIdBlacklisted(Flags.COLLIDE_BLOCK.getName(), source.getType().getId(), source.getWorld().getProperties())) {
+            return;
+        }
+        if (GriefDefenderPlugin.isTargetIdBlacklisted(Flags.COLLIDE_BLOCK.getName(), event.getTargetBlock(), source.getWorld().getProperties())) {
+            return;
+        }
+
+        final User user = CauseContextHelper.getEventUser(event);
+        if (user == null) {
+            return;
+        }
+
+        GDTimings.BLOCK_COLLIDE_EVENT.startTimingIfSync();
+        final BlockType blockType = event.getTargetBlock().getType();
+        if (blockType.equals(BlockTypes.AIR) 
+                || !GriefDefenderPlugin.getInstance().claimsEnabledForWorld(event.getTargetLocation().getExtent().getUniqueId())) {
+            GDTimings.BLOCK_COLLIDE_EVENT.stopTimingIfSync();
+            return;
+        }
+
+        if (source instanceof Item && (blockType != BlockTypes.PORTAL && !NMSUtil.getInstance().isBlockPressurePlate(blockType))) {
+            GDTimings.BLOCK_COLLIDE_EVENT.stopTimingIfSync();
+            return;
+        }
+
+        Vector3i collidePos = event.getTargetLocation().getBlockPosition();
+        short shortPos = BlockUtil.getInstance().blockPosToShort(collidePos);
+        int entityId = NMSUtil.getInstance().getEntityMinecraftId(source);
+        BlockPosCache entityBlockCache = BlockUtil.ENTITY_BLOCK_CACHE.get(entityId);
+        if (entityBlockCache == null) {
+            entityBlockCache = new BlockPosCache(shortPos);
+            BlockUtil.ENTITY_BLOCK_CACHE.put(entityId, entityBlockCache);
+        } else {
+            Tristate result = entityBlockCache.getCacheResult(shortPos);
+            if (result != Tristate.UNDEFINED) {
+                if (result == Tristate.FALSE) {
+                    event.setCancelled(true);
+                }
+
+                GDTimings.BLOCK_COLLIDE_EVENT.stopTimingIfSync();
+                return;
+            }
+        }
+
+        GDPlayerData playerData = null;
+        GDClaim targetClaim = null;
+        if (user instanceof Player) {
+            playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(event.getTargetLocation().getExtent(), user.getUniqueId());
+            targetClaim = this.dataStore.getClaimAtPlayer(playerData, event.getTargetLocation());
+        } else {
+            targetClaim = this.dataStore.getClaimAt(event.getTargetLocation());
+        }
+
+        if (GDPermissionManager.getInstance().getFinalPermission(event, event.getTargetLocation(), targetClaim, GDPermissions.COLLIDE_BLOCK, source, event.getTargetBlock(), user, TrustTypes.ACCESSOR, true) == Tristate.TRUE) {
+            entityBlockCache.setLastResult(Tristate.TRUE);
+            GDTimings.BLOCK_COLLIDE_EVENT.stopTimingIfSync();
+            return;
+        }
+        if (GDFlags.PORTAL_USE && event.getTargetBlock().getType() == BlockTypes.PORTAL) {
+            if (GDPermissionManager.getInstance().getFinalPermission(event, event.getTargetLocation(), targetClaim, GDPermissions.PORTAL_USE, source, event.getTargetBlock(), user, TrustTypes.ACCESSOR, true) == Tristate.TRUE) {
+                GDTimings.BLOCK_COLLIDE_EVENT.stopTimingIfSync();
+                return;
+            }
+            if (event.getCause().root() instanceof Player){
+                if (event.getTargetLocation().getExtent().getProperties().getTotalTime() % 20 == 0L) { // log once a second to avoid spam
+                    // Disable message temporarily
+                    //GriefDefender.sendMessage((Player) user, TextMode.Err, Messages.NoPortalFromProtectedClaim, claim.getOwnerName());
+                    /*final Text message = GriefDefenderPlugin.getInstance().messageData.permissionProtectedPortal
+                            .apply(ImmutableMap.of(
+                            "owner", targetClaim.getOwnerName())).build();*/
+                    event.setCancelled(true);
+                    entityBlockCache.setLastResult(Tristate.FALSE);
+                    GDTimings.BLOCK_COLLIDE_EVENT.stopTimingIfSync();
+                    return;
+                }
+            }
+        }
+
+        GDTimings.BLOCK_COLLIDE_EVENT.stopTimingIfSync();
+    }
+
+    @Listener(order = Order.FIRST, beforeModifications = true)
+    public void onProjectileImpactBlock(CollideBlockEvent.Impact event) {
+        if (!GDFlags.PROJECTILE_IMPACT_BLOCK || !(event.getSource() instanceof Entity)) {
+            return;
+        }
+
+        final Entity source = (Entity) event.getSource();
+        if (GriefDefenderPlugin.isSourceIdBlacklisted(Flags.PROJECTILE_IMPACT_BLOCK.getName(), source.getType().getId(), source.getWorld().getProperties())) {
+            return;
+        }
+
+        final User user = CauseContextHelper.getEventUser(event);
+        if (user == null) {
+            return;
+        }
+
+        if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(event.getImpactPoint().getExtent().getUniqueId())) {
+            return;
+        }
+
+        GDTimings.PROJECTILE_IMPACT_BLOCK_EVENT.startTimingIfSync();
+        Location<World> impactPoint = event.getImpactPoint();
+        GDClaim targetClaim = null;
+        GDPlayerData playerData = null;
+        if (user instanceof Player) {
+            playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(event.getTargetLocation().getExtent(), user.getUniqueId());
+            targetClaim = this.dataStore.getClaimAtPlayer(playerData, impactPoint);
+        } else {
+            targetClaim = this.dataStore.getClaimAt(impactPoint);
+        }
+
+        Tristate result = GDPermissionManager.getInstance().getFinalPermission(event, impactPoint, targetClaim, GDPermissions.PROJECTILE_IMPACT_BLOCK, source, event.getTargetBlock(), user, TrustTypes.ACCESSOR, true);
+        if (result == Tristate.FALSE) {
+            event.setCancelled(true);
+            GDTimings.PROJECTILE_IMPACT_BLOCK_EVENT.stopTimingIfSync();
+            return;
+        }
+
+        GDTimings.PROJECTILE_IMPACT_BLOCK_EVENT.stopTimingIfSync();
+    }
+
+    @Listener(order = Order.FIRST, beforeModifications = true)
+    public void onExplosionPre(ExplosionEvent.Pre event) {
+        final World world = event.getExplosion().getWorld();
+        if (!GDFlags.EXPLOSION_BLOCK || !GriefDefenderPlugin.getInstance().claimsEnabledForWorld(world.getUniqueId())) {
+            return;
+        }
+
+        Object source = event.getSource();
+        final Explosion explosion = event.getExplosion();
+        if (explosion.getSourceExplosive().isPresent()) {
+            source = explosion.getSourceExplosive().get();
+        } else {
+            Entity exploder = event.getCause().first(Entity.class).orElse(null);
+            if (exploder != null) {
+                source = exploder;
+            }
+        }
+
+        if (GriefDefenderPlugin.isSourceIdBlacklisted(Flags.EXPLOSION_BLOCK.getName(), source, event.getExplosion().getWorld().getProperties())) {
+            return;
+        }
+
+        GDTimings.EXPLOSION_PRE_EVENT.startTimingIfSync();
+        final User user = CauseContextHelper.getEventUser(event);
+        final Location<World> location = event.getExplosion().getLocation();
+        final GDClaim radiusClaim = NMSUtil.getInstance().createClaimFromCenter(location, event.getExplosion().getRadius());
+        final GDClaimManager claimManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(location.getExtent().getUniqueId());
+        final Set<Claim> surroundingClaims = claimManager.findOverlappingClaims(radiusClaim);
+        if (surroundingClaims.size() == 0) {
+            return;
+        }
+        for (Claim claim : surroundingClaims) {
+            // Use any location for permission check
+            Location<World> targetLocation = new Location<>(location.getExtent(), claim.getLesserBoundaryCorner());
+            Tristate result = GDPermissionManager.getInstance().getFinalPermission(event, location, claim, GDPermissions.EXPLOSION_BLOCK, source, targetLocation, user, true);
+            if (result == Tristate.FALSE) {
+                event.setCancelled(true);
+                break;
+            }
+        }
+
+        GDTimings.EXPLOSION_PRE_EVENT.stopTimingIfSync();
+    }
+
+    @Listener(order = Order.FIRST, beforeModifications = true)
+    public void onExplosionDetonate(ExplosionEvent.Detonate event) {
+        final World world = event.getExplosion().getWorld();
+        if (!GDFlags.EXPLOSION_BLOCK || !GriefDefenderPlugin.getInstance().claimsEnabledForWorld(world.getUniqueId())) {
+            return;
+        }
+
+        Object source = event.getSource();
+        if (source instanceof Explosion) {
+            final Explosion explosion = (Explosion) source;
+            if (explosion.getSourceExplosive().isPresent()) {
+                source = explosion.getSourceExplosive().get();
+            } else {
+                Entity exploder = event.getCause().first(Entity.class).orElse(null);
+                if (exploder != null) {
+                    source = exploder;
+                }
+            }
+        }
+        if (GriefDefenderPlugin.isSourceIdBlacklisted(Flags.EXPLOSION_BLOCK.getName(), source, event.getExplosion().getWorld().getProperties())) {
+            return;
+        }
+
+        GDTimings.EXPLOSION_EVENT.startTimingIfSync();
+        final User user = CauseContextHelper.getEventUser(event);
+        GDClaim targetClaim = null;
+        final List<Location<World>> filteredLocations = new ArrayList<>();
+        for (Location<World> location : event.getAffectedLocations()) {
+            targetClaim =  GriefDefenderPlugin.getInstance().dataStore.getClaimAt(location, targetClaim);
+            /*if (location.getPosition().getY() > ((net.minecraft.world.World) world).getSeaLevel() && !GriefDefenderPlugin.getActiveConfig(world.getUniqueId()).getConfig().claim.explosionSurface) {
+                filteredLocations.add(location);
+                continue;
+            }*/
+
+            Tristate result = GDPermissionManager.getInstance().getFinalPermission(event, location, targetClaim, GDPermissions.EXPLOSION_BLOCK, source, location.getBlock(), user, true);
+
+            if (result == Tristate.FALSE) {
+                // Avoid lagging server from large explosions.
+                if (event.getAffectedLocations().size() > 100) {
+                    event.setCancelled(true);
+                    break;
+                }
+                filteredLocations.add(location);
+            }
+        }
+        // Workaround for SpongeForge bug
+        if (event.isCancelled()) {
+            event.getAffectedLocations().clear();
+        } else if (!filteredLocations.isEmpty()) {
+            event.getAffectedLocations().removeAll(filteredLocations);
+        }
+        GDTimings.EXPLOSION_EVENT.stopTimingIfSync();
+    }
+
+    @Listener(order = Order.FIRST, beforeModifications = true)
+    public void onBlockBreak(ChangeBlockEvent.Break event) {
+        if (!GDFlags.BLOCK_BREAK || event instanceof ExplosionEvent) {
+            return;
+        }
+
+        if (lastBlockPreTick == Sponge.getServer().getRunningTimeTicks()) {
+            event.setCancelled(lastBlockPreCancelled);
+            return;
+        }
+
+        Object source = event.getSource();
+        // Handled in Explosion listeners
+        if (source instanceof Explosion) {
+            return;
+        }
+
+        // Pistons are handled in onBlockPre
+        if (source == BlockTypes.PISTON || source instanceof Piston) {
+            return;
+        }
+
+        final World world = event.getTransactions().get(0).getFinal().getLocation().get().getExtent();
+        if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(world.getUniqueId())) {
+            return;
+        }
+
+        if (GriefDefenderPlugin.isSourceIdBlacklisted(Flags.BLOCK_BREAK.getName(), source, world.getProperties())) {
+            return;
+        }
+
+        final Player player = source instanceof Player ? (Player) source : null;
+        final User user = player != null ? player : CauseContextHelper.getEventUser(event);
+
+        // ignore falling blocks when there is no user
+        // avoids dupes with falling blocks such as Dragon Egg
+        if (user == null && source instanceof FallingBlock) {
+            return;
+        }
+        GDClaim sourceClaim = null;
+        LocatableBlock locatable = null;
+        if (source instanceof LocatableBlock) {
+            locatable = (LocatableBlock) source;
+            sourceClaim = this.dataStore.getClaimAt(locatable.getLocation());
+        } else {
+            sourceClaim = this.getSourceClaim(event.getCause());
+        }
+        if (sourceClaim == null) {
+            return;
+        }
+
+        GDTimings.BLOCK_BREAK_EVENT.startTimingIfSync();
+        List<Transaction<BlockSnapshot>> transactions = event.getTransactions();
+        GDClaim targetClaim = null;
+        for (Transaction<BlockSnapshot> transaction : transactions) {
+            if (GriefDefenderPlugin.isTargetIdBlacklisted(Flags.BLOCK_BREAK.getName(), transaction.getOriginal(), world.getProperties())) {
+                continue;
+            }
+
+            Location<World> location = transaction.getOriginal().getLocation().orElse(null);
+            targetClaim = this.dataStore.getClaimAt(location, targetClaim);
+            if (location == null || transaction.getOriginal().getState().getType() == BlockTypes.AIR) {
+                continue;
+            }
+
+            // check overrides
+            final Tristate result = GDPermissionManager.getInstance().getFinalPermission(event, location, targetClaim, GDPermissions.BLOCK_BREAK, source, transaction.getOriginal(), user, TrustTypes.BUILDER, true);
+            if (result != Tristate.TRUE) {
+                if (player != null) {
+                    final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.PERMISSION_BUILD,
+                            ImmutableMap.of("player", targetClaim.getOwnerName()));
+                    GriefDefenderPlugin.sendClaimDenyMessage(targetClaim, player, message);
+                }
+
+                event.setCancelled(true);
+                GDTimings.BLOCK_BREAK_EVENT.stopTimingIfSync();
+                return;
+            }
+        }
+        GDTimings.BLOCK_BREAK_EVENT.stopTimingIfSync();
+    }
+
+    @Listener(order = Order.FIRST, beforeModifications = true)
+    public void onBlockPlace(ChangeBlockEvent.Place event) {
+        final Object source = event.getSource();
+        // Pistons are handled in onBlockPre
+        if (source instanceof Piston) {
+            return;
+        }
+
+        final World world = event.getTransactions().get(0).getFinal().getLocation().get().getExtent();
+        if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(world.getUniqueId())) {
+            return;
+        }
+        if (GriefDefenderPlugin.isSourceIdBlacklisted(Flags.BLOCK_PLACE.getName(), event.getSource(), world.getProperties())) {
+            return;
+        }
+
+        GDTimings.BLOCK_PLACE_EVENT.startTimingIfSync();
+        GDClaim sourceClaim = null;
+        LocatableBlock locatable = null;
+        final User user = CauseContextHelper.getEventUser(event);
+        if (source instanceof LocatableBlock) {
+            locatable = (LocatableBlock) source;
+            if (user != null && user instanceof Player) {
+                final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(locatable.getWorld(), user.getUniqueId());
+                sourceClaim = this.dataStore.getClaimAt(locatable.getLocation(), playerData.lastClaim.get());
+            } else {
+                sourceClaim = this.dataStore.getClaimAt(locatable.getLocation());
+            }
+        } else {
+            sourceClaim = this.getSourceClaim(event.getCause());
+        }
+        if (sourceClaim == null) {
+            GDTimings.BLOCK_PLACE_EVENT.stopTimingIfSync();
+            return;
+        }
+
+        Player player = user != null && user instanceof Player ? (Player) user : null;
+        GDPlayerData playerData = null;
+        if (user != null) {
+            playerData = this.dataStore.getOrCreatePlayerData(world, user.getUniqueId());
+        }
+
+        GriefDefenderConfig<?> activeConfig = GriefDefenderPlugin.getActiveConfig(world.getProperties());
+        if (sourceClaim != null && !(source instanceof User) && playerData != null && playerData.eventResultCache != null && playerData.eventResultCache.checkEventResultCache(sourceClaim, Flags.BLOCK_PLACE.getName()) == Tristate.TRUE) {
+            GDTimings.BLOCK_PLACE_EVENT.stopTimingIfSync();
+            return;
+        }
+
+        GDClaim targetClaim = null;
+        for (Transaction<BlockSnapshot> transaction : event.getTransactions()) {
+            final BlockSnapshot block = transaction.getFinal();
+            if (GriefDefenderPlugin.isTargetIdBlacklisted(Flags.BLOCK_PLACE.getName(), block, world.getProperties())) {
+                continue;
+            }
+
+            Location<World> location = block.getLocation().orElse(null);
+            if (location == null) {
+                continue;
+            }
+
+            targetClaim = this.dataStore.getClaimAt(location, targetClaim);
+            if (!checkSurroundings(event, location, player, playerData, targetClaim)) {
+                event.setCancelled(true);
+                GDTimings.BLOCK_PLACE_EVENT.stopTimingIfSync();
+                return;
+            }
+
+            if (GDFlags.BLOCK_PLACE) {
+                // Allow blocks to grow within claims
+                if (user == null && sourceClaim != null && sourceClaim.getUniqueId().equals(targetClaim.getUniqueId())) {
+                    GDTimings.BLOCK_PLACE_EVENT.stopTimingIfSync();
+                    return;
+                }
+
+                // check overrides
+                final Tristate result = GDPermissionManager.getInstance().getFinalPermission(event, location, targetClaim, GDPermissions.BLOCK_PLACE, source, block, user, TrustTypes.BUILDER, true);
+                if (result != Tristate.TRUE) {
+                    // TODO - make sure this doesn't spam
+                    /*if (source instanceof Player) {
+                        final Text message = GriefDefenderPlugin.getInstance().messageData.permissionBuild
+                                .apply(ImmutableMap.of(
+                                "player", Text.of(targetClaim.getOwnerName())
+                        )).build();
+                        GriefDefenderPlugin.sendClaimDenyMessage(targetClaim, (Player) source, message);
+                    }*/
+                    event.setCancelled(true);
+                    GDTimings.BLOCK_PLACE_EVENT.stopTimingIfSync();
+                    return;
+                }
+            }
+
+            if (!(source instanceof Player)) {
+                continue;
+            }
+
+            if (targetClaim.isWilderness() && activeConfig.getConfig().claim.autoChestClaimBlockRadius > -1) {
+                TileEntity tileEntity = block.getLocation().get().getTileEntity().orElse(null);
+                if (tileEntity == null || !(tileEntity instanceof Chest)) {
+                    GDTimings.BLOCK_PLACE_EVENT.stopTimingIfSync();
+                    return;
+                }
+
+                final int minClaimLevel = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), user, Options.MIN_LEVEL).intValue();
+                final int maxClaimLevel = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), user, Options.MAX_LEVEL).intValue();
+                if (block.getPosition().getY() < minClaimLevel || block.getPosition().getY() > maxClaimLevel) {
+                    final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.CLAIM_CHEST_OUTSIDE_LEVEL,
+                            ImmutableMap.of(
+                            "min-claim-level", minClaimLevel,
+                            "max-claim-level", maxClaimLevel));
+                    GriefDefenderPlugin.sendMessage(player, message);
+                    GDTimings.BLOCK_PLACE_EVENT.stopTimingIfSync();
+                    return;
+                }
+
+                int radius = activeConfig.getConfig().claim.autoChestClaimBlockRadius;
+
+                if (playerData.getInternalClaims().size() == 0) {
+                    if (activeConfig.getConfig().claim.autoChestClaimBlockRadius == 0) {
+                        GDCauseStackManager.getInstance().pushCause(player);
+                        final ClaimResult result = GriefDefender.getRegistry().createBuilder(Claim.Builder.class)
+                                .bounds(block.getPosition(), block.getPosition())
+                                .cuboid(false)
+                                .owner(player.getUniqueId())
+                                .sizeRestrictions(false)
+                                .type(ClaimTypes.BASIC)
+                                .world(block.getLocation().get().getExtent().getUniqueId())
+                                .build();
+                        GDCauseStackManager.getInstance().popCause();
+                        if (result.successful()) {
+                            final Claim claim = result.getClaim().get();
+                            final GDClaimManager claimManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(world.getUniqueId());
+                            claimManager.addClaim(claim, true);
+                            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().CLAIM_CHEST_CONFIRMATION);
+                            GDTimings.BLOCK_PLACE_EVENT.stopTimingIfSync();
+                            return;
+                        }
+                    } else {
+                        Vector3i lesserBoundary = new Vector3i(
+                            block.getPosition().getX() - radius,
+                            minClaimLevel,
+                            block.getPosition().getZ() - radius);
+                        Vector3i greaterBoundary = new Vector3i(
+                            block.getPosition().getX() + radius,
+                            maxClaimLevel,
+                            block.getPosition().getZ() + radius);
+
+                        while (radius >= 0) {
+                            GDCauseStackManager.getInstance().pushCause(player);
+                            ClaimResult result = GriefDefender.getRegistry().createBuilder(Claim.Builder.class)
+                                    .bounds(lesserBoundary, greaterBoundary)
+                                    .cuboid(false)
+                                    .owner(player.getUniqueId())
+                                    .sizeRestrictions(false)
+                                    .type(ClaimTypes.BASIC)
+                                    .world(block.getLocation().get().getExtent().getUniqueId())
+                                    .build();
+                            GDCauseStackManager.getInstance().popCause();
+                            if (!result.successful()) {
+                                radius--;
+                            } else {
+                                GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().CLAIM_AUTOMATIC_NOTIFICATION);
+                                GDClaim newClaim = this.dataStore.getClaimAt(block.getLocation().get());
+                                ClaimVisual visualization = new ClaimVisual(newClaim, ClaimVisual.BASIC);
+                                visualization.createClaimBlockVisuals(block.getPosition().getY(), player.getLocation(), playerData);
+                                visualization.apply(player);
+
+                                GDTimings.BLOCK_PLACE_EVENT.stopTimingIfSync();
+                                return;
+                            }
+                        }
+                    }
+                }
+
+                if (targetClaim.isWilderness() && player.hasPermission(GDPermissions.CLAIM_SHOW_TUTORIAL)) {
+                    GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.TUTORIAL_CLAIM_BASIC));
+                }
+            }
+        }
+
+        GDTimings.BLOCK_PLACE_EVENT.stopTimingIfSync();
+    }
+
+    @Listener(order = Order.FIRST, beforeModifications = true)
+    public void onSignChanged(ChangeSignEvent event) {
+        final User user = CauseContextHelper.getEventUser(event);
+        if (user == null) {
+            return;
+        }
+
+        if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(event.getTargetTile().getLocation().getExtent().getUniqueId())) {
+            return;
+        }
+
+        GDTimings.SIGN_CHANGE_EVENT.startTimingIfSync();
+        Location<World> location = event.getTargetTile().getLocation();
+        // Prevent users exploiting signs
+        GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAt(location);
+        if (GDPermissionManager.getInstance().getFinalPermission(event, location, claim, GDPermissions.INTERACT_BLOCK_SECONDARY, user, location.getBlock(), user, TrustTypes.ACCESSOR, true) == Tristate.FALSE) {
+            if (user instanceof Player) {
+                event.setCancelled(true);
+                final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.PERMISSION_ACCESS,
+                        ImmutableMap.of("player", claim.getOwnerName()));
+                GriefDefenderPlugin.sendClaimDenyMessage(claim, (Player) user, message);
+                return;
+            }
+        }
+
+        GDTimings.SIGN_CHANGE_EVENT.stopTimingIfSync();
+    }
+
+    public GDClaim getSourceClaim(Cause cause) {
+        BlockSnapshot blockSource = cause.first(BlockSnapshot.class).orElse(null);
+        LocatableBlock locatableBlock = null;
+        TileEntity tileEntitySource = null;
+        Entity entitySource = null;
+        if (blockSource == null) {
+            locatableBlock = cause.first(LocatableBlock.class).orElse(null);
+            if (locatableBlock == null) {
+                entitySource = cause.first(Entity.class).orElse(null);
+            }
+            if (locatableBlock == null && entitySource == null) {
+                tileEntitySource = cause.first(TileEntity.class).orElse(null);
+            }
+        }
+
+        GDClaim sourceClaim = null;
+        if (blockSource != null) {
+            sourceClaim = this.dataStore.getClaimAt(blockSource.getLocation().get());
+        } else if (locatableBlock != null) {
+            sourceClaim = this.dataStore.getClaimAt(locatableBlock.getLocation());
+        } else if (tileEntitySource != null) {
+            sourceClaim = this.dataStore.getClaimAt(tileEntitySource.getLocation());
+        } else if (entitySource != null) {
+            Entity entity = entitySource;
+            if (entity instanceof Player) {
+                Player player = (Player) entity;
+                GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+                sourceClaim = this.dataStore.getClaimAtPlayer(playerData, player.getLocation());
+            } else {
+                sourceClaim = this.dataStore.getClaimAt(entity.getLocation());
+            }
+        }
+
+        return sourceClaim;
+    }
+
+    // TODO: Add configuration for distance between claims
+    private boolean checkSurroundings(org.spongepowered.api.event.Event event, Location<World> location, Player player, GDPlayerData playerData, GDClaim targetClaim) {
+        if (playerData == null) {
+            return true;
+        }
+        // Don't allow players to break blocks next to land they do not own
+        if (!playerData.canIgnoreClaim(targetClaim)) {
+            // check surrounding blocks for access
+            for (Direction direction : BlockUtil.CARDINAL_SET) {
+                Location<World> loc = location.getBlockRelative(direction);
+                if (!(loc.getTileEntity().isPresent())) {
+                    continue;
+                }
+                final GDClaim claim = this.dataStore.getClaimAt(loc, targetClaim);
+                if (!claim.isWilderness() && !targetClaim.equals(claim)) {
+                    Tristate result = GDPermissionManager.getInstance().getFinalPermission(event, loc, claim, GDPermissions.BLOCK_BREAK, player, loc.getBlock(), player, TrustTypes.BUILDER, true);
+                    if (result != Tristate.TRUE) {
+                        final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.PERMISSION_BUILD_NEAR_CLAIM,
+                                ImmutableMap.of(
+                                "player", claim.getOwnerName()));
+                        GriefDefenderPlugin.sendClaimDenyMessage(claim, player, message);
+                        return false;
+                    }
+                }
+            }
+        }
+        return true;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/listener/CommonEntityEventHandler.java b/sponge/src/main/java/com/griefdefender/listener/CommonEntityEventHandler.java
new file mode 100644
index 0000000..fb61623
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/listener/CommonEntityEventHandler.java
@@ -0,0 +1,385 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.listener;
+
+import java.lang.ref.WeakReference;
+
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.data.key.Keys;
+import org.spongepowered.api.entity.Entity;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.entity.living.player.gamemode.GameMode;
+import org.spongepowered.api.entity.living.player.gamemode.GameModes;
+import org.spongepowered.api.event.entity.MoveEntityEvent;
+import org.spongepowered.api.item.inventory.ItemStack;
+import org.spongepowered.api.text.Text;
+import org.spongepowered.api.world.Location;
+import org.spongepowered.api.world.World;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.reflect.TypeToken;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GDTimings;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.ChatType;
+import com.griefdefender.api.GriefDefender;
+import com.griefdefender.api.Tristate;
+import com.griefdefender.api.claim.TrustTypes;
+import com.griefdefender.api.permission.flag.Flags;
+import com.griefdefender.api.permission.option.Options;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.event.GDBorderClaimEvent;
+import com.griefdefender.internal.util.VecHelper;
+import com.griefdefender.permission.GDPermissionManager;
+import com.griefdefender.permission.GDPermissionUser;
+import com.griefdefender.permission.GDPermissions;
+import com.griefdefender.permission.flag.GDFlags;
+import com.griefdefender.provider.MCClansProvider;
+import com.griefdefender.storage.BaseStorage;
+import com.griefdefender.util.EntityUtils;
+import com.griefdefender.util.SpongeUtil;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import nl.riebie.mcclans.api.ClanPlayer;
+
+public class CommonEntityEventHandler {
+
+    private static CommonEntityEventHandler instance;
+
+    public static CommonEntityEventHandler getInstance() {
+        return instance;
+    }
+
+    static {
+        instance = new CommonEntityEventHandler();
+    }
+
+    private final BaseStorage storage;
+
+    public CommonEntityEventHandler() {
+        this.storage = GriefDefenderPlugin.getInstance().dataStore;
+    }
+
+    public boolean onEntityMove(MoveEntityEvent event, Location<World> fromLocation, Location<World> toLocation, Entity targetEntity){
+        if ((!GDFlags.ENTER_CLAIM && !GDFlags.EXIT_CLAIM) || fromLocation.getBlockPosition().equals(toLocation.getBlockPosition())) {
+            return true;
+        }
+
+        World world = targetEntity.getWorld();
+        if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(world.getUniqueId())) {
+            return true;
+        }
+        final boolean enterBlacklisted = GriefDefenderPlugin.isSourceIdBlacklisted(Flags.ENTER_CLAIM.getName(), targetEntity, world.getProperties());
+        final boolean exitBlacklisted = GriefDefenderPlugin.isSourceIdBlacklisted(Flags.EXIT_CLAIM.getName(), targetEntity, world.getProperties());
+        if (enterBlacklisted && exitBlacklisted) {
+            return true;
+        }
+
+        GDTimings.ENTITY_MOVE_EVENT.startTimingIfSync();
+        Player player = null;
+        GDPermissionUser user = null;
+        if (targetEntity instanceof Player) {
+            player = (Player) targetEntity;
+            user = PermissionHolderCache.getInstance().getOrCreateUser(player);
+        } else {
+            final Entity controller = EntityUtils.getControllingPassenger(targetEntity);
+            if (controller != null && controller instanceof Player) {
+                player = (Player) controller;
+            }
+            user = PermissionHolderCache.getInstance().getOrCreateUser(targetEntity.getCreator().orElse(null));
+        }
+
+        if (user != null) {
+            if (user.getInternalPlayerData().teleportDelay > 0) {
+                if (!toLocation.getBlockPosition().equals(VecHelper.toVector3i(user.getInternalPlayerData().teleportSourceLocation))) {
+                    user.getInternalPlayerData().teleportDelay = 0;
+                    TextAdapter.sendComponent(player, MessageCache.getInstance().TELEPORT_MOVE_CANCEL);
+                }
+            }
+        }
+
+        if (player == null && user == null) {
+            // Handle border event without player
+            GDClaim fromClaim = this.storage.getClaimAt(fromLocation);
+            GDClaim toClaim = this.storage.getClaimAt(toLocation);
+            if (fromClaim != toClaim) {
+                GDBorderClaimEvent gpEvent = new GDBorderClaimEvent(targetEntity, fromClaim, toClaim);
+                // enter
+                if (GDFlags.ENTER_CLAIM && !enterBlacklisted && GDPermissionManager.getInstance().getFinalPermission(event, toLocation, toClaim, GDPermissions.ENTER_CLAIM, targetEntity, targetEntity, user) == Tristate.FALSE) {
+                    gpEvent.cancelled(true);
+                    if (event != null) {
+                        event.setCancelled(true);
+                    }
+                }
+
+                // exit
+                if (GDFlags.EXIT_CLAIM && !exitBlacklisted && GDPermissionManager.getInstance().getFinalPermission(event, fromLocation, fromClaim, GDPermissions.EXIT_CLAIM, targetEntity, targetEntity, user) == Tristate.FALSE) {
+                    gpEvent.cancelled(true);
+                    if (event != null) {
+                        event.setCancelled(true);
+                    }
+                }
+
+                GriefDefender.getEventManager().post(gpEvent);
+                if (gpEvent.cancelled()) {
+                    if (event != null) {
+                        event.setCancelled(true);
+                    }
+                    if (!(targetEntity instanceof Player) && EntityUtils.getOwnerUniqueId(targetEntity) == null) {
+                        targetEntity.remove();
+                    }
+                    return false;
+                }
+            }
+            GDTimings.ENTITY_MOVE_EVENT.stopTimingIfSync();
+            return true;
+        }
+
+        GDClaim fromClaim = null;
+        GDClaim toClaim = this.storage.getClaimAt(toLocation);
+        if (user != null) {
+            fromClaim = this.storage.getClaimAtPlayer(user.getInternalPlayerData(), fromLocation);
+        } else {
+            fromClaim = this.storage.getClaimAt(fromLocation);
+        }
+
+        if (GDFlags.ENTER_CLAIM && !enterBlacklisted && user != null && user.getInternalPlayerData().lastClaim != null) {
+            final GDClaim lastClaim = (GDClaim) user.getInternalPlayerData().lastClaim.get();
+            if (lastClaim != null && lastClaim != fromClaim) {
+                if (GDPermissionManager.getInstance().getFinalPermission(event, toLocation, toClaim, GDPermissions.ENTER_CLAIM, targetEntity, targetEntity, player, TrustTypes.ACCESSOR, false) == Tristate.FALSE) {
+                    Location<World> claimCorner = new Location<>(toClaim.getWorld(), toClaim.lesserBoundaryCorner.getX(), player.getLocation().getY(), toClaim.greaterBoundaryCorner.getZ());
+                    Location<World> safeLocation = Sponge.getGame().getTeleportHelper().getSafeLocation(claimCorner, 9, 9).orElse(player.getWorld().getSpawnLocation());
+                    if (event != null) {
+                        event.setToTransform(player.getTransform().setLocation(safeLocation));
+                    }
+                    return false;
+                }
+            }
+        }
+        if (fromClaim == toClaim) {
+            GDTimings.ENTITY_MOVE_EVENT.stopTimingIfSync();
+            return true;
+        }
+        // MCClans tag support
+        Component enterClanTag = null;
+        Component exitClanTag = null;
+        MCClansProvider clanApiProvider = GriefDefenderPlugin.getInstance().clanApiProvider;
+        if (clanApiProvider != null) {
+            if ((fromClaim.isBasicClaim() || (fromClaim.isSubdivision() && !fromClaim.isAdminClaim()))) {
+                ClanPlayer clanPlayer = clanApiProvider.getClanService().getClanPlayer(fromClaim.getOwnerUniqueId());
+                if (clanPlayer != null && clanPlayer.getClan() != null) {
+                    exitClanTag = SpongeUtil.fromSpongeText(Text.of(clanPlayer.getClan().getTagColored(), " "));
+                }
+            }
+            if ((toClaim.isBasicClaim() || (toClaim.isSubdivision() && !toClaim.isAdminClaim()))) {
+                ClanPlayer clanPlayer = clanApiProvider.getClanService().getClanPlayer(toClaim.getOwnerUniqueId());
+                if (clanPlayer != null && clanPlayer.getClan() != null) {
+                    enterClanTag = SpongeUtil.fromSpongeText(Text.of(clanPlayer.getClan().getTagColored(), " "));
+                }
+            }
+        }
+
+        GDBorderClaimEvent gpEvent = new GDBorderClaimEvent(targetEntity, fromClaim, toClaim);
+        if (user != null && toClaim.isUserTrusted(user, TrustTypes.ACCESSOR)) {
+            GriefDefender.getEventManager().post(gpEvent);
+            if (gpEvent.cancelled()) {
+                event.setCancelled(true);
+                if (!(targetEntity instanceof Player) && EntityUtils.getOwnerUniqueId(targetEntity) == null) {
+                    targetEntity.remove();
+                }
+                final Component cancelMessage = gpEvent.getMessage().orElse(null);
+                if (player != null && cancelMessage != null) {
+                    TextAdapter.sendComponent(player, cancelMessage);
+                }
+                return false;
+            } else {
+                    final boolean showGpPrefix = GriefDefenderPlugin.getGlobalConfig().getConfig().message.enterExitShowGdPrefix;
+                    user.getInternalPlayerData().lastClaim = new WeakReference<>(toClaim);
+                    TextComponent welcomeMessage = (TextComponent) gpEvent.getEnterMessage().orElse(null);
+                    if (welcomeMessage != null && !welcomeMessage.equals(TextComponent.empty()) && !welcomeMessage.content().equals("")) {
+                        ChatType chatType = gpEvent.getEnterMessageChatType();
+                        if (showGpPrefix) {
+                            TextAdapter.sendComponent(player, TextComponent.builder("")
+                                    .append(enterClanTag != null ? enterClanTag : GriefDefenderPlugin.GD_TEXT)
+                                    .append(welcomeMessage).build(), SpongeUtil.getSpongeChatType(chatType));
+                        } else {
+                            TextAdapter.sendComponent(player, enterClanTag != null ? enterClanTag : welcomeMessage, SpongeUtil.getSpongeChatType(chatType));
+                        }
+                    }
+
+                    Component farewellMessage = gpEvent.getExitMessage().orElse(null);
+                    if (farewellMessage != null && !farewellMessage.equals(Text.of())) {
+                        ChatType chatType = gpEvent.getExitMessageChatType();
+                        if (showGpPrefix) {
+                            TextAdapter.sendComponent(player, TextComponent.builder("")
+                                    .append(exitClanTag != null ? exitClanTag : GriefDefenderPlugin.GD_TEXT)
+                                    .append(farewellMessage)
+                                    .build(), SpongeUtil.getSpongeChatType(chatType));
+                        } else {
+                            TextAdapter.sendComponent(player, exitClanTag != null ? exitClanTag : farewellMessage, SpongeUtil.getSpongeChatType(chatType));
+                        }
+                    }
+
+                    if (toClaim.isInTown()) {
+                        user.getInternalPlayerData().inTown = true;
+                    } else {
+                        user.getInternalPlayerData().inTown = false;
+                    }
+            }
+
+            GDTimings.ENTITY_MOVE_EVENT.stopTimingIfSync();
+            return true;
+        }
+
+        if (fromClaim != toClaim) {
+            boolean enterCancelled = false;
+            boolean exitCancelled = false;
+            // enter
+            if (GDFlags.ENTER_CLAIM && !enterBlacklisted && GDPermissionManager.getInstance().getFinalPermission(event, toLocation, toClaim, GDPermissions.ENTER_CLAIM, targetEntity, targetEntity, user) == Tristate.FALSE) {
+                enterCancelled = true;
+                gpEvent.cancelled(true);
+            }
+
+            // exit
+            if (GDFlags.EXIT_CLAIM && !exitBlacklisted && GDPermissionManager.getInstance().getFinalPermission(event, fromLocation, fromClaim, GDPermissions.EXIT_CLAIM, targetEntity, targetEntity, user) == Tristate.FALSE) {
+                exitCancelled = true;
+                gpEvent.cancelled(true);
+            }
+
+            GriefDefender.getEventManager().post(gpEvent);
+            if (gpEvent.cancelled()) {
+                final Component cancelMessage = gpEvent.getMessage().orElse(null);
+                if (exitCancelled) {
+                    if (cancelMessage != null && player != null) {
+                        GriefDefenderPlugin.sendClaimDenyMessage(fromClaim, player, MessageCache.getInstance().PERMISSION_CLAIM_EXIT);
+                    }
+                } else if (enterCancelled) {
+                    if (cancelMessage != null && player != null) {
+                        GriefDefenderPlugin.sendClaimDenyMessage(toClaim, player, MessageCache.getInstance().PERMISSION_CLAIM_ENTER);
+                    }
+                }
+
+                if (cancelMessage != null && player != null) {
+                    TextAdapter.sendComponent(player, cancelMessage);
+                }
+
+                event.setCancelled(true);
+                if (!(targetEntity instanceof Player) && EntityUtils.getOwnerUniqueId(targetEntity) == null) {
+                    targetEntity.remove();
+                }
+                GDTimings.ENTITY_MOVE_EVENT.stopTimingIfSync();
+                return false;
+            }
+
+            if (user != null) {
+                final boolean showGpPrefix = GriefDefenderPlugin.getGlobalConfig().getConfig().message.enterExitShowGdPrefix;
+                user.getInternalPlayerData().lastClaim = new WeakReference<>(toClaim);
+                Component welcomeMessage = gpEvent.getEnterMessage().orElse(null);
+                if (welcomeMessage != null && !welcomeMessage.equals(TextComponent.empty())) {
+                    ChatType chatType = gpEvent.getEnterMessageChatType();
+                    if (showGpPrefix) {
+                        TextAdapter.sendComponent(player, TextComponent.builder("")
+                                .append(enterClanTag != null ? enterClanTag : GriefDefenderPlugin.GD_TEXT)
+                                .append(welcomeMessage)
+                                .build(), SpongeUtil.getSpongeChatType(chatType));
+                    } else {
+                        TextAdapter.sendComponent(player, enterClanTag != null ? enterClanTag : welcomeMessage, SpongeUtil.getSpongeChatType(chatType));
+                    }
+                }
+
+                Component farewellMessage = gpEvent.getExitMessage().orElse(null);
+                if (farewellMessage != null && !farewellMessage.equals(Text.of())) {
+                    ChatType chatType = gpEvent.getExitMessageChatType();
+                    if (showGpPrefix) {
+                        TextAdapter.sendComponent(player, TextComponent.builder("")
+                                .append(exitClanTag != null ? exitClanTag : GriefDefenderPlugin.GD_TEXT)
+                                .append(farewellMessage)
+                                .build(), SpongeUtil.getSpongeChatType(chatType));
+                    } else {
+                        TextAdapter.sendComponent(player, exitClanTag != null ? exitClanTag : farewellMessage, SpongeUtil.getSpongeChatType(chatType));
+                    }
+                }
+
+                if (toClaim.isInTown()) {
+                    user.getInternalPlayerData().inTown = true;
+                } else {
+                    user.getInternalPlayerData().inTown = false;
+                }
+
+                checkPlayerFlight(player, user.getInternalPlayerData(), fromClaim, toClaim);
+            }
+        }
+
+        GDTimings.ENTITY_MOVE_EVENT.stopTimingIfSync();
+        return true;
+    }
+
+    private void checkPlayerFlight(Player player, GDPlayerData playerData, GDClaim fromClaim, GDClaim toClaim) {
+        final GameMode gameMode = player.get(Keys.GAME_MODE).orElse(null);
+        if (gameMode == null || gameMode == GameModes.CREATIVE || gameMode == GameModes.SPECTATOR) {
+            return;
+        }
+
+        if (fromClaim == toClaim || !player.get(Keys.IS_FLYING).get()) {
+            // only handle player-fly in enter/exit
+            return;
+        }
+
+        final Boolean noFly = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Boolean.class), playerData.getSubject(), Options.PLAYER_DENY_FLIGHT, toClaim);
+        final boolean adminFly = player.hasPermission(GDPermissions.BYPASS_OPTION);
+        final boolean ownerFly = toClaim.isBasicClaim() ? player.hasPermission(GDPermissions.USER_OPTION_PERK_OWNER_FLY_BASIC) : toClaim.isTown() ? player.hasPermission(GDPermissions.USER_OPTION_PERK_OWNER_FLY_TOWN) : false;
+        if (player.getUniqueId().equals(toClaim.getOwnerUniqueId()) && ownerFly) {
+            return;
+        }
+        if (!adminFly && noFly) {
+            player.offer(Keys.CAN_FLY, false);
+            player.offer(Keys.IS_FLYING, false);
+            playerData.ignoreFallDamage = true;
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().OPTION_PLAYER_DENY_FLIGHT);
+        }
+    }
+
+    public void sendInteractEntityDenyMessage(ItemStack playerItem, Entity entity, GDClaim claim, Player player) {
+        if (entity instanceof Player || (claim.getData() != null && !claim.getData().allowDenyMessages())) {
+            return;
+        }
+
+        final String entityId = entity.getType().getId().toLowerCase();
+        if (playerItem == null || playerItem.isEmpty()) {
+            final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.PERMISSION_INTERACT_ENTITY, ImmutableMap.of(
+                    "player", claim.getOwnerName(),
+                    "entity", entityId));
+            GriefDefenderPlugin.sendClaimDenyMessage(claim, player, message);
+        } else {
+            final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.PERMISSION_INTERACT_ITEM_ENTITY, ImmutableMap.of(
+                    "item", playerItem.getType().getId().toLowerCase(),
+                    "entity", entityId));
+            GriefDefenderPlugin.sendClaimDenyMessage(claim, player, message);
+        }
+    }
+}
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/listener/EntityEventHandler.java b/sponge/src/main/java/com/griefdefender/listener/EntityEventHandler.java
new file mode 100644
index 0000000..0a62ab1
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/listener/EntityEventHandler.java
@@ -0,0 +1,899 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.listener;
+
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GDTimings;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.ChatType;
+import com.griefdefender.api.GriefDefender;
+import com.griefdefender.api.Tristate;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.TrustType;
+import com.griefdefender.api.claim.TrustTypes;
+import com.griefdefender.api.permission.flag.Flags;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.claim.GDClaimManager;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.event.GDBorderClaimEvent;
+import com.griefdefender.internal.util.NMSUtil;
+import com.griefdefender.internal.util.VecHelper;
+import com.griefdefender.permission.GDPermissionManager;
+import com.griefdefender.permission.GDPermissionUser;
+import com.griefdefender.permission.GDPermissions;
+import com.griefdefender.permission.flag.GDFlags;
+import com.griefdefender.provider.MCClansProvider;
+import com.griefdefender.storage.BaseStorage;
+import com.griefdefender.util.CauseContextHelper;
+import com.griefdefender.util.EntityUtils;
+import com.griefdefender.util.PlayerUtil;
+import com.griefdefender.util.SpongeUtil;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import nl.riebie.mcclans.api.ClanPlayer;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.block.BlockSnapshot;
+import org.spongepowered.api.block.tileentity.TileEntity;
+import org.spongepowered.api.command.source.ConsoleSource;
+import org.spongepowered.api.entity.Entity;
+import org.spongepowered.api.entity.EntityTypes;
+import org.spongepowered.api.entity.ExperienceOrb;
+import org.spongepowered.api.entity.Item;
+import org.spongepowered.api.entity.hanging.ItemFrame;
+import org.spongepowered.api.entity.living.Living;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.entity.living.player.User;
+import org.spongepowered.api.entity.projectile.Projectile;
+import org.spongepowered.api.event.Event;
+import org.spongepowered.api.event.Listener;
+import org.spongepowered.api.event.Order;
+import org.spongepowered.api.event.cause.Cause;
+import org.spongepowered.api.event.cause.EventContext;
+import org.spongepowered.api.event.cause.EventContextKeys;
+import org.spongepowered.api.event.cause.entity.damage.source.DamageSource;
+import org.spongepowered.api.event.cause.entity.damage.source.EntityDamageSource;
+import org.spongepowered.api.event.cause.entity.damage.source.IndirectEntityDamageSource;
+import org.spongepowered.api.event.cause.entity.teleport.TeleportType;
+import org.spongepowered.api.event.cause.entity.teleport.TeleportTypes;
+import org.spongepowered.api.event.entity.AttackEntityEvent;
+import org.spongepowered.api.event.entity.CollideEntityEvent;
+import org.spongepowered.api.event.entity.ConstructEntityEvent;
+import org.spongepowered.api.event.entity.DamageEntityEvent;
+import org.spongepowered.api.event.entity.DestructEntityEvent;
+import org.spongepowered.api.event.entity.MoveEntityEvent;
+import org.spongepowered.api.event.entity.RideEntityEvent;
+import org.spongepowered.api.event.entity.SpawnEntityEvent;
+import org.spongepowered.api.event.filter.cause.First;
+import org.spongepowered.api.event.filter.cause.Root;
+import org.spongepowered.api.event.item.inventory.DropItemEvent;
+import org.spongepowered.api.event.world.ExplosionEvent;
+import org.spongepowered.api.item.ItemTypes;
+import org.spongepowered.api.item.inventory.ItemStack;
+import org.spongepowered.api.service.user.UserStorageService;
+import org.spongepowered.api.text.Text;
+import org.spongepowered.api.world.Location;
+import org.spongepowered.api.world.World;
+import org.spongepowered.api.world.explosion.Explosion;
+import org.spongepowered.api.world.storage.WorldProperties;
+
+import java.lang.ref.WeakReference;
+import java.time.Instant;
+import java.util.Iterator;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.function.Predicate;
+
+//handles events related to entities
+public class EntityEventHandler {
+
+    private int lastConstructEntityTick = -1;
+    private boolean lastConstructEntityCancelled = false;
+
+    // convenience reference for the singleton datastore
+    private final BaseStorage dataStore;
+
+    public EntityEventHandler(BaseStorage dataStore) {
+        this.dataStore = dataStore;
+    }
+
+    @Listener(order = Order.FIRST, beforeModifications = true)
+    public void onEntityExplosionPre(ExplosionEvent.Pre event) {
+        if (!GDFlags.EXPLOSION_ENTITY || !GriefDefenderPlugin.getInstance().claimsEnabledForWorld(event.getTargetWorld().getUniqueId())) {
+            return;
+        }
+        if (GriefDefenderPlugin.isSourceIdBlacklisted(Flags.EXPLOSION_ENTITY.getName(), event.getSource(), event.getTargetWorld().getProperties())) {
+            return;
+        }
+
+        GDTimings.ENTITY_EXPLOSION_PRE_EVENT.startTimingIfSync();
+        Location<World> location = event.getExplosion().getLocation();
+        GDClaim claim =  GriefDefenderPlugin.getInstance().dataStore.getClaimAt(location);
+        User user = CauseContextHelper.getEventUser(event);
+        Object source = event.getSource();
+        if (source instanceof Explosion) {
+            final Explosion explosion = (Explosion) source;
+            if (explosion.getSourceExplosive().isPresent()) {
+                source = explosion.getSourceExplosive().get();
+            } else {
+                Entity exploder = event.getCause().first(Entity.class).orElse(null);
+                if (exploder != null) {
+                    source = exploder;
+                }
+            }
+        }
+
+        /*if (location.getPosition().getY() > ((net.minecraft.world.World) location.getExtent()).getSeaLevel() && !GriefDefenderPlugin.getActiveConfig(location.getExtent().getUniqueId()).getConfig().claim.explosionSurface) {
+            event.setCancelled(true);
+            return;
+        }*/
+
+        Tristate result = GDPermissionManager.getInstance().getFinalPermission(event, location, claim, GDPermissions.EXPLOSION_ENTITY, source, location.getBlock(), user, true);
+
+        if(result == Tristate.FALSE) {
+            event.setCancelled(true);
+            GDTimings.ENTITY_EXPLOSION_PRE_EVENT.stopTimingIfSync();
+            return;
+        }
+
+        GDTimings.ENTITY_EXPLOSION_PRE_EVENT.stopTimingIfSync();
+    }
+
+    @Listener(order = Order.FIRST, beforeModifications = true)
+    public void onEntityExplosionDetonate(ExplosionEvent.Detonate event) {
+        if (!GDFlags.EXPLOSION_ENTITY || !GriefDefenderPlugin.getInstance().claimsEnabledForWorld(event.getTargetWorld().getUniqueId())) {
+            return;
+        }
+        if (GriefDefenderPlugin.isSourceIdBlacklisted(Flags.EXPLOSION_ENTITY.getName(), event.getSource(), event.getTargetWorld().getProperties())) {
+            return;
+        }
+
+        GDTimings.ENTITY_EXPLOSION_DETONATE_EVENT.startTimingIfSync();
+        final User user = CauseContextHelper.getEventUser(event);
+        Iterator<Entity> iterator = event.getEntities().iterator();
+        GDClaim targetClaim = null;
+        Object source = event.getSource();
+        if (source instanceof Explosion) {
+            final Explosion explosion = (Explosion) source;
+            if (explosion.getSourceExplosive().isPresent()) {
+                source = explosion.getSourceExplosive().get();
+            } else {
+                Entity exploder = event.getCause().first(Entity.class).orElse(null);
+                if (exploder != null) {
+                    source = exploder;
+                }
+            }
+        }
+
+        while (iterator.hasNext()) {
+            Entity entity = iterator.next();
+            targetClaim =  GriefDefenderPlugin.getInstance().dataStore.getClaimAt(entity.getLocation(), targetClaim);
+            if (GDPermissionManager.getInstance().getFinalPermission(event, entity.getLocation(), targetClaim, GDPermissions.ENTITY_DAMAGE, source, entity, user) == Tristate.FALSE) {
+                iterator.remove();
+            }
+        }
+        GDTimings.ENTITY_EXPLOSION_DETONATE_EVENT.stopTimingIfSync();
+    }
+
+    @Listener(order = Order.FIRST, beforeModifications = true)
+    public void onEntityConstruct(ConstructEntityEvent.Pre event, @Root Object source) {
+        lastConstructEntityTick = Sponge.getServer().getRunningTimeTicks();
+        if (true || source instanceof ConsoleSource || !GDFlags.ENTITY_SPAWN) {
+            return;
+        }
+
+        final World world = event.getTransform().getExtent();
+        final String entityTypeId = event.getTargetType().getId();
+        if (entityTypeId.equals(EntityTypes.EXPERIENCE_ORB.getId())) {
+            return;
+        }
+
+        final Location<World> location = event.getTransform().getLocation();
+        if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(world.getUniqueId())) {
+            return;
+        }
+        if (GriefDefenderPlugin.isSourceIdBlacklisted(Flags.ENTITY_SPAWN.getName(), source, world.getProperties())) {
+            return;
+        }
+        if (GriefDefenderPlugin.isSourceIdBlacklisted(Flags.ENTITY_CHUNK_SPAWN.getName(), source, world.getProperties())) {
+            return;
+        }
+
+        if (GriefDefenderPlugin.isTargetIdBlacklisted(Flags.ENTITY_SPAWN.getName(), entityTypeId, world.getProperties())) {
+            return;
+        }
+
+        GDTimings.ENTITY_SPAWN_PRE_EVENT.startTimingIfSync();
+        final User user = CauseContextHelper.getEventUser(event);
+        final GDClaim targetClaim = GriefDefenderPlugin.getInstance().dataStore.getClaimAt(location);
+        if (targetClaim.isUserTrusted(user, TrustTypes.BUILDER)) {
+            GDTimings.ENTITY_SPAWN_PRE_EVENT.stopTimingIfSync();
+            return;
+        }
+
+        String permission = GDPermissions.ENTITY_SPAWN;
+        if (event.getTargetType() == EntityTypes.ITEM) {
+            if (user == null) {
+                GDTimings.ENTITY_SPAWN_PRE_EVENT.stopTimingIfSync();
+                return;
+            }
+            if (!GDFlags.ITEM_SPAWN) {
+                GDTimings.ENTITY_SPAWN_PRE_EVENT.stopTimingIfSync();
+                return;
+            }
+            if (GriefDefenderPlugin.isTargetIdBlacklisted(Flags.ITEM_SPAWN.getName(), entityTypeId, world.getProperties())) {
+                GDTimings.ENTITY_SPAWN_PRE_EVENT.stopTimingIfSync();
+                return;
+            }
+
+            permission = GDPermissions.ITEM_SPAWN;
+            if (source instanceof BlockSnapshot) {
+                final BlockSnapshot block = (BlockSnapshot) source;
+                final Location<World> blockLocation = block.getLocation().orElse(null);
+                if (blockLocation != null) {
+                    if (GriefDefenderPlugin.isTargetIdBlacklisted(Flags.BLOCK_BREAK.getName(), block, world.getProperties())) {
+                        GDTimings.ENTITY_SPAWN_PRE_EVENT.stopTimingIfSync();
+                        return;
+                    }
+                    final Tristate result = GDPermissionManager.getInstance().getFinalPermission(event, location, targetClaim, GDPermissions.BLOCK_BREAK, source, block, user, true);
+                    if (result != Tristate.UNDEFINED) {
+                        if (result == Tristate.TRUE) {
+                            // Check if item drop is allowed
+                            if (GDPermissionManager.getInstance().getFinalPermission(event, location, targetClaim, permission, source, entityTypeId, user, true) == Tristate.FALSE) {
+                                event.setCancelled(true);
+                            }
+                            GDTimings.ENTITY_SPAWN_PRE_EVENT.stopTimingIfSync();
+                            return;
+                        }
+                        event.setCancelled(true);
+                        GDTimings.ENTITY_SPAWN_PRE_EVENT.stopTimingIfSync();
+                        return;
+                    }
+                }
+            }
+        }
+        if (GDPermissionManager.getInstance().getFinalPermission(event, location, targetClaim, permission, source, entityTypeId, user, true) == Tristate.FALSE) {
+            event.setCancelled(true);
+        }
+        GDTimings.ENTITY_SPAWN_PRE_EVENT.stopTimingIfSync();
+    }
+
+    @Listener(order = Order.FIRST, beforeModifications = true)
+    public void onEntitySpawn(SpawnEntityEvent event) {
+        Object source = event.getSource();
+        if (source instanceof ConsoleSource || !GDFlags.ENTITY_SPAWN || event.getEntities().isEmpty()) {
+            return;
+        }
+
+        // If root cause is damage source, look for target as that should be passed instead
+        // Ex. Entity dies and drops an item would be after EntityDamageSource
+        if (source instanceof DamageSource) {
+            final Object target = event.getCause().after(DamageSource.class).orElse(null);
+            if (target != null) {
+                source = target;
+            }
+        }
+
+        final boolean isChunkSpawn = event instanceof SpawnEntityEvent.ChunkLoad;
+        if (isChunkSpawn && !GDFlags.ENTITY_CHUNK_SPAWN) {
+            return;
+        }
+        if (event instanceof DropItemEvent) {
+            if (!GDFlags.ITEM_DROP) {
+                return;
+            }
+            // only handle item spawns from non-living
+            if (source instanceof Living || NMSUtil.getInstance().containsContainerPlayer(event.getCause())) {
+                return;
+            }
+        }
+
+        final World world = event.getEntities().get(0).getWorld();
+        if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(world.getUniqueId())) {
+            return;
+        }
+        if (GriefDefenderPlugin.isSourceIdBlacklisted(Flags.ENTITY_SPAWN.getName(), source, world.getProperties())) {
+            return;
+        }
+        if (isChunkSpawn && GriefDefenderPlugin.isSourceIdBlacklisted(Flags.ENTITY_CHUNK_SPAWN.getName(), source, world.getProperties())) {
+            return;
+        }
+
+        GDTimings.ENTITY_SPAWN_EVENT.startTimingIfSync();
+        final User user = CauseContextHelper.getEventUser(event);
+        final Object actualSource = source;
+        event.filterEntities(new Predicate<Entity>() {
+            GDClaim targetClaim = null;
+
+            @Override
+            public boolean test(Entity entity) {
+                if (entity instanceof ExperienceOrb) {
+                    return true;
+                }
+
+                if (GriefDefenderPlugin.isTargetIdBlacklisted(Flags.ENTITY_SPAWN.getName(), entity, world.getProperties())) {
+                    return true;
+                }
+
+                targetClaim = GriefDefenderPlugin.getInstance().dataStore.getClaimAt(entity.getLocation(), targetClaim);
+                if (targetClaim == null) {
+                    return true;
+                }
+
+                String permission = GDPermissions.ENTITY_SPAWN;
+                if (isChunkSpawn) {
+                    if (GriefDefenderPlugin.isTargetIdBlacklisted(Flags.ENTITY_CHUNK_SPAWN.getName(), entity, world.getProperties())) {
+                        return true;
+                    }
+                    permission = GDPermissions.ENTITY_CHUNK_SPAWN;
+                }
+
+                if (!isChunkSpawn && entity instanceof Item) {
+                    if (user == null) {
+                        return true;
+                    }
+                    if (!GDFlags.ITEM_SPAWN) {
+                        return true;
+                    }
+                    if (GriefDefenderPlugin.isTargetIdBlacklisted(Flags.ITEM_SPAWN.getName(), entity, world.getProperties())) {
+                        return true;
+                    }
+                    permission = GDPermissions.ITEM_SPAWN;
+                    if (actualSource instanceof BlockSnapshot) {
+                        final BlockSnapshot block = (BlockSnapshot) actualSource;
+                        final Location<World> location = block.getLocation().orElse(null);
+                        if (location != null) {
+                            if (GriefDefenderPlugin.isTargetIdBlacklisted(Flags.BLOCK_BREAK.getName(), block, world.getProperties())) {
+                                return true;
+                            }
+                            final Tristate result = GDPermissionManager.getInstance().getFinalPermission(event, location, targetClaim, GDPermissions.BLOCK_BREAK, actualSource, block, user, TrustTypes.ACCESSOR, true);
+                            if (result != Tristate.UNDEFINED) {
+                                if (result == Tristate.TRUE) {
+                                    // Check if item drop is allowed
+                                    if (GDPermissionManager.getInstance().getFinalPermission(event, location, targetClaim, permission, actualSource, entity, user, TrustTypes.ACCESSOR, true) == Tristate.FALSE) {
+                                        return false;
+                                    }
+                                    return true;
+                                }
+                                return false;
+                            }
+                        }
+                    }
+                }
+
+                if (GDPermissionManager.getInstance().getFinalPermission(event, entity.getLocation(), targetClaim, permission, actualSource, entity, user, TrustTypes.ACCESSOR, true) == Tristate.FALSE) {
+                    return false;
+                }
+                
+                return true;
+            }
+        });
+
+        GDTimings.ENTITY_SPAWN_EVENT.stopTimingIfSync();
+    }
+
+    @Listener(order = Order.FIRST, beforeModifications = true)
+    public void onEntityAttack(AttackEntityEvent event, @First DamageSource damageSource) {
+        GDTimings.ENTITY_ATTACK_EVENT.startTimingIfSync();
+        if (protectEntity(event, event.getTargetEntity(), event.getCause(), damageSource)) {
+            event.setCancelled(true);
+        }
+        GDTimings.ENTITY_ATTACK_EVENT.stopTimingIfSync();
+    }
+
+    @Listener(order = Order.FIRST, beforeModifications = true)
+    public void onEntityDamage(DamageEntityEvent event, @First DamageSource damageSource) {
+        GDTimings.ENTITY_DAMAGE_EVENT.startTimingIfSync();
+        if (protectEntity(event, event.getTargetEntity(), event.getCause(), damageSource)) {
+            event.setCancelled(true);
+        }
+        event.getTargetEntity().setCreator(null);
+        GDTimings.ENTITY_DAMAGE_EVENT.stopTimingIfSync();
+    }
+
+    public boolean protectEntity(Event event, Entity targetEntity, Cause cause, DamageSource damageSource) {
+        if (!GDFlags.ENTITY_DAMAGE || !GriefDefenderPlugin.getInstance().claimsEnabledForWorld(targetEntity.getWorld().getUniqueId())) {
+            return false;
+        }
+        if (GriefDefenderPlugin.isTargetIdBlacklisted(Flags.ENTITY_DAMAGE.getName(), targetEntity, targetEntity.getWorld().getProperties())) {
+            return false;
+        }
+
+        User user = CauseContextHelper.getEventUser(event);
+        Player player = cause.first(Player.class).orElse(null);
+        Object source = damageSource;
+        EntityDamageSource entityDamageSource = null;
+        final TileEntity tileEntity = cause.first(TileEntity.class).orElse(null);
+        // TE takes priority over entity damage sources
+        if (tileEntity != null) {
+            source = tileEntity;
+        } else if (damageSource instanceof EntityDamageSource) {
+            entityDamageSource = (EntityDamageSource) damageSource;
+            source = entityDamageSource.getSource();
+            if (entityDamageSource instanceof IndirectEntityDamageSource) {
+                final Entity indirectSource = ((IndirectEntityDamageSource) entityDamageSource).getIndirectSource();
+                if (indirectSource != null) {
+                    source = indirectSource;
+                }
+            }
+            if (source instanceof Player) {
+                if (user == null) {
+                    user = (User) source;
+                }
+                if (player == null) {
+                    player = (Player) source;
+                }
+            }
+        }
+
+        if (GriefDefenderPlugin.isSourceIdBlacklisted(Flags.ENTITY_DAMAGE.getName(), source, targetEntity.getWorld().getProperties())) {
+            return false;
+        }
+
+        GDPlayerData playerData = null;
+        if (player != null) {
+            playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(targetEntity.getWorld(), player.getUniqueId());
+        }
+
+        if (NMSUtil.getInstance().isEntityMonster(targetEntity)) {
+            return false;
+        }
+
+        final GDClaim claim = this.dataStore.getClaimAt(targetEntity.getLocation(), playerData != null ? playerData.lastClaim.get() : null);
+        final TrustType trustType = TrustTypes.BUILDER;
+        if (GDPermissionManager.getInstance().getFinalPermission(event, targetEntity.getLocation(), claim, GDPermissions.ENTITY_DAMAGE, source, targetEntity, user, trustType, true) == Tristate.FALSE) {
+            return true;
+        }
+
+        // allow trusted users to attack entities within claim
+        if (!(targetEntity instanceof Player) && claim.isUserTrusted(user, TrustTypes.ACCESSOR)) {
+            return false;
+        }
+
+        // Protect owned entities anywhere in world
+        if (entityDamageSource != null && !NMSUtil.getInstance().isEntityMonster(targetEntity)) {
+            Tristate perm = Tristate.UNDEFINED;
+            // Ignore PvP checks for owned entities
+            if (!(source instanceof Player) && !(targetEntity instanceof Player)) {
+                if (source instanceof User) {
+                    User sourceUser = (User) source;
+                    perm = GDPermissionManager.getInstance().getFinalPermission(event, targetEntity.getLocation(), claim, GDPermissions.ENTITY_DAMAGE, source, targetEntity, sourceUser, trustType, true);
+                    if (targetEntity instanceof Living && perm == Tristate.TRUE) {
+                        return false;
+                    }
+                    Optional<UUID> creatorUuid = targetEntity.getCreator();
+                    if (creatorUuid.isPresent()) {
+                        Optional<User> creator = Sponge.getGame().getServiceManager().provide(UserStorageService.class).get().get(creatorUuid.get());
+                        if (creator.isPresent() && !creator.get().getUniqueId().equals(sourceUser.getUniqueId())) {
+                            return true;
+                        }
+                    } else if (sourceUser.getUniqueId().equals(claim.getOwnerUniqueId())) {
+                        return true;
+                    }
+    
+                    return false;
+                } else {
+                    if (targetEntity instanceof Player) {
+                        if (NMSUtil.getInstance().isEntityMonster((Entity) source)) {
+                            if (GDPermissionManager.getInstance().getFinalPermission(event, targetEntity.getLocation(), claim, GDPermissions.ENTITY_DAMAGE, source, targetEntity, user, trustType, true) != Tristate.TRUE) {
+                                return true;
+                            }
+                        }
+                    } else if (targetEntity instanceof Living && !NMSUtil.getInstance().isEntityMonster(targetEntity)) {
+                        if (user != null && !user.getUniqueId().equals(claim.getOwnerUniqueId()) && perm != Tristate.TRUE) {
+                            return true;
+                        }
+                    }
+                }
+            }
+        }
+
+        if (entityDamageSource == null || tileEntity != null) {
+            return false;
+        }
+
+        Player attacker = null;
+        Projectile projectile = null;
+
+        if (source != null) {
+            if (source instanceof Player) {
+                attacker = (Player) source;
+            } else if (source instanceof Projectile) {
+                projectile = (Projectile) source;
+                if (projectile.getShooter() instanceof Player) {
+                    attacker = (Player) projectile.getShooter();
+                }
+            }
+        }
+
+        if (GDPermissionManager.getInstance().getFinalPermission(event, targetEntity.getLocation(), claim, GDPermissions.ENTITY_DAMAGE, attacker, targetEntity, user, trustType, true) == Tristate.FALSE) {
+            return true;
+        }
+
+        return false;
+    }
+
+    @Listener(order = Order.POST)
+    public void onEntityDamageMonitor(DamageEntityEvent event) {
+        if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(event.getTargetEntity().getWorld().getUniqueId())) {
+            return;
+        }
+
+        GDTimings.ENTITY_DAMAGE_MONITOR_EVENT.startTimingIfSync();
+        //FEATURE: prevent players who very recently participated in pvp combat from hiding inventory to protect it from looting
+        //FEATURE: prevent players who are in pvp combat from logging out to avoid being defeated
+
+        if (event.getTargetEntity().getType() != EntityTypes.PLAYER || NMSUtil.getInstance().isEntityMonster(event.getTargetEntity())) {
+            GDTimings.ENTITY_DAMAGE_MONITOR_EVENT.stopTimingIfSync();
+            return;
+        }
+
+        Player defender = (Player) event.getTargetEntity();
+
+        //only interested in entities damaging entities (ignoring environmental damage)
+        // the rest is only interested in entities damaging entities (ignoring environmental damage)
+        if (!(event.getCause().root() instanceof EntityDamageSource)) {
+            GDTimings.ENTITY_DAMAGE_MONITOR_EVENT.stopTimingIfSync();
+            return;
+        }
+
+        GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(defender.getWorld(), defender.getUniqueId());
+        GDClaim claim = this.dataStore.getClaimAtPlayer(playerData, defender.getLocation());
+        EntityDamageSource entityDamageSource = (EntityDamageSource) event.getCause().root();
+
+        //if not in a pvp rules world, do nothing
+        if (!claim.isPvpEnabled()) {
+            GDTimings.ENTITY_DAMAGE_MONITOR_EVENT.stopTimingIfSync();
+            return;
+        }
+
+        GDTimings.ENTITY_DAMAGE_MONITOR_EVENT.stopTimingIfSync();
+    }
+
+    @Listener(order = Order.FIRST, beforeModifications = true)
+    public void onDestructEntity(DestructEntityEvent event) {
+      //  Thread.dumpStack();
+    }
+
+    // when an entity drops items on death
+    @Listener(order = Order.FIRST, beforeModifications = true)
+    public void onEntityDropItemDeath(DropItemEvent.Destruct event) {
+        if (!GDFlags.ITEM_DROP || event.getEntities().isEmpty()) {
+            return;
+        }
+
+        final World world = event.getEntities().get(0).getWorld();
+        if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(world.getUniqueId())) {
+            return;
+        }
+
+        Object source = event.getSource();
+        // If root cause is damage source, look for target as that should be passed instead
+        // Ex. Entity dies and drops an item would be after EntityDamageSource
+        if (source instanceof DamageSource) {
+            final Object target = event.getCause().after(DamageSource.class).orElse(null);
+            if (target != null) {
+                source = target;
+            }
+        }
+        if (!(source instanceof Entity)) {
+            return;
+        }
+
+        final Entity entity = (Entity) source;
+        if (GriefDefenderPlugin.isSourceIdBlacklisted(Flags.ITEM_DROP.getName(), entity, world.getProperties())) {
+            return;
+        }
+
+        GDTimings.ENTITY_DROP_ITEM_DEATH_EVENT.startTimingIfSync();
+
+        final User user = CauseContextHelper.getEventUser(event);
+        event.filterEntities(new Predicate<Entity>() {
+            GDClaim targetClaim = null;
+
+            @Override
+            public boolean test(Entity item) {
+                if (GriefDefenderPlugin.isTargetIdBlacklisted(Flags.ITEM_DROP.getName(), item, world.getProperties())) {
+                    return true;
+                }
+
+                targetClaim = GriefDefenderPlugin.getInstance().dataStore.getClaimAt(item.getLocation(), targetClaim);
+                if (targetClaim == null) {
+                    return true;
+                }
+
+                if (user == null) {
+                    return true;
+                }
+                if (GriefDefenderPlugin.isTargetIdBlacklisted(Flags.ITEM_DROP.getName(), item, world.getProperties())) {
+                    return true;
+                }
+
+                if (GDPermissionManager.getInstance().getFinalPermission(event, item.getLocation(), targetClaim, GDPermissions.ITEM_DROP, entity, item, user, TrustTypes.ACCESSOR, true) == Tristate.FALSE) {
+                    return false;
+                }
+                return true;
+            }
+        });
+
+        GDTimings.ENTITY_DROP_ITEM_DEATH_EVENT.stopTimingIfSync();
+    }
+
+    @Listener(order = Order.FIRST, beforeModifications = true)
+    public void onEntityMove(MoveEntityEvent event){
+        CommonEntityEventHandler.getInstance().onEntityMove(event, event.getFromTransform().getLocation(), event.getToTransform().getLocation(), event.getTargetEntity());
+    }
+
+    // when a player teleports
+    @Listener(order = Order.FIRST, beforeModifications = true)
+    public void onEntityTeleport(MoveEntityEvent.Teleport event) {
+        if (!GDFlags.ENTITY_TELEPORT_FROM && !GDFlags.ENTITY_TELEPORT_TO) {
+            return;
+        }
+
+        final Entity entity = event.getTargetEntity();
+        if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(entity.getWorld().getUniqueId())) {
+            return;
+        }
+        final boolean teleportFromBlacklisted = GriefDefenderPlugin.isSourceIdBlacklisted(Flags.ENTITY_TELEPORT_FROM.getName(), entity, entity.getWorld().getProperties());
+        final boolean teleportToBlacklisted = GriefDefenderPlugin.isSourceIdBlacklisted(Flags.ENTITY_TELEPORT_TO.getName(), entity, entity.getWorld().getProperties());
+        final boolean portalUseBlacklisted = GriefDefenderPlugin.isSourceIdBlacklisted(Flags.PORTAL_USE.getName(), entity, entity.getWorld().getProperties());
+        if (teleportFromBlacklisted && teleportToBlacklisted && portalUseBlacklisted) {
+            return;
+        }
+
+        GDTimings.ENTITY_TELEPORT_EVENT.startTimingIfSync();
+        Player player = null;
+        GDPermissionUser user = null;
+        if (entity instanceof Player) {
+            player = (Player) entity;
+            user = PermissionHolderCache.getInstance().getOrCreateUser(player);
+        } else {
+            user = PermissionHolderCache.getInstance().getOrCreateUser(entity.getCreator().orElse(null));
+        }
+
+        if (user == null) {
+            GDTimings.ENTITY_TELEPORT_EVENT.stopTimingIfSync();
+            return;
+        }
+
+        final Cause cause = event.getCause();
+        final EventContext context = cause.getContext();
+
+        final TeleportType type = context.get(EventContextKeys.TELEPORT_TYPE).orElse(TeleportTypes.ENTITY_TELEPORT);
+        final Location<World> sourceLocation = event.getFromTransform().getLocation();
+        final Location<World> destination = event.getToTransform().getLocation();
+        // Handle BorderClaimEvent
+        if (!CommonEntityEventHandler.getInstance().onEntityMove(event, sourceLocation, destination, player)) {
+            event.setCancelled(true);
+            GDTimings.ENTITY_TELEPORT_EVENT.stopTiming();
+            return;
+        }
+
+        GDClaim sourceClaim = null;
+        GDPlayerData playerData = null;
+        if (player != null) {
+            playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+            sourceClaim = this.dataStore.getClaimAtPlayer(playerData, player.getLocation());
+        } else {
+            sourceClaim = this.dataStore.getClaimAt(sourceLocation);
+        }
+
+        if (sourceClaim != null) {
+            if (GDFlags.ENTITY_TELEPORT_FROM && !teleportFromBlacklisted && GDPermissionManager.getInstance().getFinalPermission(event, sourceLocation, sourceClaim, GDPermissions.ENTITY_TELEPORT_FROM, type, entity, user, TrustTypes.ACCESSOR, true) == Tristate.FALSE) {
+                boolean cancelled = true;
+                if (GDFlags.PORTAL_USE && type.equals(TeleportTypes.PORTAL)) {
+                    if (portalUseBlacklisted || GDPermissionManager.getInstance().getFinalPermission(event, sourceLocation, sourceClaim, GDPermissions.PORTAL_USE, type, entity, user) == Tristate.TRUE) {
+                        cancelled = false;
+                    }
+                }
+                if (cancelled) {
+                    final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.PERMISSION_PORTAL_EXIT,
+                            ImmutableMap.of(
+                            "player", sourceClaim.getOwnerName()));
+                    if (player != null) {
+                        GriefDefenderPlugin.sendMessage(player, message);
+                    }
+
+                    event.setCancelled(true);
+                    GDTimings.ENTITY_TELEPORT_EVENT.stopTimingIfSync();
+                    return;
+                }
+            }
+        }
+
+        // check if destination world is enabled
+        if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(event.getToTransform().getExtent().getUniqueId())) {
+            GDTimings.ENTITY_TELEPORT_EVENT.stopTimingIfSync();
+            return;
+        }
+
+        final GDClaim toClaim = this.dataStore.getClaimAt(destination);
+        if (toClaim != null) {
+            if (GDFlags.ENTITY_TELEPORT_TO && !teleportToBlacklisted && GDPermissionManager.getInstance().getFinalPermission(event, destination, toClaim, GDPermissions.ENTITY_TELEPORT_TO, type, entity, user, TrustTypes.ACCESSOR, true) == Tristate.FALSE) {
+                boolean cancelled = true;
+                if (GDFlags.PORTAL_USE && type.equals(TeleportTypes.PORTAL)) {
+                    if (portalUseBlacklisted || GDPermissionManager.getInstance().getFinalPermission(event, destination, toClaim, GDPermissions.PORTAL_USE, type, entity, user, TrustTypes.ACCESSOR, true) == Tristate.TRUE) {
+                        cancelled = false;
+                    }
+                }
+                if (cancelled) {
+                    final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.PERMISSION_PORTAL_ENTER,
+                            ImmutableMap.of(
+                            "player", toClaim.getOwnerName()));
+                    if (player != null) {
+                        GriefDefenderPlugin.sendMessage(player, message);
+                    }
+    
+                    if (type.equals(EntityTypes.ENDER_PEARL)) {
+                        player.getInventory().offer(ItemStack.of(ItemTypes.ENDER_PEARL, 1));
+                    }
+                    event.setCancelled(true);
+                    GDTimings.ENTITY_TELEPORT_EVENT.stopTimingIfSync();
+                    return;
+                }
+            }
+        }
+
+        if (player != null && !sourceLocation.getExtent().getUniqueId().equals(destination.getExtent().getUniqueId())) {
+            // new world, check if player has world storage for it
+            GDClaimManager claimWorldManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(destination.getExtent().getUniqueId());
+
+            // update lastActive timestamps for claims this player owns
+            WorldProperties worldProperties = destination.getExtent().getProperties();
+            UUID playerUniqueId = player.getUniqueId();
+            for (Claim claim : this.dataStore.getClaimWorldManager(worldProperties.getUniqueId()).getWorldClaims()) {
+                if (claim.getOwnerUniqueId().equals(playerUniqueId)) {
+                    // update lastActive timestamp for claim
+                    claim.getData().setDateLastActive(Instant.now());
+                    claimWorldManager.addClaim(claim);
+                } else if (claim.getParent().isPresent() && claim.getParent().get().getOwnerUniqueId().equals(playerUniqueId)) {
+                    // update lastActive timestamp for subdivisions if parent owner logs on
+                    claim.getData().setDateLastActive(Instant.now());
+                    claimWorldManager.addClaim(claim);
+                }
+            }
+        }
+
+        if (playerData != null) {
+            if (toClaim.isTown()) {
+                playerData.inTown = true;
+            } else {
+                playerData.inTown = false;
+            }
+        }
+        // TODO
+        /*if (event.getCause().first(PortalTeleportCause.class).isPresent()) {
+            // FEATURE: when players get trapped in a nether portal, send them back through to the other side
+            CheckForPortalTrapTask task = new CheckForPortalTrapTask(player, event.getFromTransform().getLocation());
+            Sponge.getGame().getScheduler().createTaskBuilder().delayTicks(200).execute(task).submit(GriefDefender.instance);
+        }*/
+        GDTimings.ENTITY_TELEPORT_EVENT.stopTimingIfSync();
+    }
+
+    // Protects Item Frames
+    @Listener(order = Order.FIRST, beforeModifications = true)
+    public void onEntityCollideEntity(CollideEntityEvent event) {
+        if (!GDFlags.COLLIDE_ENTITY || event instanceof CollideEntityEvent.Impact) {
+            return;
+        }
+        //if (GriefDefenderPlugin.isSourceIdBlacklisted(ClaimFlag.ENTITY_COLLIDE_ENTITY.toString(), event.getSource(), event.getEntities().get(0).getWorld().getProperties())) {
+        //    return;
+        //}
+
+        Object rootCause = event.getCause().root();
+        final boolean isRootEntityItemFrame = rootCause instanceof ItemFrame;
+        if (!isRootEntityItemFrame) {
+            return;
+        }
+
+        GDTimings.ENTITY_COLLIDE_EVENT.startTimingIfSync();
+        event.filterEntities(new Predicate<Entity>() {
+            @Override
+            public boolean test(Entity entity) {
+                // Avoid living entities breaking itemframes
+                if (isRootEntityItemFrame && entity instanceof Living) {
+                    return false;
+                }
+
+                return true;
+            }
+        });
+        GDTimings.ENTITY_COLLIDE_EVENT.stopTimingIfSync();
+    }
+
+    @Listener(order = Order.FIRST, beforeModifications = true)
+    public void onProjectileImpactEntity(CollideEntityEvent.Impact event) {
+        if (!GDFlags.PROJECTILE_IMPACT_ENTITY) {
+            return;
+        }
+        if (GriefDefenderPlugin.isSourceIdBlacklisted(Flags.PROJECTILE_IMPACT_ENTITY.getName(), event.getSource(), event.getImpactPoint().getExtent().getProperties())) {
+            return;
+        }
+
+        final User user = CauseContextHelper.getEventUser(event);
+        if (user == null || !GriefDefenderPlugin.getInstance().claimsEnabledForWorld(event.getImpactPoint().getExtent().getUniqueId())) {
+            return;
+        }
+
+        GDTimings.PROJECTILE_IMPACT_ENTITY_EVENT.startTimingIfSync();
+        Object source = event.getCause().root();
+        Location<World> impactPoint = event.getImpactPoint();
+        GDClaim targetClaim = null;
+        for (Entity entity : event.getEntities()) {
+            if (GriefDefenderPlugin.isTargetIdBlacklisted(Flags.PROJECTILE_IMPACT_ENTITY.getName(), entity, event.getImpactPoint().getExtent().getProperties())) {
+                return;
+            }
+            targetClaim = this.dataStore.getClaimAt(impactPoint, targetClaim);
+            final Tristate result = GDPermissionManager.getInstance().getFinalPermission(event, impactPoint, targetClaim, GDPermissions.PROJECTILE_IMPACT_ENTITY, source, entity, user, TrustTypes.ACCESSOR, true);
+            if (result == Tristate.FALSE) {
+                if (GDPermissionManager.getInstance().getFinalPermission(event, impactPoint, targetClaim, GDPermissions.PROJECTILE_IMPACT_ENTITY, source, entity, user) == Tristate.TRUE) {
+                    GDTimings.PROJECTILE_IMPACT_ENTITY_EVENT.stopTimingIfSync();
+                    return;
+                }
+
+                event.setCancelled(true);
+            }
+        }
+        GDTimings.PROJECTILE_IMPACT_ENTITY_EVENT.stopTimingIfSync();
+    }
+
+    @Listener(order = Order.FIRST, beforeModifications = true)
+    public void onEntityMount(RideEntityEvent event) {
+        if (!GDFlags.ENTITY_RIDING) {
+            return;
+        }
+
+        final Entity entity = event.getTargetEntity();
+        final World world = entity.getWorld();
+        if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(world.getUniqueId())) {
+            return;
+        }
+        if (GriefDefenderPlugin.isSourceIdBlacklisted(Flags.ENTITY_RIDING.getName(), entity, world.getUniqueId())) {
+            return;
+        }
+        if (GriefDefenderPlugin.isTargetIdBlacklisted(Flags.ENTITY_RIDING.getName(), entity, world.getUniqueId())) {
+            return;
+        }
+
+        GDTimings.ENTITY_MOUNT_EVENT.startTiming();
+        final Object source = event.getSource();
+        Player player = source instanceof Player ? (Player) source : null;
+        final Location<World> location = entity.getLocation();
+        final GDClaim targetClaim = GriefDefenderPlugin.getInstance().dataStore.getClaimAt(location);
+
+        if (GDPermissionManager.getInstance().getFinalPermission(event, location, targetClaim, GDPermissions.ENTITY_RIDING, source, entity, player, TrustTypes.ACCESSOR, true) == Tristate.FALSE) {
+            if (player != null) {
+                //sendInteractEntityDenyMessage(targetClaim, player, null, entity);
+            }
+            event.setCancelled(true);
+        }
+
+        GDTimings.ENTITY_MOUNT_EVENT.stopTiming();
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/listener/LuckPermsEventHandler.java b/sponge/src/main/java/com/griefdefender/listener/LuckPermsEventHandler.java
new file mode 100644
index 0000000..4de9475
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/listener/LuckPermsEventHandler.java
@@ -0,0 +1,54 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.listener;
+
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.permission.GDPermissionHolder;
+import me.lucko.luckperms.api.LuckPermsApi;
+import me.lucko.luckperms.api.event.group.GroupDataRecalculateEvent;
+import me.lucko.luckperms.api.event.user.UserDataRecalculateEvent;
+
+public class LuckPermsEventHandler {
+
+    private final LuckPermsApi luckPermsApi;
+
+    public LuckPermsEventHandler(LuckPermsApi luckPermsApi) {
+        this.luckPermsApi = luckPermsApi;
+        this.luckPermsApi.getEventBus().subscribe(GroupDataRecalculateEvent.class, this::onGroupDataRecalculate);
+        this.luckPermsApi.getEventBus().subscribe(UserDataRecalculateEvent.class, this::onUserDataRecalculate);
+    }
+
+    public void onGroupDataRecalculate(GroupDataRecalculateEvent event) {
+        final GDPermissionHolder holder = PermissionHolderCache.getInstance().getOrCreateGroup(event.getGroup().getName());
+        PermissionHolderCache.getInstance().getOrCreatePermissionCache(holder).invalidateAll();
+    }
+
+    public void onUserDataRecalculate(UserDataRecalculateEvent event) {
+        final GDPermissionHolder holder = PermissionHolderCache.getInstance().getOrCreateUser(event.getUser().getUuid());
+        PermissionHolderCache.getInstance().getOrCreatePermissionCache(holder).invalidateAll();
+        PermissionHolderCache.getInstance().getOrCreatePermissionCache(GriefDefenderPlugin.DEFAULT_HOLDER).invalidateAll();
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/listener/MCClansEventHandler.java b/sponge/src/main/java/com/griefdefender/listener/MCClansEventHandler.java
new file mode 100644
index 0000000..fc489f2
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/listener/MCClansEventHandler.java
@@ -0,0 +1,79 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.listener;
+
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.claim.GDClaim;
+import nl.riebie.mcclans.api.events.ClanCreateEvent;
+import nl.riebie.mcclans.api.events.ClanSetHomeEvent;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.world.World;
+
+public class MCClansEventHandler {
+
+    public void onClanSetHome(ClanSetHomeEvent event) {
+        final World world = event.getLocation().getExtent();
+        if (!GriefDefenderPlugin.getGlobalConfig().getConfig().town.clanRequireTown || !GriefDefenderPlugin.getInstance().claimsEnabledForWorld(world.getUniqueId())) {
+            return;
+        }
+
+        final GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAt(event.getLocation());
+        if (!claim.isInTown()) {
+            event.setCancelledWithMessage("You must be in a town in order to set your clan home.");
+            return;
+        }
+        if (!claim.getOwnerUniqueId().equals(event.getClan().getOwner().getUUID())) {
+            event.setCancelledWithMessage("You do not own this town.");
+            return;
+        }
+    }
+
+    public void onClanCreate(ClanCreateEvent event) {
+        if (!GriefDefenderPlugin.getGlobalConfig().getConfig().town.clanRequireTown) {
+            return;
+        }
+
+        final Player player = Sponge.getServer().getPlayer(event.getOwner().getUUID()).orElse(null);
+        if (player == null) {
+            return;
+        }
+
+        final World world = player.getWorld();
+        if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(world.getUniqueId())) {
+            return;
+        }
+
+        final GDPlayerData playerData  = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(world, player.getUniqueId());
+        for (Claim claim : playerData.getInternalClaims()) {
+            if (claim.isTown()) {
+                return;
+            }
+        }
+        event.setCancelledWithMessage("You must own a town in order to create a clan.");
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/listener/NucleusEventHandler.java b/sponge/src/main/java/com/griefdefender/listener/NucleusEventHandler.java
new file mode 100644
index 0000000..1917ce4
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/listener/NucleusEventHandler.java
@@ -0,0 +1,59 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.listener;
+
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.claim.TrustTypes;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.storage.BaseStorage;
+import com.griefdefender.util.SpongeUtil;
+import io.github.nucleuspowered.nucleus.api.events.NucleusHomeEvent;
+import net.kyori.text.TextComponent;
+import net.kyori.text.format.TextColor;
+import org.spongepowered.api.event.Listener;
+import org.spongepowered.api.world.Location;
+import org.spongepowered.api.world.World;
+
+public class NucleusEventHandler {
+
+    private static final BaseStorage DATASTORE = GriefDefenderPlugin.getInstance().dataStore;
+
+    @Listener
+    public void onSetHome(NucleusHomeEvent.Create event) {
+        Location<World> location = event.getLocation().orElse(null);
+        if (location == null) {
+            return;
+        }
+
+        GDClaim claim = DATASTORE.getClaimAt(location);
+        if (claim != null && !claim.isWilderness() && !claim.isAdminClaim()) {
+            if (!claim.isUserTrusted(event.getUser(), TrustTypes.ACCESSOR)) {
+                event.setCancelled(true);
+                event.setCancelMessage(SpongeUtil.getSpongeText(TextComponent.of("You must be trusted in order to use /sethome here.").color(TextColor.RED)));
+            }
+        }
+    }
+
+}
diff --git a/sponge/src/main/java/com/griefdefender/listener/PlayerEventHandler.java b/sponge/src/main/java/com/griefdefender/listener/PlayerEventHandler.java
new file mode 100644
index 0000000..2e49220
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/listener/PlayerEventHandler.java
@@ -0,0 +1,1696 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.listener;
+
+import com.flowpowered.math.vector.Vector3d;
+import com.flowpowered.math.vector.Vector3i;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Sets;
+import com.google.common.reflect.TypeToken;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GDTimings;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.Tristate;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.ClaimBlockSystem;
+import com.griefdefender.api.claim.ClaimResult;
+import com.griefdefender.api.claim.ClaimResultType;
+import com.griefdefender.api.claim.ClaimType;
+import com.griefdefender.api.claim.ClaimTypes;
+import com.griefdefender.api.claim.ShovelTypes;
+import com.griefdefender.api.claim.TrustType;
+import com.griefdefender.api.claim.TrustTypes;
+import com.griefdefender.api.permission.flag.Flags;
+import com.griefdefender.api.permission.option.Options;
+import com.griefdefender.api.permission.option.type.CreateModeTypes;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.claim.GDClaimManager;
+import com.griefdefender.claim.GDClaimResult;
+import com.griefdefender.command.CommandHelper;
+import com.griefdefender.configuration.GriefDefenderConfig;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.event.GDCauseStackManager;
+import com.griefdefender.internal.provider.WorldEditProvider;
+import com.griefdefender.internal.util.BlockUtil;
+import com.griefdefender.internal.util.NMSUtil;
+import com.griefdefender.internal.visual.ClaimVisual;
+import com.griefdefender.permission.GDPermissionManager;
+import com.griefdefender.permission.GDPermissions;
+import com.griefdefender.permission.flag.GDFlags;
+import com.griefdefender.provider.NucleusProvider;
+import com.griefdefender.storage.BaseStorage;
+import com.griefdefender.util.EconomyUtil;
+import com.griefdefender.util.PaginationUtil;
+import com.griefdefender.util.PlayerUtil;
+import com.griefdefender.util.SpongeUtil;
+import io.github.nucleuspowered.nucleus.api.chat.NucleusChatChannel;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.format.TextColor;
+
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.block.BlockSnapshot;
+import org.spongepowered.api.block.BlockType;
+import org.spongepowered.api.block.BlockTypes;
+import org.spongepowered.api.block.tileentity.TileEntity;
+import org.spongepowered.api.command.CommandMapping;
+import org.spongepowered.api.command.CommandSource;
+import org.spongepowered.api.data.key.Keys;
+import org.spongepowered.api.data.property.entity.EyeLocationProperty;
+import org.spongepowered.api.data.type.HandType;
+import org.spongepowered.api.data.type.HandTypes;
+import org.spongepowered.api.entity.Entity;
+import org.spongepowered.api.entity.living.ArmorStand;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.entity.living.player.User;
+import org.spongepowered.api.event.Listener;
+import org.spongepowered.api.event.Order;
+import org.spongepowered.api.event.action.InteractEvent;
+import org.spongepowered.api.event.block.InteractBlockEvent;
+import org.spongepowered.api.event.cause.Cause;
+import org.spongepowered.api.event.cause.EventContext;
+import org.spongepowered.api.event.cause.EventContextKeys;
+import org.spongepowered.api.event.command.SendCommandEvent;
+import org.spongepowered.api.event.entity.DestructEntityEvent;
+import org.spongepowered.api.event.entity.InteractEntityEvent;
+import org.spongepowered.api.event.entity.living.humanoid.HandInteractEvent;
+import org.spongepowered.api.event.entity.living.humanoid.player.RespawnPlayerEvent;
+import org.spongepowered.api.event.filter.cause.First;
+import org.spongepowered.api.event.filter.cause.Root;
+import org.spongepowered.api.event.item.inventory.ChangeInventoryEvent;
+import org.spongepowered.api.event.item.inventory.ClickInventoryEvent;
+import org.spongepowered.api.event.item.inventory.DropItemEvent;
+import org.spongepowered.api.event.item.inventory.InteractInventoryEvent;
+import org.spongepowered.api.event.item.inventory.InteractItemEvent;
+import org.spongepowered.api.event.item.inventory.UseItemStackEvent;
+import org.spongepowered.api.event.message.MessageChannelEvent;
+import org.spongepowered.api.event.network.ClientConnectionEvent;
+import org.spongepowered.api.item.ItemType;
+import org.spongepowered.api.item.ItemTypes;
+import org.spongepowered.api.item.inventory.ItemStack;
+import org.spongepowered.api.item.inventory.ItemStackSnapshot;
+import org.spongepowered.api.item.inventory.transaction.SlotTransaction;
+import org.spongepowered.api.plugin.PluginContainer;
+import org.spongepowered.api.service.ban.BanService;
+import org.spongepowered.api.service.economy.Currency;
+import org.spongepowered.api.service.economy.account.Account;
+import org.spongepowered.api.service.user.UserStorageService;
+import org.spongepowered.api.text.Text;
+import org.spongepowered.api.text.channel.MessageChannel;
+import org.spongepowered.api.text.channel.MessageReceiver;
+import org.spongepowered.api.text.channel.type.FixedMessageChannel;
+import org.spongepowered.api.text.format.TextColors;
+import org.spongepowered.api.util.blockray.BlockRay;
+import org.spongepowered.api.util.blockray.BlockRayHit;
+import org.spongepowered.api.world.Chunk;
+import org.spongepowered.api.world.Location;
+import org.spongepowered.api.world.World;
+import org.spongepowered.api.world.storage.WorldProperties;
+import org.spongepowered.common.SpongeImpl;
+import org.spongepowered.common.SpongeImplHooks;
+import org.spongepowered.common.item.inventory.custom.CustomInventory;
+
+import java.math.BigDecimal;
+import java.time.Instant;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.UUID;
+
+public class PlayerEventHandler {
+
+    private final BaseStorage dataStore;
+    private final WorldEditProvider worldEditProvider;
+    private final BanService banService;
+    private int lastInteractItemPrimaryTick = -1;
+    private int lastInteractItemSecondaryTick = -1;
+    private boolean lastInteractItemCancelled = false;
+
+    public PlayerEventHandler(BaseStorage dataStore, GriefDefenderPlugin plugin) {
+        this.dataStore = dataStore;
+        this.worldEditProvider = GriefDefenderPlugin.getInstance().worldEditProvider;
+        this.banService = Sponge.getServiceManager().getRegistration(BanService.class).get().getProvider();
+    }
+
+
+    @Listener(order = Order.FIRST, beforeModifications = true)
+    public void onPlayerChat(MessageChannelEvent.Chat event, @First Player player) {
+        GDTimings.PLAYER_CHAT_EVENT.startTimingIfSync();
+        GriefDefenderConfig<?> activeConfig = GriefDefenderPlugin.getActiveConfig(player.getWorld().getProperties());
+        if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(player.getWorld().getUniqueId())) {
+            GDTimings.PLAYER_CHAT_EVENT.stopTimingIfSync();
+            return;
+        }
+
+        final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        if (playerData.inTown && playerData.townChat) {
+            final MessageChannel channel = event.getChannel().orElse(null);
+            if (GriefDefenderPlugin.getInstance().nucleusApiProvider != null && channel != null) {
+                if (channel instanceof NucleusChatChannel) {
+                    return;
+                }
+            }
+            final GDClaim sourceClaim = this.dataStore.getClaimAtPlayer(playerData, player.getLocation());
+            if (sourceClaim.isInTown()) {
+                playerData.inTown = true;
+            } else {
+                playerData.inTown = false;
+            }
+            final GDClaim sourceTown = sourceClaim.getTownClaim();
+            final Component townTag = sourceTown.getTownData().getTownTag().orElse(null);
+
+            Text header = event.getFormatter().getHeader().toText();
+            Text body = event.getFormatter().getBody().toText();
+            Text footer = event.getFormatter().getFooter().toText();
+            Text townMessage = Text.of(TextColors.GREEN, body);
+            if (townTag != null) {
+                townMessage = Text.of(townTag, townMessage);
+            }
+            event.setMessage(townMessage);
+            Set<CommandSource> recipientsToRemove = new HashSet<>();
+            Iterator<MessageReceiver> iterator = event.getChannel().get().getMembers().iterator();
+            while (iterator.hasNext()) {
+                MessageReceiver receiver = iterator.next();
+                if (receiver instanceof Player) {
+                    Player recipient = (Player) receiver;
+                    if (GriefDefenderPlugin.getInstance().nucleusApiProvider != null) {
+                        if (NucleusProvider.getPrivateMessagingService().isPresent() && NucleusProvider.getPrivateMessagingService().get().isSocialSpy(recipient)) {
+                            // always allow social spy users
+                            continue;
+                        }
+                    }
+
+                    final GDPlayerData targetPlayerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(recipient.getWorld(), recipient.getUniqueId());
+                    if (!targetPlayerData.inTown) {
+                        recipientsToRemove.add(recipient);
+                        continue;
+                    }
+
+                    final GDClaim targetClaim = this.dataStore.getClaimAtPlayer(targetPlayerData, recipient.getLocation());
+                    final GDClaim targetTown = targetClaim.getTownClaim();
+                    if (targetPlayerData.canIgnoreClaim(targetClaim)) {
+                        continue;
+                    }
+                    if (sourceTown != null && (targetTown == null || !sourceTown.getUniqueId().equals(targetTown.getUniqueId()))) {
+                        recipientsToRemove.add(recipient);
+                    }
+                }
+            }
+
+            if (!recipientsToRemove.isEmpty()) {
+                Set<MessageReceiver> newRecipients = Sets.newHashSet(event.getChannel().get().getMembers().iterator());
+                newRecipients.removeAll(recipientsToRemove);
+                event.setChannel(new FixedMessageChannel(newRecipients));
+            }
+        }
+
+        GDTimings.PLAYER_CHAT_EVENT.stopTimingIfSync();
+    }
+
+    // when a player uses a slash command...
+    @Listener(order = Order.FIRST, beforeModifications = true)
+    public void onPlayerCommand(SendCommandEvent event, @First Player player) {
+        if (!GDFlags.COMMAND_EXECUTE && !GDFlags.COMMAND_EXECUTE_PVP) {
+            return;
+        }
+        final boolean commandExecuteSourceBlacklisted = GriefDefenderPlugin.isSourceIdBlacklisted(Flags.COMMAND_EXECUTE.getName(),event.getSource(), player.getWorld().getProperties());
+        final boolean commandExecutePvpSourceBlacklisted = GriefDefenderPlugin.isSourceIdBlacklisted(Flags.COMMAND_EXECUTE_PVP.getName(),event.getSource(), player.getWorld().getProperties());
+
+        GDTimings.PLAYER_COMMAND_EVENT.startTimingIfSync();
+        String command = event.getCommand();
+        String[] args = event.getArguments().split(" ");
+        String[] parts = command.split(":");
+        String pluginId = null;
+
+        if (parts.length > 1) {
+            pluginId = parts[0];
+            command = parts[1];
+        }
+
+        String message = "/" + event.getCommand() + " " + event.getArguments();
+        if (pluginId == null || !pluginId.equals("minecraft")) {
+            CommandMapping commandMapping = Sponge.getCommandManager().get(command).orElse(null);
+            PluginContainer pluginContainer = null;
+            if (commandMapping != null) {
+                pluginContainer = Sponge.getCommandManager().getOwner(commandMapping).orElse(null);
+                if (pluginContainer != null) {
+                    pluginId = pluginContainer.getId();
+                }
+            }
+            if (pluginId == null) {
+                pluginId = "minecraft";
+            }
+        }
+
+        PaginationUtil.getInstance().updateActiveCommand(player.getUniqueId(), command, event.getArguments());
+        if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(player.getWorld().getUniqueId())) {
+            GDTimings.PLAYER_COMMAND_EVENT.stopTimingIfSync();
+            return;
+        }
+
+        GDPlayerData playerData = this.dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        // if requires access trust, check for permission
+        Location<World> location = player.getLocation();
+        GDClaim claim = this.dataStore.getClaimAtPlayer(playerData, location);
+        if (playerData.canIgnoreClaim(claim)) {
+            GDTimings.PLAYER_COMMAND_EVENT.stopTimingIfSync();
+            return;
+        }
+        String commandPermission = pluginId + "." + command;
+
+        // first check the args
+        String argument = "";
+        for (String arg : args) {
+            argument = argument + "." + arg;
+        }
+
+        if (GDFlags.COMMAND_EXECUTE && !commandExecuteSourceBlacklisted && !GriefDefenderPlugin.isTargetIdBlacklisted(Flags.COMMAND_EXECUTE.getName(), commandPermission + argument, player.getWorld().getProperties())) {
+            final Tristate result = GDPermissionManager.getInstance().getFinalPermission(event, player.getLocation(), claim, GDPermissions.COMMAND_EXECUTE, event.getSource(), commandPermission + argument, player);
+            if (result == Tristate.TRUE) {
+                GDTimings.PLAYER_COMMAND_EVENT.stopTimingIfSync();
+                return;
+            }
+            if (result == Tristate.FALSE) {
+                final Component denyMessage = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.COMMAND_BLOCKED,
+                        ImmutableMap.of(
+                        "command", command,
+                        "player", claim.getOwnerName()));
+                GriefDefenderPlugin.sendMessage(player, denyMessage);
+                event.setCancelled(true);
+                GDTimings.PLAYER_COMMAND_EVENT.stopTimingIfSync();
+                return;
+            }
+        }
+        /*if (GDFlags.COMMAND_EXECUTE_PVP && !commandExecutePvpSourceBlacklisted && playerData != null && (playerData.inPvpCombat(player.getWorld())) && !GriefDefenderPlugin.isTargetIdBlacklisted(Flags.COMMAND_EXECUTE_PVP.getName(), commandPermission + argument, player.getWorld().getProperties())) {
+            final Tristate result = GPPermissionManager.getInstance().getFinalPermission(event, player.getLocation(), claim, GPPermissions.COMMAND_EXECUTE_PVP, event.getSource(), commandPermission + argument, player);
+            if (result == Tristate.TRUE) {
+                GDTimings.PLAYER_COMMAND_EVENT.stopTimingIfSync();
+                return;
+            }
+            if (result == Tristate.FALSE) {
+                final Component denyMessage = GriefDefenderPlugin.getInstance().messageData.pvpCommandBanned
+                        .apply(ImmutableMap.of(
+                        "command", command)).build();
+                GriefDefenderPlugin.sendMessage(event.getCause().first(Player.class).get(), denyMessage);
+                event.setCancelled(true);
+                GDTimings.PLAYER_COMMAND_EVENT.stopTimingIfSync();
+                return;
+            }
+        }*/
+
+        GDTimings.PLAYER_COMMAND_EVENT.stopTimingIfSync();
+    }
+
+    @Listener(order = Order.FIRST, beforeModifications = true)
+    public void onPlayerLogin(ClientConnectionEvent.Login event) {
+        GDTimings.PLAYER_LOGIN_EVENT.startTimingIfSync();
+        User player = event.getTargetUser();
+        if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(event.getToTransform().getExtent().getUniqueId())) {
+            GDTimings.PLAYER_LOGIN_EVENT.stopTimingIfSync();
+            return;
+        }
+
+        final WorldProperties worldProperties = event.getToTransform().getExtent().getProperties();
+        final UUID playerUniqueId = player.getUniqueId();
+        final GDClaimManager claimWorldManager = this.dataStore.getClaimWorldManager(worldProperties.getUniqueId());
+        final Instant dateNow = Instant.now();
+        for (Claim claim : claimWorldManager.getWorldClaims()) {
+            if (claim.getType() != ClaimTypes.ADMIN && claim.getOwnerUniqueId().equals(playerUniqueId)) {
+                claim.getData().setDateLastActive(dateNow);
+                for (Claim subdivision : ((GDClaim) claim).children) {
+                    subdivision.getData().setDateLastActive(dateNow);
+                }
+                ((GDClaim) claim).getInternalClaimData().setRequiresSave(true);
+            }
+        }
+        GDTimings.PLAYER_LOGIN_EVENT.stopTimingIfSync();
+    }
+
+    @Listener(order = Order.FIRST)
+    public void onPlayerJoin(ClientConnectionEvent.Join event) {
+        GDTimings.PLAYER_JOIN_EVENT.startTimingIfSync();
+        Player player = event.getTargetEntity();
+        if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(player.getWorld().getUniqueId())) {
+            GDTimings.PLAYER_JOIN_EVENT.stopTimingIfSync();
+            return;
+        }
+
+        UUID playerID = player.getUniqueId();
+
+        final GDPlayerData playerData = this.dataStore.getOrCreatePlayerData(player.getWorld(), playerID);
+        final GDClaim claim = this.dataStore.getClaimAtPlayer(playerData, player.getLocation());
+        if (claim.isInTown()) {
+            playerData.inTown = true;
+        }
+
+        GDTimings.PLAYER_JOIN_EVENT.stopTimingIfSync();
+    }
+
+    @Listener(order= Order.LAST)
+    public void onPlayerQuit(ClientConnectionEvent.Disconnect event) {
+        final Player player = event.getTargetEntity();
+        if (!SpongeImpl.getServer().isServerRunning() || !GriefDefenderPlugin.getInstance().claimsEnabledForWorld(player.getWorld().getUniqueId())) {
+            return;
+        }
+
+        GDTimings.PLAYER_QUIT_EVENT.startTimingIfSync();
+        UUID playerID = player.getUniqueId();
+        GDPlayerData playerData = this.dataStore.getOrCreatePlayerData(player.getWorld(), playerID);
+
+        if (this.worldEditProvider != null) {
+            this.worldEditProvider.revertVisuals(player, playerData, null);
+            this.worldEditProvider.removePlayer(player);
+        }
+
+        playerData.onDisconnect();
+        PaginationUtil.getInstance().removeActivePageData(player.getUniqueId());
+        if (playerData.getClaims().isEmpty()) {
+            this.dataStore.clearCachedPlayerData(player.getWorld().getUniqueId(), playerID);
+        }
+
+        GDTimings.PLAYER_QUIT_EVENT.stopTimingIfSync();
+    }
+
+    @Listener(order = Order.FIRST, beforeModifications = true)
+    public void onPlayerDeath(DestructEntityEvent.Death event) {
+        if (!(event.getTargetEntity() instanceof Player)) {
+            return;
+        }
+
+        final Player player = (Player) event.getTargetEntity();
+        GDCauseStackManager.getInstance().pushCause(player);
+        final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        final GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(playerData, player.getLocation());
+        final Tristate keepInventory = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Tristate.class), playerData.getSubject(), Options.PLAYER_KEEP_INVENTORY, claim);
+        //final Tristate keepLevel = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Tristate.class), playerData.getSubject(), Options.PLAYER_KEEP_LEVEL, claim);
+        if (keepInventory != Tristate.UNDEFINED) {
+            event.setKeepInventory(keepInventory.asBoolean());
+        }
+        //if (keepLevel != Tristate.UNDEFINED) {
+        //    event.setKeepLevel(keepLevel.asBoolean());
+       // }
+    }
+
+    @Listener(order = Order.FIRST, beforeModifications = true)
+    public void onPlayerRespawn(RespawnPlayerEvent event) {
+        final World world = event.getToTransform().getExtent();
+        if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(world.getUniqueId())) {
+            return;
+        }
+
+        final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(event.getTargetEntity().getWorld(), event.getTargetEntity().getUniqueId());
+        playerData.lastPvpTimestamp = null;
+    }
+
+    @Listener(order = Order.FIRST, beforeModifications = true)
+    public void onPlayerDispenseItem(DropItemEvent.Dispense event, @Root Entity spawncause) {
+        if (!GDFlags.ITEM_DROP || !(spawncause instanceof User)) {
+            return;
+        }
+
+        final User user = (User) spawncause;
+        final World world = spawncause.getWorld();
+        if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(world.getUniqueId())) {
+            return;
+        }
+
+        GDTimings.PLAYER_DISPENSE_ITEM_EVENT.startTimingIfSync();
+        Player player = user instanceof Player ? (Player) user : null;
+        GDPlayerData playerData = this.dataStore.getOrCreatePlayerData(world, user.getUniqueId());
+
+        for (Entity entityItem : event.getEntities()) {
+            if (GriefDefenderPlugin.isTargetIdBlacklisted(Flags.ITEM_DROP.toString(), entityItem, world.getProperties())) {
+                continue;
+            }
+
+            Location<World> location = entityItem.getLocation();
+            GDClaim claim = this.dataStore.getClaimAtPlayer(playerData, location);
+            if (claim != null) {
+                if (GDPermissionManager.getInstance().getFinalPermission(event, location, claim, GDPermissions.ITEM_DROP, user, entityItem, user, TrustTypes.ACCESSOR, true) == Tristate.FALSE) {
+                    event.setCancelled(true);
+                    if (spawncause instanceof Player) {
+                        final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.PERMISSION_ITEM_DROP,
+                                ImmutableMap.of(
+                                "player", claim.getOwnerName(),
+                                "item", entityItem.getType().getId()));
+                        GriefDefenderPlugin.sendClaimDenyMessage(claim, player, message);
+                    }
+                    GDTimings.PLAYER_DISPENSE_ITEM_EVENT.stopTimingIfSync();
+                    return;
+                }
+            }
+        }
+        GDTimings.PLAYER_DISPENSE_ITEM_EVENT.stopTimingIfSync();
+    }
+
+    @Listener(order = Order.FIRST, beforeModifications = true)
+    public void onPlayerInteractInventoryOpen(InteractInventoryEvent.Open event, @First Player player) {
+        if (!GDFlags.INTERACT_INVENTORY || !GriefDefenderPlugin.getInstance().claimsEnabledForWorld(player.getWorld().getUniqueId())) {
+            return;
+        }
+
+        final Cause cause = event.getCause();
+        final EventContext context = cause.getContext();
+        final BlockSnapshot blockSnapshot = context.get(EventContextKeys.BLOCK_HIT).orElse(BlockSnapshot.NONE);
+        if (blockSnapshot == BlockSnapshot.NONE) {
+            return;
+        }
+        if (GriefDefenderPlugin.isTargetIdBlacklisted(Flags.INTERACT_INVENTORY.getName(), blockSnapshot, player.getWorld().getProperties())) {
+            return;
+        }
+
+        GDTimings.PLAYER_INTERACT_INVENTORY_OPEN_EVENT.startTimingIfSync();
+        final Location<World> location = blockSnapshot.getLocation().get();
+        final GDClaim claim = this.dataStore.getClaimAt(location);
+        final Tristate result = GDPermissionManager.getInstance().getFinalPermission(event, location, claim, GDPermissions.INVENTORY_OPEN, player, blockSnapshot, player, TrustTypes.CONTAINER, true);
+        if (result == Tristate.FALSE) {
+            final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.PERMISSION_INVENTORY_OPEN,
+                    ImmutableMap.of(
+                    "player", claim.getOwnerName(),
+                    "block", blockSnapshot.getState().getType().getId()));
+            GriefDefenderPlugin.sendClaimDenyMessage(claim, player, message);
+            NMSUtil.getInstance().closePlayerScreen(player);
+            event.setCancelled(true);
+        }
+
+        GDTimings.PLAYER_INTERACT_INVENTORY_OPEN_EVENT.stopTimingIfSync();
+    }
+
+    @Listener(order = Order.FIRST, beforeModifications = true)
+    public void onPlayerInteractInventoryClose(InteractInventoryEvent.Close event, @Root Player player) {
+        final ItemStackSnapshot cursor = event.getCursorTransaction().getOriginal();
+        if (cursor == ItemStackSnapshot.NONE || !GDFlags.ITEM_DROP || !GriefDefenderPlugin.getInstance().claimsEnabledForWorld(player.getWorld().getUniqueId())) {
+            return;
+        }
+        if (GriefDefenderPlugin.isTargetIdBlacklisted(Flags.ITEM_DROP.getName(), cursor, player.getWorld().getProperties())) {
+            return;
+        }
+
+        GDTimings.PLAYER_INTERACT_INVENTORY_CLOSE_EVENT.startTimingIfSync();
+        final Location<World> location = player.getLocation();
+        final GDClaim claim = this.dataStore.getClaimAt(location);
+        if (GDPermissionManager.getInstance().getFinalPermission(event, location, claim, GDPermissions.ITEM_DROP, player, cursor, player, TrustTypes.ACCESSOR, true) == Tristate.FALSE) {
+            final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.PERMISSION_ITEM_DROP,
+                    ImmutableMap.of(
+                    "player", claim.getOwnerName(),
+                    "item", cursor.getType().getId()));
+            GriefDefenderPlugin.sendClaimDenyMessage(claim, player, message);
+            event.setCancelled(true);
+        }
+
+        GDTimings.PLAYER_INTERACT_INVENTORY_CLOSE_EVENT.stopTimingIfSync();
+    }
+
+    @Listener(order = Order.FIRST, beforeModifications = true)
+    public void onPlayerInteractInventoryClick(ClickInventoryEvent event, @First Player player) {
+        if (!GDFlags.INTERACT_INVENTORY_CLICK || !GriefDefenderPlugin.getInstance().claimsEnabledForWorld(player.getWorld().getUniqueId())) {
+            return;
+        }
+
+        if (NMSUtil.getInstance().isContainerCustomInventory(event.getTargetInventory())) {
+        }
+
+        GDTimings.PLAYER_INTERACT_INVENTORY_CLICK_EVENT.startTimingIfSync();
+        final Location<World> location = player.getLocation();
+        final GDClaim claim = this.dataStore.getClaimAt(location);
+        final boolean isDrop = event instanceof ClickInventoryEvent.Drop;
+        final ItemStackSnapshot cursorItem = event.getCursorTransaction().getOriginal();
+        // check if original cursor item can be dropped
+        if (isDrop && cursorItem != ItemStackSnapshot.NONE && !GriefDefenderPlugin.isTargetIdBlacklisted(Flags.ITEM_DROP.getName(), cursorItem, player.getWorld().getProperties())) {
+            if (GDPermissionManager.getInstance().getFinalPermission(event, location, claim, GDPermissions.ITEM_DROP, player, cursorItem, player, TrustTypes.ACCESSOR, true) == Tristate.FALSE) {
+                final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.PERMISSION_ITEM_DROP,
+                        ImmutableMap.of(
+                        "player", claim.getOwnerName(),
+                        "item", cursorItem.getType().getId()));
+                GriefDefenderPlugin.sendClaimDenyMessage(claim, player, message);
+                event.setCancelled(true);
+                GDTimings.PLAYER_INTERACT_INVENTORY_CLICK_EVENT.stopTimingIfSync();
+                return;
+            }
+        }
+        for (SlotTransaction transaction : event.getTransactions()) {
+            if (transaction.getOriginal() == ItemStackSnapshot.NONE) {
+                continue;
+            }
+
+            if (GriefDefenderPlugin.isTargetIdBlacklisted(Flags.INTERACT_INVENTORY_CLICK.getName(), transaction.getOriginal(), player.getWorld().getProperties())) {
+                continue;
+            }
+
+            final Tristate result = GDPermissionManager.getInstance().getFinalPermission(event, location, claim, GDPermissions.INVENTORY_CLICK, player, transaction.getOriginal(), player, TrustTypes.CONTAINER, true);
+            if (result == Tristate.FALSE) {
+                Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.PERMISSION_INTERACT_ITEM,
+                        ImmutableMap.of(
+                        "player", claim.getOwnerName(),
+                        "item", transaction.getOriginal().getType().getId()));
+                GriefDefenderPlugin.sendClaimDenyMessage(claim, player, message);
+                event.setCancelled(true);
+                GDTimings.PLAYER_INTERACT_INVENTORY_CLICK_EVENT.stopTimingIfSync();
+                return;
+            }
+
+            if (isDrop && transaction.getFinal() != ItemStackSnapshot.NONE) {
+                if (GriefDefenderPlugin.isTargetIdBlacklisted(Flags.ITEM_DROP.getName(), transaction.getFinal(), player.getWorld().getProperties())) {
+                    continue;
+                }
+
+                if (GDPermissionManager.getInstance().getFinalPermission(event, location, claim, GDPermissions.ITEM_DROP, player, transaction.getFinal(), player, TrustTypes.ACCESSOR, true) == Tristate.FALSE) {
+                    final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.PERMISSION_ITEM_DROP,
+                            ImmutableMap.of(
+                            "player", claim.getOwnerName(),
+                            "item", transaction.getFinal().getType().getId()));
+                    GriefDefenderPlugin.sendClaimDenyMessage(claim, player, message);
+                    event.setCancelled(true);
+                    GDTimings.PLAYER_INTERACT_INVENTORY_CLICK_EVENT.stopTimingIfSync();
+                    return;
+                }
+            }
+        }
+        GDTimings.PLAYER_INTERACT_INVENTORY_CLICK_EVENT.stopTimingIfSync();
+    }
+ 
+    // when a player interacts with an entity...
+    @Listener(order = Order.FIRST, beforeModifications = true)
+    public void onPlayerInteractEntity(InteractEntityEvent.Primary event, @First Player player) {
+        if (!GDFlags.INTERACT_ENTITY_PRIMARY || !GriefDefenderPlugin.getInstance().claimsEnabledForWorld(player.getWorld().getUniqueId())) {
+            return;
+        }
+
+        final Entity targetEntity = event.getTargetEntity();
+        final HandType handType = event.getHandType();
+        final ItemStack itemInHand = player.getItemInHand(handType).orElse(ItemStack.empty());
+        final Object source = player;
+        if (GriefDefenderPlugin.isTargetIdBlacklisted(Flags.INTERACT_ENTITY_PRIMARY.getName(), targetEntity, player.getWorld().getProperties())) {
+            return;
+        }
+
+        GDTimings.PLAYER_INTERACT_ENTITY_PRIMARY_EVENT.startTimingIfSync();
+        Location<World> location = targetEntity.getLocation();
+        GDClaim claim = this.dataStore.getClaimAt(location);
+        if (event.isCancelled() && claim.getData().getPvpOverride() == Tristate.TRUE && targetEntity instanceof Player) {
+            event.setCancelled(false);
+        }
+
+        Tristate result = GDPermissionManager.getInstance().getFinalPermission(event, location, claim, GDPermissions.INTERACT_ENTITY_PRIMARY, source, targetEntity, player, TrustTypes.ACCESSOR, true);
+        if (result == Tristate.FALSE) {
+            if (GDPermissionManager.getInstance().getFinalPermission(event, location, claim, GDPermissions.ENTITY_DAMAGE, source, targetEntity, player, TrustTypes.ACCESSOR, true) != Tristate.TRUE) {
+                final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.CLAIM_PROTECTED_ENTITY,
+                        ImmutableMap.of(
+                        "player", claim.getOwnerName()));
+                GriefDefenderPlugin.sendMessage(player, message);
+                event.setCancelled(true);
+                this.sendInteractEntityDenyMessage(itemInHand, targetEntity, claim, player, handType);
+                GDTimings.PLAYER_INTERACT_ENTITY_PRIMARY_EVENT.stopTimingIfSync();
+                return;
+            }
+        }
+    }
+
+    // when a player interacts with an entity...
+    @Listener(order = Order.FIRST, beforeModifications = true)
+    public void onPlayerInteractEntity(InteractEntityEvent.Secondary event, @First Player player) {
+        if (!GDFlags.INTERACT_ENTITY_SECONDARY || !GriefDefenderPlugin.getInstance().claimsEnabledForWorld(player.getWorld().getUniqueId())) {
+            return;
+        }
+
+        final Entity targetEntity = event.getTargetEntity();
+        final HandType handType = event.getHandType();
+        final ItemStack itemInHand = player.getItemInHand(handType).orElse(ItemStack.empty());
+        final Object source = player;
+        if (GriefDefenderPlugin.isTargetIdBlacklisted(Flags.INTERACT_ENTITY_SECONDARY.getName(), targetEntity, player.getWorld().getProperties())) {
+            return;
+        }
+
+        GDTimings.PLAYER_INTERACT_ENTITY_SECONDARY_EVENT.startTimingIfSync();
+        Location<World> location = targetEntity.getLocation();
+        GDClaim claim = this.dataStore.getClaimAt(location);
+        GDPlayerData playerData = this.dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+
+        if (playerData.canIgnoreClaim(claim)) {
+            GDTimings.PLAYER_INTERACT_ENTITY_SECONDARY_EVENT.stopTimingIfSync();
+            return;
+        }
+
+        Tristate result = GDPermissionManager.getInstance().getFinalPermission(event, location, claim, GDPermissions.INTERACT_ENTITY_SECONDARY, source, targetEntity, player, TrustTypes.ACCESSOR, true);
+        if (result == Tristate.TRUE && targetEntity instanceof ArmorStand) {
+            result = GDPermissionManager.getInstance().getFinalPermission(event, location, claim, GDPermissions.INVENTORY_OPEN, source, targetEntity, player, TrustTypes.CONTAINER, false);
+        }
+        if (result == Tristate.FALSE) {
+            event.setCancelled(true);
+            this.sendInteractEntityDenyMessage(itemInHand, targetEntity, claim, player, handType);
+            GDTimings.PLAYER_INTERACT_ENTITY_SECONDARY_EVENT.stopTimingIfSync();
+        }
+    }
+
+    @Listener(order = Order.FIRST, beforeModifications = true)
+    public void onPlayerInteractItem(InteractItemEvent event, @Root Player player) {
+        if (event instanceof InteractItemEvent.Primary) {
+            lastInteractItemPrimaryTick = Sponge.getServer().getRunningTimeTicks();
+        } else {
+            lastInteractItemSecondaryTick = Sponge.getServer().getRunningTimeTicks();
+        }
+
+        final World world = player.getWorld();
+        final HandInteractEvent handEvent = (HandInteractEvent) event;
+        final ItemStack itemInHand = player.getItemInHand(handEvent.getHandType()).orElse(ItemStack.empty());
+
+        handleItemInteract(event, player, world, itemInHand);
+    }
+
+    // when a player picks up an item...
+    @Listener(order = Order.LAST, beforeModifications = true)
+    public void onPlayerPickupItem(ChangeInventoryEvent.Pickup.Pre event, @Root Player player) {
+        if (!GDFlags.ITEM_PICKUP || !GriefDefenderPlugin.getInstance().claimsEnabledForWorld(player.getWorld().getUniqueId())) {
+            return;
+        }
+        if (GriefDefenderPlugin.isTargetIdBlacklisted(Flags.ITEM_PICKUP.getName(), event.getTargetEntity(), player.getWorld().getProperties())) {
+            return;
+        }
+
+        GDTimings.PLAYER_PICKUP_ITEM_EVENT.startTimingIfSync();
+        final World world = player.getWorld();
+        GDPlayerData playerData = this.dataStore.getOrCreatePlayerData(world, player.getUniqueId());
+        Location<World> location = player.getLocation();
+        GDClaim claim = this.dataStore.getClaimAtPlayer(playerData, location);
+        if (GDPermissionManager.getInstance().getFinalPermission(event, location, claim, GDPermissions.ITEM_PICKUP, player, event.getTargetEntity(), player, true) == Tristate.FALSE) {
+            event.setCancelled(true);
+        }
+
+        GDTimings.PLAYER_PICKUP_ITEM_EVENT.stopTimingIfSync();
+    }
+
+    // when a player switches in-hand items
+    @Listener
+    public void onPlayerChangeHeldItem(ChangeInventoryEvent.Held event, @First Player player) {
+        if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(player.getWorld().getUniqueId())) {
+            return;
+        }
+
+        GDTimings.PLAYER_CHANGE_HELD_ITEM_EVENT.startTimingIfSync();
+        GDPlayerData playerData = this.dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+
+        int count = 0;
+        // if he's switching to the golden shovel
+        for (SlotTransaction transaction : event.getTransactions()) {
+            ItemStackSnapshot newItemStack = transaction.getFinal();
+            if (count == 1 && newItemStack != null && newItemStack.getType().equals(GriefDefenderPlugin.getInstance().modificationTool.getType())) {
+                playerData.lastShovelLocation = null;
+                playerData.endShovelLocation = null;
+                playerData.claimResizing = null;
+                // always reset to basic claims mode
+                if (playerData.shovelMode != ShovelTypes.BASIC) {
+                    playerData.shovelMode = ShovelTypes.BASIC;
+                    GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().MODE_BASIC);
+                }
+
+                // tell him how many claim blocks he has available
+                if (GriefDefenderPlugin.CLAIM_BLOCK_SYSTEM == ClaimBlockSystem.VOLUME) {
+                    final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.PLAYER_REMAINING_BLOCKS_3D,
+                            ImmutableMap.of(
+                            "block-amount", playerData.getRemainingClaimBlocks(),
+                            "chunk-amount", playerData.getRemainingChunks()));
+                    GriefDefenderPlugin.sendMessage(player, message);
+                } else {
+                    final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.PLAYER_REMAINING_BLOCKS_2D,
+                            ImmutableMap.of(
+                             "block-amount", playerData.getRemainingClaimBlocks()));
+                    GriefDefenderPlugin.sendMessage(player, message);
+                }
+
+            }
+            count++;
+        }
+        GDTimings.PLAYER_CHANGE_HELD_ITEM_EVENT.stopTimingIfSync();
+    }
+
+    @Listener(order = Order.FIRST, beforeModifications = true)
+    public void onPlayerUseItem(UseItemStackEvent.Start event, @First Player player) {
+        if (!GDFlags.ITEM_USE || !GriefDefenderPlugin.getInstance().claimsEnabledForWorld(player.getWorld().getUniqueId())) {
+            return;
+        }
+        if (GriefDefenderPlugin.isTargetIdBlacklisted(Flags.ITEM_USE.getName(), event.getItemStackInUse().getType(), player.getWorld().getProperties())) {
+            return;
+        }
+
+        GDTimings.PLAYER_USE_ITEM_EVENT.startTimingIfSync();
+        Location<World> location = player.getLocation();
+        GDPlayerData playerData = this.dataStore.getOrCreatePlayerData(location.getExtent(), player.getUniqueId());
+        GDClaim claim = this.dataStore.getClaimAtPlayer(playerData, location);
+
+        final Tristate result = GDPermissionManager.getInstance().getFinalPermission(event, location, claim, GDPermissions.ITEM_USE, player, event.getItemStackInUse().getType(), player, TrustTypes.ACCESSOR, true);
+        if (result == Tristate.FALSE) {
+            final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.PERMISSION_ITEM_USE,
+                    ImmutableMap.of(
+                    "item", event.getItemStackInUse().getType().getId()));
+            GriefDefenderPlugin.sendClaimDenyMessage(claim, player,  message);
+            event.setCancelled(true);
+        }
+        GDTimings.PLAYER_USE_ITEM_EVENT.stopTimingIfSync();
+    }
+
+    @Listener
+    public void onInteractBlock(InteractBlockEvent event) {
+    }
+
+    @Listener(order = Order.FIRST, beforeModifications = true)
+    public void onPlayerInteractBlockPrimary(InteractBlockEvent.Primary.MainHand event, @First Player player) {
+        final GDPlayerData playerData = this.dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        final HandType handType = event.getHandType();
+        final ItemStack itemInHand = player.getItemInHand(handType).orElse(ItemStack.empty());
+        if (event.getTargetBlock() != BlockSnapshot.NONE) {
+            // Run our item hook since Sponge no longer fires InteractItemEvent when targetting a non-air block
+            if (handleItemInteract(event, player, player.getWorld(), itemInHand).isCancelled()) {
+                return;
+            }
+        } else if (playerData.claimMode) {
+            if (investigateClaim(event, player, event.getTargetBlock(), itemInHand)) {
+                event.setCancelled(true);
+            }
+        }
+
+        if (playerData.claimMode) {
+            return;
+        }
+        // check give pet
+        if (playerData.petRecipientUniqueId != null) {
+            playerData.petRecipientUniqueId = null;
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().COMMAND_PET_TRANSFER_CANCEL);
+            event.setCancelled(true);
+            return;
+        }
+        if (!GDFlags.INTERACT_BLOCK_PRIMARY || !GriefDefenderPlugin.getInstance().claimsEnabledForWorld(player.getWorld().getUniqueId())) {
+            return;
+        }
+        if (GriefDefenderPlugin.isTargetIdBlacklisted(Flags.INTERACT_BLOCK_PRIMARY.getName(), event.getTargetBlock().getState(), player.getWorld().getProperties())) {
+            return;
+        }
+
+        GDTimings.PLAYER_INTERACT_BLOCK_PRIMARY_EVENT.startTimingIfSync();
+        final BlockSnapshot clickedBlock = event.getTargetBlock();
+        final Location<World> location = clickedBlock.getLocation().orElse(null);
+        final Object source = player;
+        if (location == null) {
+            GDTimings.PLAYER_INTERACT_BLOCK_PRIMARY_EVENT.stopTimingIfSync();
+            return;
+        }
+
+        final GDClaim claim = this.dataStore.getClaimAt(location);
+        final Tristate result = GDPermissionManager.getInstance().getFinalPermission(event, location, claim, GDPermissions.INTERACT_BLOCK_PRIMARY, source, clickedBlock.getState(), player, TrustTypes.BUILDER, true);
+        if (result == Tristate.FALSE) {
+            if (GriefDefenderPlugin.isTargetIdBlacklisted(Flags.BLOCK_BREAK.getName(), clickedBlock.getState(), player.getWorld().getProperties())) {
+                GDTimings.PLAYER_INTERACT_BLOCK_PRIMARY_EVENT.stopTimingIfSync();
+                return;
+            }
+            if (GDPermissionManager.getInstance().getFinalPermission(event, location, claim, GDPermissions.BLOCK_BREAK, source, clickedBlock.getState(), player, TrustTypes.BUILDER, true) == Tristate.TRUE) {
+                GDTimings.PLAYER_INTERACT_BLOCK_PRIMARY_EVENT.stopTimingIfSync();
+                return;
+            }
+
+            // Don't send a deny message if the player is holding an investigation tool
+            if (Sponge.getServer().getRunningTimeTicks() != lastInteractItemPrimaryTick || lastInteractItemCancelled != true) {
+                if (!PlayerUtil.getInstance().hasItemInOneHand(player, GriefDefenderPlugin.getInstance().investigationTool.getType())) {
+                    this.sendInteractBlockDenyMessage(itemInHand, clickedBlock, claim, player, playerData, handType);
+                }
+            }
+            event.setCancelled(true);
+            GDTimings.PLAYER_INTERACT_BLOCK_PRIMARY_EVENT.stopTimingIfSync();
+            return;
+        }
+        GDTimings.PLAYER_INTERACT_BLOCK_PRIMARY_EVENT.stopTimingIfSync();
+    }
+
+    @Listener(order = Order.FIRST, beforeModifications = true)
+    public void onPlayerInteractBlockSecondary(InteractBlockEvent.Secondary event, @First Player player) {
+        // Run our item hook since Sponge no longer fires InteractItemEvent when targetting a non-air block
+        final HandType handType = event.getHandType();
+        final ItemStack itemInHand = player.getItemInHand(handType).orElse(ItemStack.empty());
+        if (handleItemInteract(event, player, player.getWorld(), itemInHand).isCancelled()) {
+            event.setCancelled(true);
+            return;
+        }
+
+        if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(player.getWorld().getUniqueId())) {
+            return;
+        }
+        if (GriefDefenderPlugin.isTargetIdBlacklisted(Flags.INTERACT_BLOCK_SECONDARY.getName(), event.getTargetBlock().getState(), player.getWorld().getProperties())) {
+            return;
+        }
+
+        GDTimings.PLAYER_INTERACT_BLOCK_SECONDARY_EVENT.startTimingIfSync();
+        final BlockSnapshot clickedBlock = event.getTargetBlock();
+        final Object source = player;
+        // Check if item is banned
+        final GDPlayerData playerData = this.dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        final Location<World> location = clickedBlock.getLocation().orElse(null);
+
+        final GDClaim claim = this.dataStore.getClaimAt(location);
+        //GriefDefender.getPermissionManager().getFinalPermission(claim, Flags.ENTITY_SPAWN, source, target, user)
+        final TileEntity tileEntity = clickedBlock.getLocation().get().getTileEntity().orElse(null);
+        final TrustType trustType = (tileEntity != null && NMSUtil.getInstance().containsInventory(tileEntity)) ? TrustTypes.CONTAINER : TrustTypes.ACCESSOR;
+        if (GDFlags.INTERACT_BLOCK_SECONDARY && playerData != null) {
+            Tristate result = GDPermissionManager.getInstance().getFinalPermission(event, location, claim, GDPermissions.INTERACT_BLOCK_SECONDARY, source, event.getTargetBlock(), player, trustType, true);
+            if (result == Tristate.FALSE) {
+                // if player is holding an item, check if it can be placed
+                if (GDFlags.BLOCK_PLACE && !itemInHand.isEmpty() && NMSUtil.getInstance().isItemBlock(itemInHand)) {
+                    if (GriefDefenderPlugin.isTargetIdBlacklisted(Flags.BLOCK_PLACE.getName(), itemInHand, player.getWorld().getProperties())) {
+                        GDTimings.PLAYER_INTERACT_BLOCK_SECONDARY_EVENT.stopTimingIfSync();
+                        return;
+                    }
+                    if (GDPermissionManager.getInstance().getFinalPermission(event, location, claim, GDPermissions.BLOCK_PLACE, source, itemInHand, player, TrustTypes.BUILDER, true) == Tristate.TRUE) {
+                        GDTimings.PLAYER_INTERACT_BLOCK_SECONDARY_EVENT.stopTimingIfSync();
+                        return;
+                    }
+                }
+                // Don't send a deny message if the player is holding an investigation tool
+                if (Sponge.getServer().getRunningTimeTicks() != lastInteractItemSecondaryTick || lastInteractItemCancelled != true) {
+                    if (!PlayerUtil.getInstance().hasItemInOneHand(player, GriefDefenderPlugin.getInstance().investigationTool.getType())) {
+                        this.sendInteractBlockDenyMessage(itemInHand, clickedBlock, claim, player, playerData, handType);
+                    }
+                }
+                if (handType == HandTypes.MAIN_HAND) {
+                    NMSUtil.getInstance().closePlayerScreen(player);
+                }
+
+                event.setCancelled(true);
+                GDTimings.PLAYER_INTERACT_BLOCK_SECONDARY_EVENT.stopTimingIfSync();
+                return;
+            }
+        }
+
+        GDTimings.PLAYER_INTERACT_BLOCK_SECONDARY_EVENT.stopTimingIfSync();
+    }
+
+    public InteractEvent handleItemInteract(InteractEvent event, Player player, World world, ItemStack itemInHand) {
+        final ItemType itemType = itemInHand.getType();
+        final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        if (!playerData.claimMode && (itemInHand.isEmpty() || NMSUtil.getInstance().isItemFood(itemType))) {
+            return event;
+        }
+
+        final boolean primaryEvent = event instanceof InteractItemEvent.Primary || event instanceof InteractBlockEvent.Primary;
+        if (!GDFlags.INTERACT_ITEM_PRIMARY && primaryEvent || !GDFlags.INTERACT_ITEM_SECONDARY && !primaryEvent || !GriefDefenderPlugin.getInstance().claimsEnabledForWorld(world.getUniqueId())) {
+            return event;
+        }
+
+        if (primaryEvent && GriefDefenderPlugin.isTargetIdBlacklisted(Flags.INTERACT_ITEM_PRIMARY.toString(), itemInHand.getType(), world.getProperties())) {
+            return event;
+        }
+        if (!primaryEvent && GriefDefenderPlugin.isTargetIdBlacklisted(Flags.INTERACT_ITEM_SECONDARY.toString(), itemInHand.getType(), world.getProperties())) {
+            return event;
+        }
+
+        final Cause cause = event.getCause();
+        final EventContext context = cause.getContext();
+        final BlockSnapshot blockSnapshot = context.get(EventContextKeys.BLOCK_HIT).orElse(BlockSnapshot.NONE);
+        final Vector3d interactPoint = event.getInteractionPoint().orElse(null);
+        final Entity entity = context.get(EventContextKeys.ENTITY_HIT).orElse(null);
+        final Location<World> location = entity != null ? entity.getLocation() 
+                : blockSnapshot != BlockSnapshot.NONE ? blockSnapshot.getLocation().get() 
+                        : interactPoint != null ? new Location<World>(world, interactPoint) 
+                                : player.getLocation();
+        final GDClaim claim = this.dataStore.getClaimAt(location);
+
+        final String ITEM_PERMISSION = primaryEvent ? GDPermissions.INTERACT_ITEM_PRIMARY : GDPermissions.INTERACT_ITEM_SECONDARY;
+
+        if (playerData.claimMode || (!itemInHand.isEmpty() && (itemInHand.getType().equals(GriefDefenderPlugin.getInstance().modificationTool.getType()) ||
+                itemInHand.getType().equals(GriefDefenderPlugin.getInstance().investigationTool.getType())))) {
+            GDPermissionManager.getInstance().addEventLogEntry(event, location, itemInHand, blockSnapshot == null ? entity : blockSnapshot, player, ITEM_PERMISSION, null, Tristate.TRUE);
+            event.setCancelled(true);
+            if (investigateClaim(event, player, blockSnapshot, itemInHand)) {
+                return event;
+            }
+            if (!primaryEvent) {
+                onPlayerHandleClaimCreateAction(event, blockSnapshot, player, itemInHand, playerData);
+            }
+            return event;
+        }
+
+        if (GDPermissionManager.getInstance().getFinalPermission(event, location, claim, ITEM_PERMISSION, player, itemType, player, TrustTypes.ACCESSOR, true) == Tristate.FALSE) {
+            Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.PERMISSION_INTERACT_ITEM,
+                    ImmutableMap.of(
+                    "player", claim.getOwnerName(),
+                    "item", itemInHand.getType().getId()));
+            GriefDefenderPlugin.sendClaimDenyMessage(claim, player, message);
+            if (event instanceof InteractBlockEvent.Secondary) {
+                ((InteractBlockEvent.Secondary) event).setUseItemResult(SpongeUtil.getSpongeTristate(Tristate.FALSE));
+            } else {
+                event.setCancelled(true);
+            }
+            lastInteractItemCancelled = true;
+        }
+        return event;
+    }
+
+    private void onPlayerHandleClaimCreateAction(InteractEvent event, BlockSnapshot targetBlock, Player player, ItemStack itemInHand, GDPlayerData playerData) {
+        if (player.get(Keys.IS_SNEAKING).get() && (event instanceof InteractBlockEvent.Secondary || event instanceof InteractItemEvent.Secondary)) {
+            playerData.revertActiveVisual(player);
+            // check for any active WECUI visuals
+            if (this.worldEditProvider != null) {
+                this.worldEditProvider.revertVisuals(player, playerData, null);
+            }
+            playerData.lastShovelLocation = null;
+            playerData.endShovelLocation = null;
+            playerData.claimResizing = null;
+            playerData.shovelMode = ShovelTypes.BASIC;
+            return;
+        }
+
+        /*if (!playerData.claimMode) {
+            GriefDefenderConfig<?> activeConfig = GriefDefenderPlugin.getActiveConfig(player.getWorld().getProperties());
+            ItemType materialInHand = player.getItemInHand(handType).get().getType();
+            if (!materialInHand.getId().equals(activeConfig.getConfig().claim.modificationTool)) {
+                return;
+            }
+        }*/
+
+        GDTimings.PLAYER_HANDLE_SHOVEL_ACTION.startTimingIfSync();
+        BlockSnapshot clickedBlock = targetBlock;
+        Location<World> location = clickedBlock.getLocation().orElse(null);
+
+        if (clickedBlock.getState().getType() == BlockTypes.AIR) {
+            boolean ignoreAir = false;
+            if (this.worldEditProvider != null) {
+                // Ignore air so players can use client-side WECUI block target which uses max reach distance
+                if (this.worldEditProvider.hasCUISupport(player) && playerData.getClaimCreateMode() == CreateModeTypes.VOLUME && playerData.lastShovelLocation != null) {
+                    ignoreAir = true;
+                }
+            }
+            final int distance = !ignoreAir ? 100 : NMSUtil.getInstance().getPlayerBlockReachDistance(player);
+            location = BlockUtil.getInstance().getTargetBlock(player, playerData, distance, ignoreAir).orElse(null);
+            if (location == null) {
+                GDTimings.PLAYER_HANDLE_SHOVEL_ACTION.stopTiming();
+                return;
+            }
+        }
+
+        if (location == null) {
+            GDTimings.PLAYER_HANDLE_SHOVEL_ACTION.stopTimingIfSync();
+            return;
+        }
+
+        event.setCancelled(true);
+        if (playerData.shovelMode == ShovelTypes.RESTORE) {
+            if (true) {
+                GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().FEATURE_NOT_AVAILABLE);
+                GDTimings.PLAYER_HANDLE_SHOVEL_ACTION.stopTiming();
+                return;
+            }
+            final GDClaim claim = this.dataStore.getClaimAtPlayer(location, playerData, true);
+            if (!claim.isUserTrusted(player, TrustTypes.MANAGER)) {
+                final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.BLOCK_CLAIMED,
+                        ImmutableMap.of(
+                        "player", claim.getOwnerName()));
+                GriefDefenderPlugin.sendMessage(player, message);
+                ClaimVisual claimVisual = new ClaimVisual(claim, ClaimVisual.ERROR);
+                claimVisual.createClaimBlockVisuals(location.getBlockY(), player.getLocation(), playerData);
+                claimVisual.apply(player);
+                GDTimings.PLAYER_HANDLE_SHOVEL_ACTION.stopTiming();
+                return;
+            }
+
+            Chunk chunk = player.getWorld().getChunk(location.getBlockX() >> 4, 0, location.getBlockZ() >> 4).get();
+            int miny = location.getBlockY();
+            World world = chunk.getWorld();
+            final Chunk newChunk = world.regenerateChunk(chunk.getPosition().getX(), 0, chunk.getPosition().getZ()).orElse(null);
+            GDTimings.PLAYER_HANDLE_SHOVEL_ACTION.stopTiming();
+            return;
+        }
+
+        if (!playerData.canCreateClaim(player, true)) {
+            GDTimings.PLAYER_HANDLE_SHOVEL_ACTION.stopTiming();
+            return;
+        }
+
+        if (playerData.claimResizing != null) {
+            handleResizeFinish(event, player, location, playerData);
+            GDTimings.PLAYER_HANDLE_SHOVEL_ACTION.stopTiming();
+            return;
+        }
+
+        GDClaim claim = this.dataStore.getClaimAtPlayer(location, playerData, true);
+        if (!claim.isWilderness()) {
+            Component noEditReason = claim.allowEdit(player);
+            if (noEditReason != null) {
+                final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.CREATE_OVERLAP_PLAYER,
+                        ImmutableMap.of(
+                        "player", claim.getOwnerName()));
+                GriefDefenderPlugin.sendMessage(player, message);
+                ClaimVisual visualization = new ClaimVisual(claim, ClaimVisual.ERROR);
+                visualization.createClaimBlockVisuals(location.getBlockY(), player.getLocation(), playerData);
+                visualization.apply(player);
+                Set<Claim> claims = new HashSet<>();
+                claims.add(claim);
+                CommandHelper.showClaims(player, claims, location.getBlockY(), true);
+            } else if (playerData.lastShovelLocation == null && BlockUtil.getInstance().clickedClaimCorner(claim, location.getBlockPosition())) {
+                handleResizeStart(event, player, location, playerData, claim);
+            } else if ((playerData.shovelMode == ShovelTypes.SUBDIVISION 
+                    || ((claim.isTown() || claim.isAdminClaim()) && (playerData.lastShovelLocation == null || playerData.claimSubdividing != null)) && playerData.shovelMode != ShovelTypes.TOWN)) {
+                if (claim.getTownClaim() != null && playerData.shovelMode == ShovelTypes.TOWN) {
+                    GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().CREATE_OVERLAP_SHORT);
+                    Set<Claim> claims = new HashSet<>();
+                    claims.add(claim);
+                    CommandHelper.showClaims(player, claims, location.getBlockY(), true);
+                } else if (playerData.lastShovelLocation == null) {
+                    createSubdivisionStart(event, player, itemInHand, location, playerData, claim);
+                } else if (playerData.claimSubdividing != null) {
+                    createSubdivisionFinish(event, player, location, playerData, claim);
+                }
+            } else {
+                GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().CREATE_OVERLAP);
+                Set<Claim> claims = new HashSet<>();
+                claims.add(claim);
+                CommandHelper.showClaims(player, claims, location.getBlockY(), true);
+            }
+            GDTimings.PLAYER_HANDLE_SHOVEL_ACTION.stopTiming();
+            return;
+        } else if (playerData.shovelMode == ShovelTypes.SUBDIVISION && playerData.lastShovelLocation != null) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().CREATE_SUBDIVISION_FAIL);
+            playerData.lastShovelLocation = null;
+            GDTimings.PLAYER_HANDLE_SHOVEL_ACTION.stopTiming();
+            return;
+        }
+
+        Location<World> lastShovelLocation = playerData.lastShovelLocation;
+        if (lastShovelLocation == null) {
+            createClaimStart(event, player, itemInHand, location, playerData, claim);
+            GDTimings.PLAYER_HANDLE_SHOVEL_ACTION.stopTiming();
+            return;
+        }
+
+        createClaimFinish(event, player, location, playerData, claim);
+        GDTimings.PLAYER_HANDLE_SHOVEL_ACTION.stopTiming();
+    }
+
+    private void createClaimStart(InteractEvent event, Player player, ItemStack itemInHand, Location<World> location, GDPlayerData playerData, GDClaim claim) {
+        if (!player.hasPermission(GDPermissions.BYPASS_CLAIM_LIMIT)) {
+            int createClaimLimit = -1;
+            if (playerData.shovelMode == ShovelTypes.BASIC && (claim.isAdminClaim() || claim.isTown() || claim.isWilderness())) {
+                createClaimLimit = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), player, Options.CREATE_LIMIT, claim).intValue();
+            } else if (playerData.shovelMode == ShovelTypes.TOWN && (claim.isAdminClaim() || claim.isWilderness())) {
+                createClaimLimit = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), player, Options.CREATE_LIMIT, claim).intValue();
+            } else if (playerData.shovelMode == ShovelTypes.SUBDIVISION && !claim.isWilderness()) {
+                createClaimLimit = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), player, Options.CREATE_LIMIT, claim).intValue();
+            }
+
+            if (createClaimLimit > 0 && createClaimLimit < (playerData.getInternalClaims().size() + 1)) {
+                GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.CREATE_FAILED_CLAIM_LIMIT));
+                return;
+            }
+        }
+
+        final int minClaimLevel = playerData.getMinClaimLevel();
+        if (playerData.shovelMode != ShovelTypes.ADMIN && location.getBlockY() < minClaimLevel) {
+            final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.CLAIM_BELOW_LEVEL,
+                    ImmutableMap.of(
+                    "limit", minClaimLevel));
+            GriefDefenderPlugin.sendMessage(player, message);
+            return;
+        }
+        final int maxClaimLevel = playerData.getMaxClaimLevel();
+        if (playerData.shovelMode != ShovelTypes.ADMIN && location.getBlockY() > maxClaimLevel) {
+            final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.CLAIM_ABOVE_LEVEL,
+                    ImmutableMap.of(
+                    "limit", maxClaimLevel));
+            GriefDefenderPlugin.sendMessage(player, message);
+            return;
+        }
+
+        if (playerData.shovelMode == ShovelTypes.SUBDIVISION && claim.isWilderness()) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().CREATE_SUBDIVISION_FAIL);
+            return;
+        }
+
+        final ClaimType type = PlayerUtil.getInstance().getClaimTypeFromShovel(playerData.shovelMode);
+        if ((type == ClaimTypes.BASIC || type == ClaimTypes.TOWN) && GriefDefenderPlugin.getGlobalConfig().getConfig().economy.economyMode) {
+            // Check current economy mode cost
+            final Double economyBlockCost = playerData.getInternalEconomyBlockCost();
+            if (economyBlockCost == null || economyBlockCost <= 0) {
+                GriefDefenderPlugin.sendMessage(player, TextComponent.builder().color(TextColor.RED)
+                        .append("Economy mode is enabled but the current cost for blocks is ")
+                        .append("0", TextColor.GOLD)
+                        .append("\nRaise the value for option 'economy-block-cost' in config or via '")
+                        .append("/gd option claim", TextColor.WHITE)
+                        .append("' command.", TextColor.RED)
+                        .build());
+                return;
+            }
+        }
+
+        playerData.lastShovelLocation = location;
+        Component message = null;
+        if (playerData.claimMode) {
+            message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.CLAIM_MODE_START,
+                    ImmutableMap.of(
+                    "type", PlayerUtil.getInstance().getClaimTypeComponentFromShovel(playerData.shovelMode)));
+        } else {
+            message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.CLAIM_START,
+                    ImmutableMap.of(
+                    "type", PlayerUtil.getInstance().getClaimTypeComponentFromShovel(playerData.shovelMode),
+                    "item", itemInHand.getType().getId()));
+        }
+        GriefDefenderPlugin.sendMessage(player, message);
+        ClaimVisual visual = ClaimVisual.fromClick(location, location.getBlockY(), PlayerUtil.getInstance().getVisualTypeFromShovel(playerData.shovelMode), player, playerData);
+        visual.apply(player, false);
+    }
+
+    private void createClaimFinish(InteractEvent event, Player player, Location<World> location, GDPlayerData playerData, GDClaim claim) {
+        Location<World> lastShovelLocation = playerData.lastShovelLocation;
+        final GDClaim firstClaim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(playerData.lastShovelLocation, playerData, true);
+        final GDClaim clickedClaim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(location, playerData, true);
+        if (!firstClaim.equals(clickedClaim)) {
+            final GDClaim overlapClaim = firstClaim.isWilderness() ? clickedClaim : firstClaim;
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().CREATE_OVERLAP_SHORT);
+            Set<Claim> claims = new HashSet<>();
+            claims.add(overlapClaim);
+            CommandHelper.showClaims(player, claims, location.getBlockY(), true);
+            return;
+        }
+
+        final boolean cuboid = playerData.getClaimCreateMode() == CreateModeTypes.VOLUME;
+        Vector3i lesserBoundaryCorner = new Vector3i(
+                lastShovelLocation.getBlockX(),
+                cuboid ? lastShovelLocation.getBlockY() : playerData.getMinClaimLevel(),
+                lastShovelLocation.getBlockZ());
+        Vector3i greaterBoundaryCorner = new Vector3i(
+                location.getBlockX(),
+                cuboid ? location.getBlockY() : playerData.getMaxClaimLevel(),
+                location.getBlockZ());
+
+        final ClaimType type = PlayerUtil.getInstance().getClaimTypeFromShovel(playerData.shovelMode);
+        if ((type == ClaimTypes.BASIC || type == ClaimTypes.TOWN) && GriefDefenderPlugin.getGlobalConfig().getConfig().economy.economyMode) {
+            EconomyUtil.getInstance().economyCreateClaimConfirmation(player, playerData, location.getBlockY(), lesserBoundaryCorner, greaterBoundaryCorner, PlayerUtil.getInstance().getClaimTypeFromShovel(playerData.shovelMode),
+                    cuboid, playerData.claimSubdividing);
+            return;
+        }
+
+        GDCauseStackManager.getInstance().pushCause(player);
+        ClaimResult result = this.dataStore.createClaim(
+                player.getWorld(),
+                lesserBoundaryCorner,
+                greaterBoundaryCorner,
+                type, player.getUniqueId(), cuboid);
+        GDCauseStackManager.getInstance().popCause();
+        GDClaim gdClaim = (GDClaim) result.getClaim().orElse(null);
+        if (!result.successful()) {
+            if (result.getResultType() == ClaimResultType.OVERLAPPING_CLAIM) {
+                GDClaim overlapClaim = (GDClaim) result.getClaim().get();
+                GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().CREATE_OVERLAP_SHORT);
+                Set<Claim> claims = new HashSet<>();
+                claims.add(overlapClaim);
+                CommandHelper.showOverlapClaims(player, claims, location.getBlockY());
+            } else if (result.getResultType() == ClaimResultType.CLAIM_EVENT_CANCELLED) {
+                GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().CREATE_CANCEL);
+            } else {
+                GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.CREATE_FAILED_RESULT,
+                        ImmutableMap.of("reason", result.getResultType())));
+            }
+            return;
+        } else {
+            playerData.lastShovelLocation = null;
+            final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.CREATE_SUCCESS,
+                    ImmutableMap.of(
+                    "type", gdClaim.getFriendlyNameType(true)));
+            GriefDefenderPlugin.sendMessage(player, message);
+            final WorldEditProvider worldEditProvider = GriefDefenderPlugin.getInstance().worldEditProvider;
+            if (worldEditProvider != null) {
+                worldEditProvider.stopVisualDrag(player);
+                worldEditProvider.visualizeClaim(gdClaim, player, playerData, false);
+            }
+            gdClaim.getVisualizer().createClaimBlockVisuals(location.getBlockY(), player.getLocation(), playerData);
+            gdClaim.getVisualizer().apply(player, false);
+        }
+    }
+
+    private void createSubdivisionStart(InteractEvent event, Player player, ItemStack itemInHand, Location<World> location, GDPlayerData playerData, GDClaim claim) {
+        final int minClaimLevel = playerData.getMinClaimLevel();
+        if (playerData.shovelMode != ShovelTypes.ADMIN && location.getBlockY() < minClaimLevel) {
+            final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.CLAIM_BELOW_LEVEL,
+                    ImmutableMap.of(
+                    "limit", minClaimLevel));
+            GriefDefenderPlugin.sendMessage(player, message);
+            return;
+        }
+        final int maxClaimLevel = playerData.getMaxClaimLevel();
+        if (playerData.shovelMode != ShovelTypes.ADMIN && location.getBlockY() > maxClaimLevel) {
+            final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.CLAIM_ABOVE_LEVEL,
+                    ImmutableMap.of(
+                    "limit", maxClaimLevel));
+            GriefDefenderPlugin.sendMessage(player, message);
+            return;
+        }
+
+        if (claim.isSubdivision()) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().RESIZE_OVERLAP_SUBDIVISION);
+        } else {
+            Component message = null;
+            if (playerData.claimMode) {
+                message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.CLAIM_START,
+                        ImmutableMap.of(
+                        "type", playerData.shovelMode.getName()));
+            } else {
+                message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.CLAIM_START,
+                        ImmutableMap.of(
+                        "type", playerData.shovelMode.getName(),
+                        "item", itemInHand.getType().getId()));
+            }
+            GriefDefenderPlugin.sendMessage(player, message);
+            playerData.lastShovelLocation = location;
+            playerData.claimSubdividing = claim;
+            ClaimVisual visualization = ClaimVisual.fromClick(location, location.getBlockY(), PlayerUtil.getInstance().getVisualTypeFromShovel(playerData.shovelMode), player, playerData);
+            visualization.apply(player, false);
+        }
+    }
+
+    private void createSubdivisionFinish(InteractEvent event, Player player, Location<World> location, GDPlayerData playerData, GDClaim claim) {
+        final GDClaim clickedClaim = GriefDefenderPlugin.getInstance().dataStore.getClaimAt(location);
+        if (clickedClaim == null || !playerData.claimSubdividing.getUniqueId().equals(clickedClaim.getUniqueId())) {
+            if (clickedClaim != null) {
+                GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().CREATE_OVERLAP_SHORT);
+                final GDClaim overlapClaim = playerData.claimSubdividing;
+                Set<Claim> claims = new HashSet<>();
+                claims.add(overlapClaim);
+                CommandHelper.showClaims(player, claims, location.getBlockY(), true);
+            }
+
+            return;
+        }
+
+        Vector3i lesserBoundaryCorner = new Vector3i(playerData.lastShovelLocation.getBlockX(), 
+                playerData.getClaimCreateMode() == CreateModeTypes.VOLUME ? playerData.lastShovelLocation.getBlockY() : playerData.getMinClaimLevel(),
+                playerData.lastShovelLocation.getBlockZ());
+        Vector3i greaterBoundaryCorner = new Vector3i(location.getBlockX(), 
+                playerData.getClaimCreateMode() == CreateModeTypes.VOLUME ? location.getBlockY() : playerData.getMaxClaimLevel(),
+                        location.getBlockZ());
+
+        GDCauseStackManager.getInstance().pushCause(player);
+        ClaimResult result = this.dataStore.createClaim(player.getWorld(),
+                lesserBoundaryCorner, greaterBoundaryCorner, PlayerUtil.getInstance().getClaimTypeFromShovel(playerData.shovelMode),
+                player.getUniqueId(), playerData.getClaimCreateMode() == CreateModeTypes.VOLUME, playerData.claimSubdividing);
+        GDCauseStackManager.getInstance().popCause();
+        GDClaim gdClaim = (GDClaim) result.getClaim().orElse(null);
+        if (!result.successful()) {
+            if (result.getResultType() == ClaimResultType.OVERLAPPING_CLAIM) {
+                GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().CREATE_OVERLAP_SHORT);
+                Set<Claim> claims = new HashSet<>();
+                claims.add(gdClaim);
+                CommandHelper.showOverlapClaims(player, claims, location.getBlockY());
+            }
+            event.setCancelled(true);
+            return;
+        } else {
+            playerData.lastShovelLocation = null;
+            playerData.claimSubdividing = null;
+            final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.CREATE_SUCCESS, ImmutableMap.of(
+                    "type", gdClaim.getFriendlyNameType(true)));
+            GriefDefenderPlugin.sendMessage(player, message);
+            gdClaim.getVisualizer().createClaimBlockVisuals(location.getBlockY(), player.getLocation(), playerData);
+            gdClaim.getVisualizer().apply(player, false);
+            final WorldEditProvider worldEditProvider = GriefDefenderPlugin.getInstance().worldEditProvider;
+            if (worldEditProvider != null) {
+                worldEditProvider.stopVisualDrag(player);
+                worldEditProvider.visualizeClaim(gdClaim, player, playerData, false);
+            }
+        }
+    }
+
+    private void handleResizeStart(InteractEvent event, Player player, Location<World> location, GDPlayerData playerData, GDClaim claim) {
+        boolean playerCanResize = true;
+        if (!player.hasPermission(GDPermissions.CLAIM_RESIZE_ALL) 
+                && !playerData.canIgnoreClaim(claim) 
+                && !claim.isUserTrusted(player.getUniqueId(), TrustTypes.MANAGER)) {
+
+            if (claim.isAdminClaim()) {
+                if (!playerData.canManageAdminClaims) {
+                    playerCanResize = false;
+                }
+            } else if (!player.getUniqueId().equals(claim.getOwnerUniqueId())) {
+                playerCanResize = false;
+            }
+            if (!playerCanResize) {
+                if (claim.parent != null) {
+                    if (claim.parent.isAdminClaim() && claim.isSubdivision()) {
+                        playerCanResize = player.hasPermission(GDPermissions.CLAIM_RESIZE_ADMIN_SUBDIVISION);
+                    } else if (claim.parent.isBasicClaim() && claim.isSubdivision()) {
+                        playerCanResize = player.hasPermission(GDPermissions.CLAIM_RESIZE_BASIC_SUBDIVISION);
+                    } else if (claim.isTown()) {
+                        playerCanResize = player.hasPermission(GDPermissions.CLAIM_RESIZE_TOWN);
+                    } else if (claim.isAdminClaim()) {
+                        playerCanResize = player.hasPermission(GDPermissions.CLAIM_RESIZE_ADMIN);
+                    } else {
+                        playerCanResize = player.hasPermission(GDPermissions.CLAIM_RESIZE_BASIC);
+                    }
+                } else if (claim.isTown()) {
+                    playerCanResize = player.hasPermission(GDPermissions.CLAIM_RESIZE_TOWN);
+                } else if (claim.isAdminClaim()) {
+                    playerCanResize = player.hasPermission(GDPermissions.CLAIM_RESIZE_ADMIN);
+                } else {
+                    playerCanResize = player.hasPermission(GDPermissions.CLAIM_RESIZE_BASIC);
+                }
+            }
+        }
+
+        if (!claim.getInternalClaimData().isResizable() || !playerCanResize) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().PERMISSION_CLAIM_RESIZE);
+            return;
+        }
+
+        playerData.claimResizing = claim;
+        playerData.lastShovelLocation = location;
+        if (GriefDefenderPlugin.getInstance().worldEditProvider != null) {
+            // get opposite corner
+            final int x = playerData.lastShovelLocation.getBlockX() == claim.lesserBoundaryCorner.getX() ? claim.greaterBoundaryCorner.getX() : claim.lesserBoundaryCorner.getX();
+            final int y = playerData.lastShovelLocation.getBlockY() == claim.lesserBoundaryCorner.getY() ? claim.greaterBoundaryCorner.getY() : claim.lesserBoundaryCorner.getY();
+            final int z = playerData.lastShovelLocation.getBlockZ() == claim.lesserBoundaryCorner.getZ() ? claim.greaterBoundaryCorner.getZ() : claim.lesserBoundaryCorner.getZ();
+            GriefDefenderPlugin.getInstance().worldEditProvider.visualizeClaim(claim, new Vector3i(x, y, z), playerData.lastShovelLocation.getBlockPosition(), player, playerData, false);
+        }
+        // Show visual block for resize corner click
+        ClaimVisual visual = ClaimVisual.fromClick(location, location.getBlockY(), PlayerUtil.getInstance().getVisualTypeFromShovel(playerData.shovelMode), player, playerData);
+        visual.apply(player, false);
+        GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().RESIZE_START);
+    }
+
+    private void handleResizeFinish(InteractEvent event, Player player, Location<World> location, GDPlayerData playerData) {
+        if (location.equals(playerData.lastShovelLocation)) {
+            return;
+        }
+
+        playerData.endShovelLocation = location;
+        int newx1, newx2, newz1, newz2, newy1, newy2;
+        int smallX = 0, smallY = 0, smallZ = 0, bigX = 0, bigY = 0, bigZ = 0;
+
+        newx1 = playerData.lastShovelLocation.getBlockX();
+        newx2 = location.getBlockX();
+        newy1 = playerData.lastShovelLocation.getBlockY();
+        newy2 = location.getBlockY();
+        newz1 = playerData.lastShovelLocation.getBlockZ();
+        newz2 = location.getBlockZ();
+        Vector3i lesserBoundaryCorner = playerData.claimResizing.getLesserBoundaryCorner();
+        Vector3i greaterBoundaryCorner = playerData.claimResizing.getGreaterBoundaryCorner();
+        smallX = lesserBoundaryCorner.getX();
+        smallY = lesserBoundaryCorner.getY();
+        smallZ = lesserBoundaryCorner.getZ();
+        bigX = greaterBoundaryCorner.getX();
+        bigY = greaterBoundaryCorner.getY();
+        bigZ = greaterBoundaryCorner.getZ();
+
+        if (newx1 == smallX) {
+            smallX = newx2;
+        } else {
+            bigX = newx2;
+        }
+
+        if (newy1 == smallY) {
+            smallY = newy2;
+        } else {
+            bigY = newy2;
+        }
+
+        if (newz1 == smallZ) {
+            smallZ = newz2;
+        } else {
+            bigZ = newz2;
+        }
+
+        ClaimResult claimResult = null;
+        GDCauseStackManager.getInstance().pushCause(player);
+        claimResult = playerData.claimResizing.resize(smallX, bigX, smallY, bigY, smallZ, bigZ);
+        GDCauseStackManager.getInstance().popCause();
+        if (claimResult.successful()) {
+            Claim claim = (GDClaim) claimResult.getClaim().get();
+            int claimBlocksRemaining = playerData.getRemainingClaimBlocks();;
+            if (!playerData.claimResizing.isAdminClaim()) {
+                UUID ownerID = playerData.claimResizing.getOwnerUniqueId();
+                if (playerData.claimResizing.parent != null) {
+                    ownerID = playerData.claimResizing.parent.getOwnerUniqueId();
+                }
+
+                if (ownerID.equals(player.getUniqueId())) {
+                    claimBlocksRemaining = playerData.getRemainingClaimBlocks();
+                } else {
+                    GDPlayerData ownerData = this.dataStore.getOrCreatePlayerData(player.getWorld(), ownerID);
+                    claimBlocksRemaining = ownerData.getRemainingClaimBlocks();
+                    Optional<User> owner = Sponge.getGame().getServiceManager().provide(UserStorageService.class).get().get(ownerID);
+                    if (owner.isPresent() && !owner.get().isOnline()) {
+                        this.dataStore.clearCachedPlayerData(player.getWorld().getUniqueId(), ownerID);
+                    }
+                }
+            }
+
+            playerData.claimResizing = null;
+            playerData.lastShovelLocation = null;
+            playerData.endShovelLocation = null;
+            if (GriefDefenderPlugin.getInstance().isEconomyModeEnabled()) {
+                final Account playerAccount = GriefDefenderPlugin.getInstance().economyService.get().getOrCreateAccount(player.getUniqueId()).orElse(null);
+                if (playerAccount != null) {
+                    final Currency defaultCurrency = GriefDefenderPlugin.getInstance().economyService.get().getDefaultCurrency();
+                    final BigDecimal currentFunds = playerAccount.getBalance(defaultCurrency);
+                    if (GriefDefenderPlugin.CLAIM_BLOCK_SYSTEM == ClaimBlockSystem.VOLUME) {
+                        final double claimableChunks = claimBlocksRemaining / 65536.0;
+                        final Map<String, Object> params = ImmutableMap.of(
+                                "balance", String.valueOf("$" + currentFunds.intValue()),
+                                "chunk-amount", Math.round(claimableChunks * 100.0)/100.0, 
+                                "block-amount", claimBlocksRemaining);
+                        GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.ECONOMY_MODE_RESIZE_SUCCESS_3D, params));
+                    } else {
+                        final Map<String, Object> params = ImmutableMap.of(
+                                "balance", String.valueOf("$" + currentFunds.intValue()),
+                                "block-amount", claimBlocksRemaining);
+                        GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.ECONOMY_MODE_RESIZE_SUCCESS_2D, params));
+                    }
+                }
+            } else {
+                if (GriefDefenderPlugin.CLAIM_BLOCK_SYSTEM == ClaimBlockSystem.VOLUME) {
+                    final double claimableChunks = claimBlocksRemaining / 65536.0;
+                    final Map<String, Object> params = ImmutableMap.of(
+                            "chunk-amount", Math.round(claimableChunks * 100.0)/100.0, 
+                            "block-amount", claimBlocksRemaining);
+                    GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.RESIZE_SUCCESS_3D, params));
+                } else {
+                    final Map<String, Object> params = ImmutableMap.of(
+                            "block-amount", claimBlocksRemaining);
+                    GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.RESIZE_SUCCESS_2D, params));
+                }
+            }
+            playerData.revertActiveVisual(player);
+            ((GDClaim) claim).getVisualizer().resetVisuals();
+            ((GDClaim) claim).getVisualizer().createClaimBlockVisuals(location.getBlockY(), player.getLocation(), playerData);
+            ((GDClaim) claim).getVisualizer().apply(player);
+            if (this.worldEditProvider != null) {
+                this.worldEditProvider.visualizeClaim(claim, player, playerData, false);
+            }
+        } else {
+            if (claimResult.getResultType() == ClaimResultType.OVERLAPPING_CLAIM) {
+                GDClaim overlapClaim = (GDClaim) claimResult.getClaim().get();
+                GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().RESIZE_OVERLAP);
+                Set<Claim> claims = new HashSet<>();
+                claims.add(overlapClaim);
+                CommandHelper.showOverlapClaims(player, claims, location.getBlockY());
+            } else {
+                if (!claimResult.getMessage().isPresent()) {
+                    GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().CLAIM_NOT_YOURS);
+                }
+            }
+
+            playerData.claimSubdividing = null;
+            event.setCancelled(true);
+        }
+    }
+
+    private boolean investigateClaim(InteractEvent event, Player player, BlockSnapshot clickedBlock, ItemStack itemInHand) {
+        final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        if (playerData.claimMode && (event instanceof InteractItemEvent.Secondary || event instanceof InteractBlockEvent.Secondary)) {
+            if (player.get(Keys.IS_SNEAKING).get()) {
+                return true;
+            }
+            // claim mode inspects with left-click
+            return false;
+        }
+        if (!playerData.claimMode && (itemInHand.isEmpty() || itemInHand.getType() != GriefDefenderPlugin.getInstance().investigationTool.getType())) {
+            return false;
+        }
+
+        if (event instanceof InteractItemEvent.Primary || event instanceof InteractBlockEvent.Primary) {
+            if (!playerData.claimMode || !playerData.visualBlocks.isEmpty()) {
+                playerData.revertActiveVisual(player);
+                if (this.worldEditProvider != null) {
+                    this.worldEditProvider.revertVisuals(player, playerData, null);
+                }
+                GDTimings.PLAYER_INVESTIGATE_CLAIM.stopTiming();
+                return false;
+            }
+        }
+
+        GDTimings.PLAYER_INVESTIGATE_CLAIM.startTimingIfSync();
+        // if holding shift (sneaking), show all claims in area
+        GDClaim claim = null;
+        if (clickedBlock.getState().getType().equals(BlockTypes.AIR)) {
+            claim = this.findNearbyClaim(player);
+            // if holding shift (sneaking), show all claims in area
+            if (player.get(Keys.IS_SNEAKING).get()) {
+                if (!playerData.canIgnoreClaim(claim) && !player.hasPermission(GDPermissions.VISUALIZE_CLAIMS_NEARBY)) {
+                    GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().PERMISSION_VISUAL_CLAIMS_NEARBY);
+                    GDTimings.PLAYER_INVESTIGATE_CLAIM.stopTimingIfSync();
+                    return false;
+                }
+
+                Location<World> nearbyLocation = playerData.lastValidInspectLocation != null ? playerData.lastValidInspectLocation : player.getLocation();
+                Set<Claim> claims = BlockUtil.getInstance().getNearbyClaims(nearbyLocation);
+                int height = (int) (playerData.lastValidInspectLocation != null ? playerData.lastValidInspectLocation.getBlockY() : PlayerUtil.getInstance().getEyeHeight(player));
+                boolean hideBorders = this.worldEditProvider != null &&
+                                      this.worldEditProvider.hasCUISupport(player) &&
+                                      GriefDefenderPlugin.getActiveConfig(player.getWorld().getUniqueId()).getConfig().visual.hideBorders;
+                if (!hideBorders) {
+                    ClaimVisual visualization = ClaimVisual.fromClaims(claims, PlayerUtil.getInstance().getVisualClaimHeight(playerData, height), player.getLocation(), playerData, null);
+                    visualization.apply(player);
+                }
+
+                final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.CLAIM_SHOW_NEARBY,
+                        ImmutableMap.of(
+                        "amount", claims.size()));
+                GriefDefenderPlugin.sendMessage(player, message);
+                if (!claims.isEmpty()) {
+
+                    if (this.worldEditProvider != null) {
+                        worldEditProvider.revertVisuals(player, playerData, null);
+                        worldEditProvider.visualizeClaims(claims, player, playerData, true);
+                    }
+                    CommandHelper.showClaims(player, claims);
+                }
+                GDTimings.PLAYER_INVESTIGATE_CLAIM.stopTimingIfSync();
+                return true;
+            }
+            if (claim != null && claim.isWilderness()) {
+                playerData.lastValidInspectLocation = null;
+                GDTimings.PLAYER_INVESTIGATE_CLAIM.stopTimingIfSync();
+                return false;
+            }
+        } else {
+            claim = this.dataStore.getClaimAt(clickedBlock.getLocation().get());
+            if (claim.isWilderness()) {
+                GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.BLOCK_NOT_CLAIMED));
+                GDTimings.PLAYER_INVESTIGATE_CLAIM.stopTimingIfSync();
+                return false;
+            }
+        }
+
+        // visualize boundary
+        if (claim.getUniqueId() != playerData.visualClaimId) {
+            int height = playerData.lastValidInspectLocation != null ? playerData.lastValidInspectLocation.getBlockY() : clickedBlock.getLocation().get().getBlockY();
+            claim.getVisualizer().createClaimBlockVisuals(playerData.getClaimCreateMode() == CreateModeTypes.VOLUME ? height : PlayerUtil.getInstance().getEyeHeight(player), player.getLocation(), playerData);
+            claim.getVisualizer().apply(player);
+            if (this.worldEditProvider != null) {
+                worldEditProvider.visualizeClaim(claim, player, playerData, true);
+            }
+            Set<Claim> claims = new HashSet<>();
+            claims.add(claim);
+            CommandHelper.showClaims(player, claims);
+        }
+        final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.BLOCK_CLAIMED,
+                ImmutableMap.of(
+                "player", claim.getOwnerName()));
+        GriefDefenderPlugin.sendMessage(player, message);
+
+        GDTimings.PLAYER_INVESTIGATE_CLAIM.stopTimingIfSync();
+        return true;
+    }
+
+    private GDClaim findNearbyClaim(Player player) {
+        int maxDistance = GriefDefenderPlugin.getInstance().maxInspectionDistance;
+        BlockRay<World> blockRay = BlockRay.from(player).distanceLimit(maxDistance).build();
+        GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        GDClaim claim = null;
+        int count = 0;
+        while (blockRay.hasNext()) {
+            BlockRayHit<World> blockRayHit = blockRay.next();
+            Location<World> location = blockRayHit.getLocation();
+            claim = this.dataStore.getClaimAt(location);
+            if (claim != null && !claim.isWilderness() && (playerData.visualBlocks == null || (claim.getUniqueId() != playerData.visualClaimId))) {
+                playerData.lastValidInspectLocation = location;
+                return claim;
+            }
+
+            BlockType blockType = location.getBlockType();
+            if (blockType != BlockTypes.AIR && blockType != BlockTypes.TALLGRASS) {
+                break;
+            }
+            count++;
+        }
+
+        if (count == maxDistance) {
+            GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().CLAIM_TOO_FAR);
+        } else if (claim != null && claim.isWilderness()){
+            GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.BLOCK_NOT_CLAIMED));
+        }
+
+        return claim;
+    }
+
+    private void sendInteractEntityDenyMessage(ItemStack playerItem, Entity entity, GDClaim claim, Player player, HandType handType) {
+        if (entity instanceof Player || (claim.getData() != null && !claim.getData().allowDenyMessages())) {
+            return;
+        }
+
+        final String entityId = entity.getType() != null ? entity.getType().getId() : NMSUtil.getInstance().getEntityName(entity);
+        if (playerItem == null || playerItem == ItemTypes.NONE || playerItem.isEmpty()) {
+            final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.PERMISSION_INTERACT_ENTITY, ImmutableMap.of(
+                    "player", claim.getOwnerName(),
+                    "entity", entityId));
+            GriefDefenderPlugin.sendClaimDenyMessage(claim, player, message);
+        } else {
+            final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.PERMISSION_INTERACT_ITEM_ENTITY, ImmutableMap.of(
+                    "item", playerItem.getType().getId(),
+                    "entity", entityId));
+            GriefDefenderPlugin.sendClaimDenyMessage(claim, player, message);
+        }
+    }
+
+    private void sendInteractBlockDenyMessage(ItemStack playerItem, BlockSnapshot blockSnapshot, GDClaim claim, Player player, GDPlayerData playerData, HandType handType) {
+        if (claim.getData() != null && !claim.getData().allowDenyMessages()) {
+            return;
+        }
+
+        if (playerData != null && claim.getData() != null && claim.getData().isExpired() /*&& GriefDefenderPlugin.getActiveConfig(player.getWorld().getProperties()).getConfig().claim.bankTaxSystem*/) {
+            playerData.sendTaxExpireMessage(player, claim);
+        } else if (playerItem == null || playerItem == ItemTypes.NONE || playerItem.isEmpty()) {
+            final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.PERMISSION_INTERACT_BLOCK,
+                    ImmutableMap.of(
+                    "player", claim.getOwnerName(),
+                    "block", blockSnapshot.getState().getType().getId()));
+            GriefDefenderPlugin.sendClaimDenyMessage(claim, player, message);
+        } else {
+            final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.PERMISSION_INTERACT_ITEM_BLOCK,
+                    ImmutableMap.of(
+                    "item", playerItem.getType().getId(),
+                    "block", blockSnapshot.getState().getType().getId()));
+            GriefDefenderPlugin.sendClaimDenyMessage(claim, player, message);
+        }
+        if (handType == HandTypes.MAIN_HAND) {
+            NMSUtil.getInstance().closePlayerScreen(player);
+        }
+    }
+}
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/listener/WorldEventHandler.java b/sponge/src/main/java/com/griefdefender/listener/WorldEventHandler.java
new file mode 100644
index 0000000..af93661
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/listener/WorldEventHandler.java
@@ -0,0 +1,97 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.listener;
+
+import com.griefdefender.GDBootstrap;
+import com.griefdefender.GDTimings;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.claim.GDClaimManager;
+import com.griefdefender.internal.util.NMSUtil;
+import com.griefdefender.task.TaxApplyTask;
+import com.griefdefender.util.TaskUtil;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.event.Listener;
+import org.spongepowered.api.event.Order;
+import org.spongepowered.api.event.world.LoadWorldEvent;
+import org.spongepowered.api.event.world.SaveWorldEvent;
+import org.spongepowered.api.event.world.UnloadWorldEvent;
+import org.spongepowered.api.event.world.chunk.LoadChunkEvent;
+import org.spongepowered.api.event.world.chunk.UnloadChunkEvent;
+import org.spongepowered.common.SpongeImpl;
+
+import java.util.concurrent.TimeUnit;
+
+public class WorldEventHandler {
+
+    @Listener(order = Order.EARLY, beforeModifications = true)
+    public void onWorldLoad(LoadWorldEvent event) {
+        if (!SpongeImpl.getServer().isServerRunning() || !GriefDefenderPlugin.getInstance().claimsEnabledForWorld(event.getTargetWorld().getUniqueId())) {
+            return;
+        }
+
+        GDTimings.WORLD_LOAD_EVENT.startTimingIfSync();
+        GriefDefenderPlugin.getInstance().dataStore.registerWorld(event.getTargetWorld());
+        GriefDefenderPlugin.getInstance().dataStore.loadWorldData(event.getTargetWorld());
+
+        NMSUtil.getInstance().addEntityRemovalListener(event.getTargetWorld());
+        GDTimings.WORLD_LOAD_EVENT.stopTimingIfSync();
+        if (!GriefDefenderPlugin.getActiveConfig(event.getTargetWorld().getProperties()).getConfig().claim.bankTaxSystem) {
+            return;
+        }
+        if (GriefDefenderPlugin.getInstance().economyService.isPresent()) {
+            // run tax task
+            TaxApplyTask taxTask = new TaxApplyTask(event.getTargetWorld().getProperties());
+            int taxHour = GriefDefenderPlugin.getActiveConfig(event.getTargetWorld().getProperties()).getConfig().claim.taxApplyHour;
+            long delay = TaskUtil.computeDelay(taxHour, 0, 0);
+            Sponge.getScheduler().createTaskBuilder().delay(delay, TimeUnit.SECONDS).interval(1, TimeUnit.DAYS).execute(taxTask).submit(GDBootstrap.getInstance());
+        }
+    }
+
+    @Listener(order = Order.FIRST, beforeModifications = true)
+    public void onWorldUnload(UnloadWorldEvent event) {
+        if (!SpongeImpl.getServer().isServerRunning() || !GriefDefenderPlugin.getInstance().claimsEnabledForWorld(event.getTargetWorld().getUniqueId())) {
+            return;
+        }
+
+        GriefDefenderPlugin.getInstance().dataStore.removeClaimWorldManager(event.getTargetWorld().getProperties());
+    }
+
+    @Listener
+    public void onWorldSave(SaveWorldEvent.Post event) {
+        if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(event.getTargetWorld().getUniqueId())) {
+            return;
+        }
+
+        GDTimings.WORLD_SAVE_EVENT.startTimingIfSync();
+        GDClaimManager claimWorldManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(event.getTargetWorld().getUniqueId());
+        if (claimWorldManager == null) {
+            GDTimings.WORLD_SAVE_EVENT.stopTimingIfSync();
+            return;
+        }
+
+        claimWorldManager.save();
+        GDTimings.WORLD_SAVE_EVENT.stopTimingIfSync();
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/migrator/GPBukkitMigrator.java b/sponge/src/main/java/com/griefdefender/migrator/GPBukkitMigrator.java
new file mode 100644
index 0000000..a51e4d8
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/migrator/GPBukkitMigrator.java
@@ -0,0 +1,713 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.migrator;
+
+import com.flowpowered.math.vector.Vector3i;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.Tristate;
+import com.griefdefender.api.claim.ClaimType;
+import com.griefdefender.api.claim.ClaimTypes;
+import com.griefdefender.api.permission.Context;
+import com.griefdefender.api.permission.ContextKeys;
+import com.griefdefender.api.permission.flag.Flags;
+import com.griefdefender.api.permission.option.Options;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.configuration.ClaimDataConfig;
+import com.griefdefender.configuration.ClaimStorageData;
+import com.griefdefender.internal.util.BlockUtil;
+import com.griefdefender.permission.GDPermissionHolder;
+import com.griefdefender.permission.GDPermissionUser;
+import com.griefdefender.storage.BaseStorage;
+import com.griefdefender.util.PermissionUtil;
+
+import net.kyori.text.serializer.legacy.LegacyComponentSerializer;
+import ninja.leaping.configurate.ConfigurationNode;
+import ninja.leaping.configurate.yaml.YAMLConfigurationLoader;
+import org.spongepowered.api.world.World;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class GPBukkitMigrator {
+
+    private static final File gpBukkitPlayerDataMigrated = BaseStorage.globalPlayerDataPath.resolve("_bukkitMigrated").toFile();
+    private static final Path gpFlags = Paths.get("plugins", "GPFlags", "flags.yml");
+    private static final Map<Integer, UUID> idToUUID = new ConcurrentHashMap<>();
+    private static final Map<UUID, ClaimStorageData> claimStorageMap = new ConcurrentHashMap<>();
+    private static final GDPermissionHolder DEFAULT_HOLDER = GriefDefenderPlugin.DEFAULT_HOLDER;
+    private static int count;
+
+    private static final String FLAG_CHANGE_BIOME = "changebiome";
+    private static final String FLAG_COMMAND_BLACKLIST = "commandblacklist";
+    private static final String FLAG_COMMAND_WHITELIST = "commandwhitelist";
+    private static final String FLAG_ENTER_COMMAND = "entercommand";
+    private static final String FLAG_ENTER_COMMAND_MEMBERS = "entercommandmembers";
+    private static final String FLAG_ENTER_COMMAND_OWNER = "entercommandowner";
+    private static final String FLAG_ENTER_MESSAGE = "entermessage";
+    private static final String FLAG_ENTER_PLAYER_COMMAND = "enterplayercommand";
+    private static final String FLAG_EXIT_COMMAND = "exitcommand";
+    private static final String FLAG_EXIT_COMMAND_MEMBERS = "exitcommandmembers";
+    private static final String FLAG_EXIT_COMMAND_OWNER = "exitcommandowner";
+    private static final String FLAG_EXIT_MESSAGE = "exitmessage";
+    private static final String FLAG_EXIT_PLAYER_COMMAND = "exitplayercommand";
+    private static final String FLAG_HEALTH_REGEN = "healthregen";
+    private static final String FLAG_INFINITE_ARROWS = "infinitearrows";
+    private static final String FLAG_KEEP_INVENTORY = "keepinventory";
+    private static final String FLAG_KEEP_LEVEL = "keeplevel";
+    private static final String FLAG_NETHER_PORTAL_CONSOLE_COMMAND = "netherportalconsolecommand";
+    private static final String FLAG_NETHER_PORTAL_PLAYER_COMMAND = "netherportalplayercommand";
+    private static final String FLAG_NO_CHORUS_FRUIT = "nochorusfruit";
+    private static final String FLAG_NO_COMBAT_LOOT = "nocombatloot";
+    private static final String FLAG_NO_ENDER_PEARL = "noenderpearl";
+    private static final String FLAG_NO_ENTER = "noenter";
+    private static final String FLAG_NO_ENTER_PLAYER = "noenterplayer";
+    private static final String FLAG_NO_EXPIRATION = "noexpiration";
+    private static final String FLAG_NO_EXPLOSION_DAMAGE = "noexplosiondamage";
+    private static final String FLAG_NO_FALL_DAMAGE = "nofalldamage";
+    private static final String FLAG_NO_FIRE_DAMAGE = "nofiredamage";
+    private static final String FLAG_NO_FIRE_SPREAD = "nofirespread";
+    private static final String FLAG_NO_FLIGHT = "noflight";
+    private static final String FLAG_NO_FLUID_FLOW = "nofluidflow";
+    private static final String FLAG_NO_GROWTH = "nogrowth";
+    private static final String FLAG_NO_HUNGER = "nohunger";
+    private static final String FLAG_NO_ICE_FORM = "noiceform";
+    private static final String FLAG_NO_ITEM_DAMAGE = "noitemdamage";
+    private static final String FLAG_NO_ITEM_DROP = "noitemdrop";
+    private static final String FLAG_NO_ITEM_PICKUP = "noitempickup";
+    private static final String FLAG_NO_LEAF_DECAY = "noleafdecay";
+    private static final String FLAG_NO_LOOT_PROTECTION = "nolootprotection";
+    private static final String FLAG_NO_MOB_DAMAGE = "nomobdamage";
+    private static final String FLAG_NO_MOB_SPAWNS = "nomobspawns";
+    private static final String FLAG_NO_MOB_SPAWNS_TYPE = "nomobspawnstype";
+    private static final String FLAG_NO_OPEN_DOORS = "noopendoors";
+    private static final String FLAG_NO_PET_DAMAGE = "nopetdamage";
+    private static final String FLAG_NO_PLAYER_DAMAGE = "noplayerdamage";
+    private static final String FLAG_NO_SNOW_FORM = "nosnowform";
+    private static final String FLAG_NO_VEHICLE = "novehicle";
+    private static final String FLAG_NO_VINE_GROWTH = "novinegrowth";
+    private static final String FLAG_NO_WEATHER_CHANGE = "noweatherchange";
+    private static final String FLAG_OWNER_FLY = "ownerfly";
+    private static final String FLAG_OWNER_MEMBER_FLY = "ownermemberfly";
+    private static final String FLAG_PLAYER_GAMEMODE = "playergamemode";
+    private static final String FLAG_PLAYER_TIME = "playertime";
+    private static final String FLAG_PLAYER_WEATHER = "playerweather";
+    private static final String FLAG_RESPAWN_LOCATION = "respawnlocation";
+    private static final String FLAG_SPLEEF_ARENA = "spleefarena";
+    private static final String FLAG_TRAPPED_DESTINATION = "trappeddestination";
+
+    private static final Context SOURCE_PLAYER = new Context(ContextKeys.SOURCE, "player");
+    private static final Context TARGET_ICE_FORM = new Context(ContextKeys.TARGET, "ice");
+    private static final Context TARGET_PLAYER = new Context(ContextKeys.TARGET, "player");
+    private static final Context TARGET_SNOW_LAYER = new Context("target", "snow_layer");
+
+    public static void migrate(World world, Path gpClassicDataPath) throws FileNotFoundException, ClassNotFoundException {
+        count = 0;
+        GriefDefenderPlugin.getInstance().getLogger().info("Starting GriefPrevention data migration for world " + world.getName() + "...");
+        // Migrate playerdata first
+        migratePlayerData(world);
+        File[] files = gpClassicDataPath.toFile().listFiles();
+        if (files != null) {
+            for (int i = 0; i < files.length; i++) {
+                File file = files[i];
+                if (file.isFile()) {
+                    try {
+                        int claimId = Integer.parseInt(file.getName().replaceFirst("[.][^.]+$", ""));
+                        idToUUID.put(claimId, UUID.randomUUID());
+                    } catch (NumberFormatException e) {
+                        if (file.getName().equalsIgnoreCase("_nextClaimID")) {
+                            // GP keeps track of next claim ID in this file so we can safely ignore the exception
+                            continue;
+                        }
+                        e.printStackTrace();
+                        continue;
+                    }
+                }
+            }
+            migrateClaims(world, files, true);
+            migrateClaims(world, files, false);
+            GriefDefenderPlugin.getInstance().getLogger().info("Finished GriefPrevention data migration for world '" + world.getName() + "'."
+                    + " Migrated a total of " + count + " claims.");
+        }
+        if (Files.exists(gpFlags)) {
+            migrateGpFlags(world);
+        }
+    }
+
+    private static void migrateGpFlags(World world) {
+        YAMLConfigurationLoader flagsManager = YAMLConfigurationLoader.builder().setPath(gpFlags).build();
+        ConfigurationNode root = null;
+        try {
+            root = flagsManager.load();
+        } catch (IOException e1) {
+            e1.printStackTrace();
+            return;
+        }
+
+        int count = 0;
+        for (Entry<Object, ? extends ConfigurationNode> claims : root.getChildrenMap().entrySet()) {
+            Object key = claims.getKey();
+            int bukkitClaimId = -1;
+            try {
+                bukkitClaimId = Integer.parseInt(key.toString());
+            } catch (NumberFormatException e) {
+                e.printStackTrace();
+                continue;
+            }
+
+            final UUID claimUniqueId = idToUUID.get(bukkitClaimId);
+            if (claimUniqueId == null) {
+                GriefDefenderPlugin.getInstance().getLogger().info("Could not locate migrated GD claim for id '" + bukkitClaimId + "'. Skipping...");
+                continue;
+            }
+
+            final Set<Context> contexts = new HashSet<>();
+            final ClaimStorageData claimStorage = claimStorageMap.get(claimUniqueId);
+            if (claimStorage == null) {
+                // Different world
+                continue;
+            }
+            count++;
+            final UUID ownerUniqueId = claimStorage.getConfig().getOwnerUniqueId();
+            GDPermissionUser claimOwner = null;
+            if (ownerUniqueId != null) {
+                claimOwner = PermissionHolderCache.getInstance().getOrCreateUser(ownerUniqueId);
+            }
+            contexts.add(new Context(ContextKeys.CLAIM, claimUniqueId.toString()));
+            ConfigurationNode flags = claims.getValue();
+            GriefDefenderPlugin.getInstance().getLogger().info("Starting flag migration for legacy claim id '" + bukkitClaimId + "'...");
+            for (Entry<Object, ? extends ConfigurationNode> flagEntry : flags.getChildrenMap().entrySet()){
+                final String flag = (String) flagEntry.getKey();
+                final ConfigurationNode paramNode = flagEntry.getValue();
+                final String param = paramNode.getNode("params").getString();
+                final boolean value = paramNode.getNode("value").getBoolean();
+                if (!value) {
+                    continue;
+                }
+                GriefDefenderPlugin.getInstance().getLogger().info("Migrating flag '" + flag + "'...");
+                switch (flag) {
+                    case FLAG_COMMAND_BLACKLIST :
+                    case FLAG_COMMAND_WHITELIST :
+                        // TODO
+                        break;
+                    case FLAG_ENTER_MESSAGE :
+                        claimStorage.getConfig().setGreeting(LegacyComponentSerializer.legacy().deserialize(param, '�'));
+                        claimStorage.getConfig().setRequiresSave(true);
+                        claimStorage.save();
+                        break;
+                    case FLAG_ENTER_COMMAND :
+                        contexts.add(new Context(ContextKeys.FLAG, Flags.ENTER_CLAIM.getName()));
+                        contexts.add(TARGET_PLAYER);
+                        PermissionUtil.getInstance().setOptionValue(DEFAULT_HOLDER, Options.PLAYER_COMMAND.getPermission(), param, contexts);
+                        break;
+                    case FLAG_ENTER_COMMAND_OWNER :
+                        contexts.add(new Context(ContextKeys.FLAG, Flags.ENTER_CLAIM.getName()));
+                        contexts.add(TARGET_PLAYER);
+                        if (claimOwner == null) {
+                            GriefDefenderPlugin.getInstance().getLogger().info("Could not locate owner legacy claim id '" + bukkitClaimId + "'. Skipping...");
+                            break;
+                        }
+                        PermissionUtil.getInstance().setOptionValue(claimOwner, Options.PLAYER_COMMAND.getPermission(), param, contexts);
+                        break;
+                    case FLAG_ENTER_COMMAND_MEMBERS : {
+                        contexts.add(new Context(ContextKeys.FLAG, Flags.ENTER_CLAIM.getName()));
+                        contexts.add(TARGET_PLAYER);
+                        List<UUID> members = new ArrayList<>();
+                        members.addAll(claimStorage.getConfig().getAccessors());
+                        members.addAll(claimStorage.getConfig().getBuilders());
+                        members.addAll(claimStorage.getConfig().getContainers());
+                        members.addAll(claimStorage.getConfig().getManagers());
+                        for (UUID memberUniqueId : members) {
+                            final GDPermissionUser user = PermissionHolderCache.getInstance().getOrCreateUser(memberUniqueId);
+                            PermissionUtil.getInstance().setOptionValue(user, Options.PLAYER_COMMAND.getPermission(), param, contexts);
+                        }
+                        break;
+                    }
+                    case FLAG_ENTER_PLAYER_COMMAND :
+                        contexts.add(new Context(ContextKeys.FLAG, Flags.ENTER_CLAIM.getName()));
+                        contexts.add(new Context(ContextKeys.TARGET, "player"));
+                        PermissionUtil.getInstance().setOptionValue(DEFAULT_HOLDER, Options.PLAYER_COMMAND.getPermission(), param, contexts);
+                        break;
+                    case FLAG_EXIT_COMMAND_MEMBERS : {
+                        contexts.add(new Context(ContextKeys.FLAG, Flags.EXIT_CLAIM.getName()));
+                        contexts.add(TARGET_PLAYER);
+                        List<UUID> members = new ArrayList<>();
+                        members.addAll(claimStorage.getConfig().getAccessors());
+                        members.addAll(claimStorage.getConfig().getBuilders());
+                        members.addAll(claimStorage.getConfig().getContainers());
+                        members.addAll(claimStorage.getConfig().getManagers());
+                        for (UUID memberUniqueId : members) {
+                            final GDPermissionUser user = PermissionHolderCache.getInstance().getOrCreateUser(memberUniqueId);
+                            PermissionUtil.getInstance().setOptionValue(user, Options.PLAYER_COMMAND.getPermission(), param, contexts);
+                        }
+                        break;
+                    }
+                    case FLAG_EXIT_COMMAND_OWNER :
+                        contexts.add(new Context(ContextKeys.FLAG, Flags.EXIT_CLAIM.getName()));
+                        contexts.add(TARGET_PLAYER);
+                        if (claimOwner == null) {
+                            GriefDefenderPlugin.getInstance().getLogger().info("Could not locate owner legacy claim id '" + bukkitClaimId + "'. Skipping...");
+                            break;
+                        }
+                        PermissionUtil.getInstance().setOptionValue(claimOwner, Options.PLAYER_COMMAND.getPermission(), param, contexts);
+                        break;
+                    case FLAG_EXIT_COMMAND :
+                    case FLAG_EXIT_PLAYER_COMMAND :
+                        contexts.add(new Context(ContextKeys.FLAG, Flags.EXIT_CLAIM.getName()));
+                        contexts.add(TARGET_PLAYER);
+                        PermissionUtil.getInstance().setOptionValue(DEFAULT_HOLDER, Options.PLAYER_COMMAND.getPermission(), param, contexts);
+                        break;
+                    case FLAG_EXIT_MESSAGE :
+                        claimStorage.getConfig().setFarewell(LegacyComponentSerializer.legacy().deserialize(param, '�'));
+                        claimStorage.getConfig().setRequiresSave(true);
+                        claimStorage.save();
+                        break;
+                    case FLAG_HEALTH_REGEN :
+                        PermissionUtil.getInstance().setOptionValue(DEFAULT_HOLDER, Options.PLAYER_HEALTH_REGEN.getPermission(), param, contexts);
+                        break;
+                    case FLAG_KEEP_INVENTORY :
+                        PermissionUtil.getInstance().setOptionValue(DEFAULT_HOLDER, Options.PLAYER_KEEP_INVENTORY.getPermission(), "1", contexts);
+                        break;
+                    case FLAG_KEEP_LEVEL :
+                        PermissionUtil.getInstance().setOptionValue(DEFAULT_HOLDER, Options.PLAYER_KEEP_LEVEL.getPermission(), "1", contexts);
+                        break;
+                    case FLAG_NO_CHORUS_FRUIT :
+                        contexts.add(new Context(ContextKeys.TARGET, "chorus_fruit"));
+                        PermissionUtil.getInstance().setPermissionValue(DEFAULT_HOLDER, Flags.ITEM_USE.getPermission(), Tristate.FALSE, contexts);
+                        break;
+                    case FLAG_NO_ENDER_PEARL :
+                        contexts.add(new Context(ContextKeys.TARGET, "ender_pearl"));
+                        PermissionUtil.getInstance().setPermissionValue(DEFAULT_HOLDER, Flags.INTERACT_ITEM_SECONDARY.getPermission(), Tristate.FALSE, contexts);
+                        break;
+                    case FLAG_NO_ENTER :
+                        contexts.add(new Context(ContextKeys.TARGET, "player"));
+                        PermissionUtil.getInstance().setPermissionValue(DEFAULT_HOLDER, Flags.ENTER_CLAIM.getPermission(), Tristate.FALSE, contexts);
+                        break;
+                    case FLAG_NO_ENTER_PLAYER :
+                        // TODO
+                        break;
+                    case FLAG_NO_EXPIRATION :
+                        claimStorage.getConfig().setExpiration(false);
+                        claimStorage.getConfig().setRequiresSave(true);
+                        claimStorage.save();
+                        break;
+                    case FLAG_NO_EXPLOSION_DAMAGE :
+                        PermissionUtil.getInstance().setPermissionValue(DEFAULT_HOLDER, Flags.EXPLOSION_BLOCK.getPermission(), Tristate.FALSE, contexts);
+                        PermissionUtil.getInstance().setPermissionValue(DEFAULT_HOLDER, Flags.EXPLOSION_ENTITY.getPermission(), Tristate.FALSE, contexts);
+                        break;
+                    case FLAG_NO_FALL_DAMAGE :
+                        contexts.add(new Context(ContextKeys.SOURCE, "fall"));
+                        contexts.add(TARGET_PLAYER);
+                        PermissionUtil.getInstance().setPermissionValue(DEFAULT_HOLDER, Flags.ENTITY_DAMAGE.getPermission(), Tristate.FALSE, contexts);
+                        break;
+                    case FLAG_NO_FIRE_DAMAGE :
+                        contexts.add(new Context(ContextKeys.SOURCE, "fire"));
+                        contexts.add(TARGET_PLAYER);
+                        PermissionUtil.getInstance().setPermissionValue(DEFAULT_HOLDER, Flags.ENTITY_DAMAGE.getPermission(), Tristate.FALSE, contexts);
+                        break;
+                    case FLAG_NO_FIRE_SPREAD :
+                        contexts.add(new Context(ContextKeys.SOURCE, "fire"));
+                        PermissionUtil.getInstance().setPermissionValue(DEFAULT_HOLDER, Flags.BLOCK_SPREAD.getPermission(), Tristate.FALSE, contexts);
+                        break;
+                    case FLAG_NO_FLIGHT :
+                        PermissionUtil.getInstance().setOptionValue(DEFAULT_HOLDER, Options.PLAYER_DENY_FLIGHT.getPermission(), "true", contexts);
+                        break;
+                    case FLAG_NO_FLUID_FLOW :
+                        PermissionUtil.getInstance().setPermissionValue(DEFAULT_HOLDER, Flags.LIQUID_FLOW.getPermission(), Tristate.FALSE, contexts);
+                        break;
+                    case FLAG_NO_GROWTH :
+                        PermissionUtil.getInstance().setPermissionValue(DEFAULT_HOLDER, Flags.BLOCK_GROW.getPermission(), Tristate.FALSE, contexts);
+                        break;
+                    case FLAG_NO_HUNGER :
+                        PermissionUtil.getInstance().setOptionValue(DEFAULT_HOLDER, Options.PLAYER_DENY_HUNGER.getPermission(), "true", contexts);
+                        break;
+                    case FLAG_NO_ICE_FORM :
+                        contexts.add(TARGET_ICE_FORM);
+                        PermissionUtil.getInstance().setPermissionValue(DEFAULT_HOLDER, Flags.BLOCK_MODIFY.getPermission(), Tristate.FALSE, contexts);
+                        break;
+                    case FLAG_NO_ITEM_DROP :
+                        contexts.add(TARGET_PLAYER);
+                        PermissionUtil.getInstance().setPermissionValue(DEFAULT_HOLDER, Flags.ITEM_DROP.getPermission(), Tristate.FALSE, contexts);
+                        break;
+                    case FLAG_NO_ITEM_PICKUP :
+                        contexts.add(TARGET_PLAYER);
+                        PermissionUtil.getInstance().setPermissionValue(DEFAULT_HOLDER, Flags.ITEM_PICKUP.getPermission(), Tristate.FALSE, contexts);
+                        break;
+                    case FLAG_NO_LEAF_DECAY :
+                        PermissionUtil.getInstance().setPermissionValue(DEFAULT_HOLDER, Flags.LEAF_DECAY.getPermission(), Tristate.FALSE, contexts);
+                        break;
+                    case FLAG_NO_MOB_DAMAGE :
+                        contexts.add(SOURCE_PLAYER);
+                        contexts.add(new Context(ContextKeys.TARGET, "#monster"));
+                        PermissionUtil.getInstance().setPermissionValue(DEFAULT_HOLDER, Flags.ENTITY_DAMAGE.getPermission(), Tristate.FALSE, contexts);
+                        break;
+                    case FLAG_NO_MOB_SPAWNS :
+                        contexts.add(new Context(ContextKeys.TARGET, "#monster"));
+                        PermissionUtil.getInstance().setPermissionValue(DEFAULT_HOLDER, Flags.ENTITY_SPAWN.getPermission(), Tristate.FALSE, contexts);
+                        break;
+                    case FLAG_NO_PLAYER_DAMAGE :
+                        contexts.add(new Context(ContextKeys.TARGET, "player"));
+                        PermissionUtil.getInstance().setPermissionValue(DEFAULT_HOLDER, Flags.ENTITY_DAMAGE.getPermission(), Tristate.FALSE, contexts);
+                        break;
+                    case FLAG_NO_SNOW_FORM :
+                        contexts.add(TARGET_SNOW_LAYER);
+                        PermissionUtil.getInstance().setPermissionValue(DEFAULT_HOLDER, Flags.BLOCK_MODIFY.getPermission(), Tristate.FALSE, contexts);
+                        break;
+                    case FLAG_NO_VEHICLE :
+                        contexts.add(new Context(ContextKeys.TARGET, "#vehicle"));
+                        PermissionUtil.getInstance().setPermissionValue(DEFAULT_HOLDER, Flags.INTERACT_ENTITY_SECONDARY.getPermission(), Tristate.FALSE, contexts);
+                        break;
+                    case FLAG_NO_VINE_GROWTH :
+                        contexts.add(new Context(ContextKeys.SOURCE, "vine"));
+                        PermissionUtil.getInstance().setPermissionValue(DEFAULT_HOLDER, Flags.BLOCK_GROW.getPermission(), Tristate.FALSE, contexts);
+                        break;
+                    case FLAG_OWNER_FLY : {
+                        if (claimOwner == null) {
+                            GriefDefenderPlugin.getInstance().getLogger().info("Could not locate owner legacy claim id '" + bukkitClaimId + "'. Skipping...");
+                            break;
+                        }
+                        PermissionUtil.getInstance().setOptionValue(claimOwner, Options.PLAYER_DENY_FLIGHT.getPermission(), "1", contexts);
+                        break;
+                    }
+                    case FLAG_OWNER_MEMBER_FLY : {
+                        List<UUID> members = new ArrayList<>();
+                        members.addAll(claimStorage.getConfig().getAccessors());
+                        members.addAll(claimStorage.getConfig().getBuilders());
+                        members.addAll(claimStorage.getConfig().getContainers());
+                        members.addAll(claimStorage.getConfig().getManagers());
+                        for (UUID memberUniqueId : members) {
+                            final GDPermissionUser user = PermissionHolderCache.getInstance().getOrCreateUser(memberUniqueId);
+                            PermissionUtil.getInstance().setOptionValue(user, Options.PLAYER_DENY_FLIGHT.getPermission(), "1", contexts);
+                        }
+                        break;
+                    }
+
+                    case FLAG_RESPAWN_LOCATION :
+                        // TODO
+                        break;
+
+                    // NOT CURRENTLY SUPPORTED
+                    case FLAG_CHANGE_BIOME :
+                    case FLAG_INFINITE_ARROWS :
+                    case FLAG_NETHER_PORTAL_CONSOLE_COMMAND :
+                    case FLAG_NETHER_PORTAL_PLAYER_COMMAND :
+                    case FLAG_NO_COMBAT_LOOT :
+                    case FLAG_NO_ITEM_DAMAGE :
+                    case FLAG_NO_LOOT_PROTECTION :
+                    case FLAG_NO_MOB_SPAWNS_TYPE :
+                    case FLAG_NO_OPEN_DOORS :
+                    case FLAG_NO_PET_DAMAGE :
+                    case FLAG_NO_WEATHER_CHANGE :
+                    case FLAG_PLAYER_GAMEMODE :
+                    case FLAG_PLAYER_TIME :
+                    case FLAG_PLAYER_WEATHER :
+                    case FLAG_SPLEEF_ARENA :
+                    case FLAG_TRAPPED_DESTINATION :
+                        GriefDefenderPlugin.getInstance().getLogger().info("Flag '" + flag + "' not currently supported! Skipping...");
+                        break;
+                }
+            }
+ 
+            GriefDefenderPlugin.getInstance().getLogger().info("Successfully migrated GPFlags for claim id '" + bukkitClaimId + "' to '" + claimUniqueId + "'");
+        }
+        GriefDefenderPlugin.getInstance().getLogger().info("Finished GPFlags data migration for world '" + world.getName() + "'."
+                + " Migrated a total of " + count + " claims.");
+    }
+
+    private static void migratePlayerData(World world) {
+        if (gpBukkitPlayerDataMigrated.exists()) {
+            return;
+        }
+
+        final Path path = Paths.get("plugins", "GriefPreventionData", "PlayerData");
+        if (!path.toFile().exists()) {
+            return;
+        }
+
+        File[] files = path.toFile().listFiles();
+        if (files != null) {
+            GriefDefenderPlugin.getInstance().getLogger().info("Migrating " + files.length + " player data files...");
+            for (int i = 0; i < files.length; i++) {
+                final File file = files[i];
+                GriefDefenderPlugin.getInstance().getLogger().info("Migrating playerdata " + file.getName() + "...");
+                UUID uuid = null;
+                try {
+                    uuid = UUID.fromString(file.getName().replaceFirst("[.][^.]+$", ""));
+                } catch (IllegalArgumentException e) {
+                    e.printStackTrace();
+                    continue;
+                }
+                List<String> lines;
+                try {
+                    lines = Files.readAllLines(file.toPath());
+                } catch (IOException e) {
+                    e.printStackTrace();
+                    continue;
+                }
+                if (lines.size() < 3) {
+                    continue;
+                }
+                try {
+                    final int accruedBlocks = Integer.parseInt(lines.get(1));
+                    final int bonusBlocks = Integer.parseInt(lines.get(2));
+                    final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(world, uuid);
+                    // Set directly in storage as subject data has not been initialized
+                    playerData.getStorageData().getConfig().setAccruedClaimBlocks(accruedBlocks);
+                    playerData.getStorageData().getConfig().setBonusClaimBlocks(bonusBlocks);
+                    playerData.saveAllData();
+                } catch (NumberFormatException e) {
+                    e.printStackTrace();
+                    continue;
+                }
+            }
+        }
+
+        try {
+            Files.createFile(gpBukkitPlayerDataMigrated.toPath());
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private static void migrateClaims(World world, File[] files, boolean parentsOnly) {
+        for (int i = 0; i < files.length; i++) {
+            File file = files[i];
+            if (file.isFile()) {
+                createClaim(world, file, parentsOnly);
+            }
+        }
+    }
+
+    private static void createClaim(World world, File file, boolean parentsOnly) {
+        int claimId;
+        try {
+            claimId = Integer.parseInt(file.getName().replaceFirst("[.][^.]+$", ""));
+        } catch (NumberFormatException e) {
+            return;
+        }
+        final UUID claimUniqueId = idToUUID.get(claimId);
+        if (claimUniqueId == null) {
+            return;
+        }
+        YAMLConfigurationLoader regionManager = YAMLConfigurationLoader.builder().setPath(file.toPath()).build();
+        ConfigurationNode region = null;
+        try {
+            region = regionManager.load();
+        } catch (IOException e1) {
+            e1.printStackTrace();
+            return;
+        }
+        if (parentsOnly && region.getChildrenMap().get("Parent Claim ID").getInt() != -1) {
+            return;
+        }
+
+        final ConfigurationNode lesserNode = region.getChildrenMap().get("Lesser Boundary Corner");
+        if (lesserNode.isVirtual()) {
+            return;
+        }
+        final String claimWorldName = getWorldName(lesserNode.getValue().toString());
+        if (lesserNode != null && !world.getName().equalsIgnoreCase(claimWorldName)) {
+            return;
+        }
+        Vector3i lesserBoundaryCorner = null;
+        Vector3i greaterBoundaryCorner = null;
+        List<UUID> builders = new ArrayList<>();
+        List<UUID> containers = new ArrayList<>();
+        List<UUID> accessors = new ArrayList<>();
+        List<UUID> managers = new ArrayList<>();
+        UUID parentClaimUniqueId = null;
+        UUID ownerUniqueId = null;
+        boolean inherit = true;
+        for (Entry<Object, ? extends ConfigurationNode> mapEntry : region.getChildrenMap().entrySet()){
+            Object key = mapEntry.getKey();
+            ConfigurationNode value = mapEntry.getValue();
+            if (key.equals("Lesser Boundary Corner")) {
+                lesserBoundaryCorner = classicPosFromString(value.getString(), true);
+            } else if (key.equals("Greater Boundary Corner")) {
+                greaterBoundaryCorner = classicPosFromString(value.getString(), false);
+            } else if (key.equals("Owner")) {
+                final String owner = value.getString();
+                if (owner != null && !owner.isEmpty()) {
+                    try {
+                        ownerUniqueId = UUID.fromString(value.getString());
+                    } catch (IllegalArgumentException e) {
+                        e.printStackTrace();
+                    }
+                }
+            } else if (key.equals("Builders")) {
+                for (String id : (List<String>) value.getValue()) {
+                    if (id.equals("public")) {
+                        builders.add(GriefDefenderPlugin.PUBLIC_UUID);
+                    } else {
+                        try {
+                            final UUID uuid = UUID.fromString(id);
+                            builders.add(uuid);
+                        } catch (IllegalArgumentException e) {
+                            
+                        }
+                    }
+                }
+            } else if (key.equals("Containers")) {
+                for (String id : (List<String>) value.getValue()) {
+                    if (id.equals("public")) {
+                        containers.add(GriefDefenderPlugin.PUBLIC_UUID);
+                    } else {
+                        try {
+                            final UUID uuid = UUID.fromString(id);
+                            containers.add(uuid);
+                        } catch (IllegalArgumentException e) {
+                            
+                        }
+                    }
+                }
+            } else if (key.equals("Accessors")) {
+                for (String id : (List<String>) value.getValue()) {
+                    if (id.equals("public")) {
+                        accessors.add(GriefDefenderPlugin.PUBLIC_UUID);
+                    } else {
+                        try {
+                            final UUID uuid = UUID.fromString(id);
+                            accessors.add(uuid);
+                        } catch (IllegalArgumentException e) {
+                            
+                        }
+                    }
+                }
+            } else if (key.equals("Managers")) {
+                for (String id : (List<String>) value.getValue()) {
+                    if (id.equals("public")) {
+                        managers.add(GriefDefenderPlugin.PUBLIC_UUID);
+                    } else {
+                        try {
+                            final UUID uuid = UUID.fromString(id);
+                            managers.add(uuid);
+                        } catch (IllegalArgumentException e) {
+                            
+                        }
+                    }
+                }
+            } else if (key.equals("Parent Claim ID")) {
+                if (value.getInt() != -1) {
+                    parentClaimUniqueId = idToUUID.get(value.getInt());
+                    if (parentClaimUniqueId == null) {
+                        GriefDefenderPlugin.getInstance().getLogger().info("Detected corrupted subdivision claim file '" + file + "' with parent id " + value.getInt() + " that does NOT exist. Skipping...");
+                        return;
+                    }
+                }
+            } else if (key.equals("inheritNothing")) {
+                inherit = !value.getBoolean();
+            }
+        }
+        if (lesserBoundaryCorner == null) {
+            return;
+        }
+
+        ClaimType type = null;
+        if (parentClaimUniqueId != null) {
+            type = ClaimTypes.SUBDIVISION;
+        } else if (ownerUniqueId == null) {
+            type = ClaimTypes.ADMIN;
+        } else {
+            type = ClaimTypes.BASIC;
+        }
+        Path claimDataFolderPath = null;
+        if (parentClaimUniqueId != null) {
+            final ClaimStorageData claimStorage = claimStorageMap.get(parentClaimUniqueId);
+            claimDataFolderPath = claimStorage.filePath.getParent().resolve(type.getName().toLowerCase());
+            if (ownerUniqueId == null) {
+                ownerUniqueId = claimStorage.getConfig().getOwnerUniqueId();
+            }
+        } else {
+            claimDataFolderPath = BaseStorage.worldConfigMap.get(world.getUniqueId()).getPath().getParent().resolve("ClaimData").resolve(type.getName().toLowerCase());
+        }
+        Path claimFilePath = claimDataFolderPath.resolve(claimUniqueId.toString());
+        if (!Files.exists(claimFilePath)) {
+            claimFilePath.toFile().getParentFile().mkdirs();
+            try {
+                Files.createFile(claimFilePath);
+            } catch (IOException e) {
+                e.printStackTrace();
+                return;
+            }
+        }
+
+        ClaimStorageData claimStorage = new ClaimStorageData(claimFilePath, world.getUniqueId());
+        claimStorageMap.put(claimUniqueId, claimStorage);
+        ClaimDataConfig claimDataConfig = claimStorage.getConfig();
+        if (ownerUniqueId != null) {
+            claimDataConfig.setOwnerUniqueId(ownerUniqueId);
+        }
+
+        claimDataConfig.setLesserBoundaryCorner(BlockUtil.getInstance().posToString(lesserBoundaryCorner));
+        claimDataConfig.setGreaterBoundaryCorner(BlockUtil.getInstance().posToString(greaterBoundaryCorner));
+        claimDataConfig.setBuilders(builders);
+        claimDataConfig.setContainers(containers);
+        claimDataConfig.setAccessors(accessors);
+        claimDataConfig.setManagers(managers);
+        claimDataConfig.setInheritParent(inherit);
+        claimDataConfig.setDateLastActive(Instant.now());
+        claimDataConfig.setParent(parentClaimUniqueId);
+        claimDataConfig.setWorldUniqueId(world.getUniqueId());
+        claimDataConfig.setType(type);
+        claimDataConfig.setRequiresSave(true);
+        claimStorage.save();
+        GriefDefenderPlugin.getInstance().getLogger().info("Successfully migrated GriefPrevention claim file '" + file.getName() + "' to '" + claimFilePath + "'");
+        count++;
+    }
+
+    private static String getWorldName(String string) {
+        String[] elements = string.split(";");
+        return elements[0];
+    }
+
+    private static Vector3i classicPosFromString(String string, boolean lesser) {
+        // split the input string on the space
+        String[] elements = string.split(";");
+
+        String worldName = elements[0];
+        String xString = elements[1];
+        String zString = elements[3];
+
+        // convert those numerical strings to integer values
+        int x = Integer.parseInt(xString);
+        int y = lesser ? 0 : 255;
+        int z = Integer.parseInt(zString);
+
+        return new Vector3i(x, y, z);
+    }
+}
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/migrator/GPSpongeMigrator.java b/sponge/src/main/java/com/griefdefender/migrator/GPSpongeMigrator.java
new file mode 100644
index 0000000..1f20717
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/migrator/GPSpongeMigrator.java
@@ -0,0 +1,308 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.migrator;
+
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.permission.ContextKeys;
+import com.griefdefender.api.permission.flag.Flag;
+import com.griefdefender.registry.FlagRegistryModule;
+import org.apache.commons.io.FileUtils;
+import org.spongepowered.api.service.context.Context;
+import org.spongepowered.api.service.permission.PermissionService;
+import org.spongepowered.api.service.permission.Subject;
+import org.spongepowered.api.util.Tristate;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.function.Consumer;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class GPSpongeMigrator {
+
+    final String regex = "^([^\\.]+).([^\\.]+).([^\\.]+)";
+    final Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE);
+
+    private static GPSpongeMigrator instance;
+    public PermissionService permissionService = GriefDefenderPlugin.getInstance().permissionService;
+    final Path GP_GLOBAL_PLAYER_DATA_PATH = Paths.get("config", "griefprevention", "GlobalPlayerData");
+    final Path GP_CLAIM_DATA_PATH = Paths.get("config", "griefprevention", "worlds");
+    final Path GD_DATA_ROOT_PATH = Paths.get("config", "griefdefender");
+
+    public void migrateData() {
+        try {
+            FileUtils.copyDirectory(GP_GLOBAL_PLAYER_DATA_PATH.toFile(), GD_DATA_ROOT_PATH.resolve("GlobalPlayerData").toFile(), true);
+            FileUtils.copyDirectory(GP_CLAIM_DATA_PATH.toFile(), GD_DATA_ROOT_PATH.resolve("worlds").toFile(), true);
+            //FileUtils.copyFile(GP_GLOBAL_CONFIG.toFile(), GD_DATA_ROOT_PATH.resolve("global.conf").toFile());
+        } catch (IOException e1) {
+            e1.printStackTrace();
+        }
+        final File dataPath = GD_DATA_ROOT_PATH.resolve("worlds").toFile();
+        final File[] files = dataPath.listFiles();
+
+        GriefDefenderPlugin.getInstance().executor.execute(() -> {
+            final CompletableFuture<Set<String>> future = permissionService.getGroupSubjects().getAllIdentifiers();
+            future.thenAccept(groups -> {
+                int count = 0;
+                final int size = groups.size();
+                List<Integer> printedSizes = new CopyOnWriteArrayList<>();
+                for (String group : groups) {
+                    count++;
+                    final Subject groupSubject = permissionService.getGroupSubjects().getSubject(group).orElse(null);
+                    final int current = count;
+                    if (groupSubject == null) {
+                        permissionService.getGroupSubjects().loadSubject(group).thenAccept(s -> {
+                            migrateSubject(s, true);
+                            final int printSize = (current*100/size);
+                            if (!printedSizes.contains(printSize)) {
+                                GriefDefenderPlugin.getInstance().getLogger().info("Performing group permission migration: " + printSize + "%");
+                                printedSizes.add(printSize);
+                            }
+                        });
+                    } else {
+                        migrateSubject(groupSubject, true);
+                        final int printSize = (current*100/size);
+                        if (!printedSizes.contains(printSize)) {
+                            GriefDefenderPlugin.getInstance().getLogger().info("Performing group permission migration: " + printSize + "%");
+                            printedSizes.add(printSize);
+                        }
+                    }
+                    try {
+                        Thread.sleep(10);
+                    } catch (InterruptedException e) {
+                        e.printStackTrace();
+                    }
+                }
+            });
+            final CompletableFuture<Set<String>> futureUsers = permissionService.getUserSubjects().getAllIdentifiers();
+            futureUsers.thenAccept(users -> {
+                int count = 0;
+                final int size = users.size();
+                List<Integer> printedSizes = new CopyOnWriteArrayList<>();
+                for (String user : users) {
+                    count++;
+                    final Subject userSubject = permissionService.getUserSubjects().getSubject(user).orElse(null);
+                    final int current = count;
+                    if (userSubject == null) {
+                        permissionService.getUserSubjects().loadSubject(user).thenAccept(s -> {
+                            migrateSubject(s, false);
+                            final int printSize = (current*100/size);
+                            if (!printedSizes.contains(printSize)) {
+                                GriefDefenderPlugin.getInstance().getLogger().info("Performing user permission migration: " + printSize + "%");
+                                printedSizes.add(printSize);
+                            }
+                        });
+                    } else {
+                        migrateSubject(userSubject, false);
+                        final int printSize = (current*100/size);
+                        if (!printedSizes.contains(printSize) && printSize != 100) {
+                            GriefDefenderPlugin.getInstance().getLogger().info("Performing user permission migration: " + printSize + "%");
+                            printedSizes.add(printSize);
+                        }
+                    }
+                    try {
+                        Thread.sleep(1);
+                    } catch (InterruptedException e) {
+                        e.printStackTrace();
+                    }
+                }
+                // migrate default user
+                migrateSubject(GriefDefenderPlugin.getInstance().permissionService.getDefaults(), false);
+                GriefDefenderPlugin.getInstance().getLogger().info("Performing user permission migration: 100%");
+            });
+        });
+    }
+
+    private void migrateFolderData(File[] files) {
+        for (int i = 0; i < files.length; i++) {
+            File file = files[i];
+            if (file.isDirectory()) {
+                migrateFolderData(file.listFiles());
+            }
+        }
+    }
+
+    public Consumer<Subject> migrateSubjectPermissions(boolean isGroup) {
+        return subject -> {
+            migrateSubject(subject, isGroup);
+        };
+    }
+
+    public void migrateSubject(Subject subject, boolean isGroup) {
+        boolean migrated = false;
+        try {
+            for (Map.Entry<Set<Context>, Map<String, Boolean>> mapEntry : subject.getSubjectData().getAllPermissions().entrySet()) {
+                final Set<Context> originalContexts = mapEntry.getKey();
+                Iterator<Map.Entry<String, Boolean>> iterator = mapEntry.getValue().entrySet().iterator();
+                Set<Context> gdContexts = new HashSet<>();
+                for (Context context : originalContexts) {
+                    if (context.getKey().equalsIgnoreCase("gp_claim_overrides")) {
+                        gdContexts.add(new Context("gd_claim_override", context.getValue()));
+                    } else if (context.getKey().equalsIgnoreCase("gp_claim_defaults")) {
+                        gdContexts.add(new Context("gd_claim_default", context.getValue()));
+                    } else if (context.getKey().equalsIgnoreCase("gp_claim")) {
+                        gdContexts.add(new Context("gd_claim", context.getValue()));
+                    } else {
+                        gdContexts.add(context);
+                    }
+                }
+                while (iterator.hasNext()) {
+                    final Map.Entry<String, Boolean> entry = iterator.next();
+                    final String originalPermission = entry.getKey();
+                    if (!originalPermission.startsWith("griefprevention")) {
+                        continue;
+                    }
+                    final String currentPermission = originalPermission.replace("griefprevention", "griefdefender").replace("fire-spread", "block-spread");
+                    final Matcher matcher = pattern.matcher(currentPermission);
+                    String flagBase = "";
+                    if (matcher.find()) {
+                        flagBase = matcher.group(0);
+                    }
+                    final Flag flag = FlagRegistryModule.getInstance().getById(flagBase).orElse(null);
+                    if (flag == null) {
+                        GriefDefenderPlugin.getInstance().getLogger().info("Detected legacy permission '" + originalPermission + "' on subject " + subject.getFriendlyIdentifier().orElse(subject.getIdentifier()) + "'. Migrating...");
+                        subject.getSubjectData().setPermission(originalContexts, originalPermission, Tristate.UNDEFINED);
+                        GriefDefenderPlugin.getInstance().getLogger().info("Removed legacy permission '" + originalPermission + "'.");
+                        subject.getSubjectData().setPermission(originalContexts, currentPermission, Tristate.fromBoolean(entry.getValue()));
+                        GriefDefenderPlugin.getInstance().getLogger().info("Set new permission '" + currentPermission + "' with contexts " + originalContexts);
+                        GriefDefenderPlugin.getInstance().getLogger().info("Successfully migrated permission " + currentPermission + " to " + currentPermission + " with contexts " + originalContexts);
+                    } else {
+                        GriefDefenderPlugin.getInstance().getLogger().info("Detected legacy flag permission '" + originalPermission + "' on subject " + subject.getFriendlyIdentifier().orElse(subject.getIdentifier()) + "'. Migrating...");
+                        subject.getSubjectData().setPermission(originalContexts, originalPermission, Tristate.UNDEFINED);
+                        GriefDefenderPlugin.getInstance().getLogger().info("Removed legacy flag permission '" + originalPermission + "'.");
+                        Set<Context> newContextSet = new HashSet<>(gdContexts);
+                        applyContexts(flagBase, currentPermission, newContextSet);
+                        subject.getSubjectData().setPermission(newContextSet, flag.getPermission(), Tristate.fromBoolean(entry.getValue()));
+                        GriefDefenderPlugin.getInstance().getLogger().info("Set new flag permission '" + flag.getPermission() + "' with contexts " + newContextSet);
+                        GriefDefenderPlugin.getInstance().getLogger().info("Successfully migrated flag permission " + currentPermission + " to " + flag.getPermission() + " with contexts " + newContextSet);
+                    }
+                    migrated = true;
+                }
+            }
+            if (migrated) {
+                GriefDefenderPlugin.getInstance().getLogger().info("Finished migration of subject '" + subject.getIdentifier() + "'\n");
+            }
+            if (isGroup) {
+                permissionService.getGroupSubjects().suggestUnload(subject.getIdentifier());
+            } else {
+                permissionService.getUserSubjects().suggestUnload(subject.getIdentifier());
+            }
+        } catch(Throwable t) {
+            t.printStackTrace();
+        }
+    }
+
+    private void applyContexts(String flagBase, String currentPermission, Set<Context> contexts) {
+        final int permLength = currentPermission.split("\\.").length;
+        if (permLength == 3) {
+            // Only contains flag
+            return;
+        }
+
+        String newPermission = currentPermission.replace(flagBase + ".", "");
+        final String[] parts = newPermission.split("\\.source\\.");
+        String targetPart = parts[0];
+        String sourcePart = parts.length > 1 ? parts[1] : null;
+        if (sourcePart != null) {
+            // handle source
+            String sourceParts[] = sourcePart.split("\\.");
+            String sourceId = sourceParts[0];
+            if (sourceParts.length > 1) {
+                String value = sourceParts[1];
+                int index = 2;
+                while (index < sourceParts.length) {
+                    value += "." + sourceParts[index];
+                    index++;
+                }
+                if (value.equals("animal")) {
+                    sourceId = "#animal";
+                } else if (value.equals("monster")) {
+                    sourceId = "#monster";
+                } else {
+                    if (value.contains("animal.")) {
+                        value = value.replace("animal.", "");
+                    } else if (value.contains("monster.")) {
+                        value = value.replace("monster.", "");
+                    }
+                    sourceId += ":" + value;
+                }
+            } else {
+                sourceId += ":any";
+            }
+            if (currentPermission.contains("interact") && GriefDefenderPlugin.ITEM_IDS.contains(sourceId)) {
+                contexts.add(new Context("used_item", sourceId));
+                GriefDefenderPlugin.getInstance().getLogger().info("Created new used_item context for '" + sourceId + "'.");
+            } else {
+                contexts.add(new Context("source", sourceId));
+                GriefDefenderPlugin.getInstance().getLogger().info("Created new source context for '" + sourceId + "'.");
+            }
+        }
+
+        // Handle target
+        String targetParts[] = targetPart.split("\\.");
+        String targetId = targetParts[0];
+        if (targetParts.length > 1) {
+            String value = targetParts[1];
+            int index = 2;
+            while (index < targetParts.length) {
+                value += "." + targetParts[index];
+                index++;
+            }
+            if (value.equals("animal")) {
+                targetId = "#animal";
+            } else if (value.equals("monster")) {
+                targetId = "#monster";
+            } else {
+                if (value.contains("animal.")) {
+                    value = value.replace("animal.", "");
+                } else if (value.contains("monster.")) {
+                    value = value.replace("monster.", "");
+                }
+                targetId += ":" + value;
+            }
+        } else {
+            targetId += ":any";
+        }
+        contexts.add(new Context(ContextKeys.TARGET, targetId));
+        GriefDefenderPlugin.getInstance().getLogger().info("Created new target context for '" + targetId + "'.");
+    }
+
+    public static GPSpongeMigrator getInstance() {
+        return instance;
+    }
+
+    static {
+        instance = new GPSpongeMigrator();
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/migrator/RedProtectMigrator.java b/sponge/src/main/java/com/griefdefender/migrator/RedProtectMigrator.java
new file mode 100644
index 0000000..3e177bf
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/migrator/RedProtectMigrator.java
@@ -0,0 +1,193 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.migrator;
+
+import com.google.common.reflect.TypeToken;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.claim.ClaimTypes;
+import com.griefdefender.configuration.ClaimDataConfig;
+import com.griefdefender.configuration.ClaimStorageData;
+import com.griefdefender.internal.util.BlockUtil;
+import com.griefdefender.util.PermissionUtil;
+import com.griefdefender.util.PlayerUtil;
+import net.kyori.text.TextComponent;
+import ninja.leaping.configurate.commented.CommentedConfigurationNode;
+import ninja.leaping.configurate.hocon.HoconConfigurationLoader;
+import ninja.leaping.configurate.loader.ConfigurationLoader;
+import ninja.leaping.configurate.objectmapping.ObjectMappingException;
+import org.spongepowered.api.world.Location;
+import org.spongepowered.api.world.World;
+
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class RedProtectMigrator {
+
+    private static final String USERNAME_PATTERN = "[a-zA-Z0-9_]+";
+
+    public static void migrate(World world, Path redProtectFilePath, Path gpClaimDataPath) throws FileNotFoundException, ClassNotFoundException {
+        if (!GriefDefenderPlugin.getGlobalConfig().getConfig().migrator.redProtectMigrator) {
+            return;
+        }
+
+        int count = 0;
+        try {
+            GriefDefenderPlugin.getInstance().getLogger().info("Starting RedProtect region data migration for world " + world.getProperties().getWorldName() + "...");
+            ConfigurationLoader<CommentedConfigurationNode> regionManager = HoconConfigurationLoader.builder().setPath(redProtectFilePath).build();
+            CommentedConfigurationNode region = regionManager.load();
+            GriefDefenderPlugin.getInstance().getLogger().info("Scanning RedProtect regions in world data file '" + redProtectFilePath + "'...");
+            for (Object key:region.getChildrenMap().keySet()){
+                String rname = key.toString();
+                if (!region.getNode(rname).hasMapChildren()){
+                    continue;
+                }
+                int maxX = region.getNode(rname,"maxX").getInt();
+                int maxY = region.getNode(rname,"maxY").getInt(255);
+                int maxZ = region.getNode(rname,"maxZ").getInt();
+                int minX = region.getNode(rname,"minX").getInt();
+                int minY = region.getNode(rname,"minY").getInt(0);
+                int minZ = region.getNode(rname,"minZ").getInt();
+                List<String> owners = new ArrayList<String>();
+                owners.addAll(region.getNode(rname,"owners").getList(TypeToken.of(String.class)));
+                
+                List<String> members = new ArrayList<String>();
+                members.addAll(region.getNode(rname,"members").getList(TypeToken.of(String.class)));
+                
+                String creator = region.getNode(rname,"creator").getString();             
+                String welcome = region.getNode(rname,"welcome").getString();                 
+
+                // create GP claim data file
+                GriefDefenderPlugin.getInstance().getLogger().info("Migrating RedProtect region data '" + rname + "'...");
+                UUID ownerUniqueId = null;
+                if (validate(creator)) {
+                    try {
+                        // check cache first
+                        ownerUniqueId = PermissionUtil.getInstance().lookupUserUniqueId(creator);
+                        if (ownerUniqueId == null) {
+                            ownerUniqueId = UUID.fromString(getUUID(creator));
+                        }
+                    } catch (Throwable e) {
+                        // assume admin claim
+                    }
+                }
+
+                UUID claimUniqueId = UUID.randomUUID();
+                Location<World> lesserBoundaryCorner = new Location<>(world, minX, minY, minZ);
+                Location<World> greaterBoundaryCorner = new Location<>(world, maxX, maxY, maxZ);
+                Path claimFilePath = gpClaimDataPath.resolve(claimUniqueId.toString());
+                if (!Files.exists(claimFilePath)) {
+                    Files.createFile(claimFilePath);
+                }
+
+                ClaimStorageData claimStorage = new ClaimStorageData(claimFilePath, world.getUniqueId());
+                ClaimDataConfig claimDataConfig = claimStorage.getConfig();
+                claimDataConfig.setName(TextComponent.of(rname));
+                claimDataConfig.setWorldUniqueId(world.getUniqueId());
+                claimDataConfig.setOwnerUniqueId(ownerUniqueId);
+                claimDataConfig.setLesserBoundaryCorner(BlockUtil.getInstance().posToString(lesserBoundaryCorner));
+                claimDataConfig.setGreaterBoundaryCorner(BlockUtil.getInstance().posToString(greaterBoundaryCorner));
+                claimDataConfig.setDateLastActive(Instant.now());
+                claimDataConfig.setType(ownerUniqueId == null ? ClaimTypes.ADMIN : ClaimTypes.BASIC);
+                if (!welcome.equals("")) {
+                    claimDataConfig.setGreeting(TextComponent.of(welcome));
+                }
+                List<String> rpUsers = new ArrayList<>(owners);
+                rpUsers.addAll(members);
+                List<UUID> builders = claimDataConfig.getBuilders();
+                for (String builder : rpUsers) {
+                    if (!validate(builder)) {
+                        continue;
+                    }
+
+                    UUID builderUniqueId = null;
+                    try {
+                        builderUniqueId = PermissionUtil.getInstance().lookupUserUniqueId(creator);
+                        if (builderUniqueId == null) {
+                            builderUniqueId = UUID.fromString(getUUID(builder));
+                        }
+                    } catch (Throwable e) {
+                        GriefDefenderPlugin.getInstance().getLogger().error("Could not locate a valid UUID for user '" + builder + "' in region '" + rname + 
+                                "'. Skipping...");
+                        continue;
+                    }
+                    if (!builders.contains(builderUniqueId) && ownerUniqueId != null && !builderUniqueId.equals(ownerUniqueId)) {
+                        builders.add(builderUniqueId);
+                    }
+                }
+
+                claimDataConfig.setRequiresSave(true);
+                claimStorage.save();
+                GriefDefenderPlugin.getInstance().getLogger().info("Successfully migrated RedProtect region data '" + rname + "' to '" + claimFilePath + "'");
+                count++;
+            }
+            GriefDefenderPlugin.getInstance().getLogger().info("Finished RedProtect region data migration for world '" + world.getProperties().getWorldName() + "'."
+                    + " Migrated a total of " + count + " regions.");
+        } catch (IOException e) {
+            e.printStackTrace();
+        } catch (ObjectMappingException e) {
+            e.printStackTrace();
+        } 
+    }
+
+    private static boolean validate(final String username){
+        Matcher matcher = Pattern.compile(USERNAME_PATTERN).matcher(username);
+        return matcher.matches();
+    }
+
+    // Below taken from https://github.com/FabioZumbi12/Sponge-Redprotect-19/blob/master/src/main/java/br/net/fabiozumbi12/redprotect/MojangUUIDs.java
+    public static String getUUID(String player) {
+        try {
+          URL url = new URL("https://api.mojang.com/users/profiles/minecraft/" + player);
+          BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
+          String line = in.readLine();
+          if (line == null){
+             return null;
+          }
+         // JSONObject jsonProfile = (JSONObject) new JSONParser().parse(line);
+          String name = "";//(String) jsonProfile.get("id");
+          return toUUID(name);
+        } catch (Exception ex) {
+           ex.printStackTrace();
+        }
+        return null;
+    }
+    
+    private static String toUUID(String uuid){
+        return uuid.substring(0, 8) + "-" + uuid.substring(8, 12) + "-"
+                   + uuid.substring(12, 16) + "-" + uuid.substring(16, 20)
+                   + "-" + uuid.substring(20, 32);
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/migrator/WorldGuardMigrator.java b/sponge/src/main/java/com/griefdefender/migrator/WorldGuardMigrator.java
new file mode 100644
index 0000000..ac8538c
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/migrator/WorldGuardMigrator.java
@@ -0,0 +1,793 @@
+/*
+ * This file is part of GriefPrevention, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.migrator;
+
+import com.flowpowered.math.vector.Vector3i;
+import com.google.common.reflect.TypeToken;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.Tristate;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.ClaimResult;
+import com.griefdefender.api.claim.ClaimType;
+import com.griefdefender.api.claim.ClaimTypes;
+import com.griefdefender.api.claim.TrustTypes;
+import com.griefdefender.api.permission.Context;
+import com.griefdefender.api.permission.ContextKeys;
+import com.griefdefender.api.permission.flag.Flags;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.ClaimDataConfig;
+import com.griefdefender.internal.util.BlockUtil;
+import com.griefdefender.permission.GDPermissionManager;
+import com.griefdefender.permission.GDPermissionUser;
+
+import net.kyori.text.TextComponent;
+import net.kyori.text.serializer.legacy.LegacyComponentSerializer;
+import ninja.leaping.configurate.ConfigurationNode;
+import ninja.leaping.configurate.loader.ConfigurationLoader;
+import ninja.leaping.configurate.objectmapping.ObjectMappingException;
+import ninja.leaping.configurate.yaml.YAMLConfigurationLoader;
+import org.spongepowered.api.world.DimensionType;
+import org.spongepowered.api.world.World;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.UUID;
+
+public class WorldGuardMigrator {
+
+    static final Context SOURCE_PLAYER = new Context("source", "player");
+    static final Context SOURCE_TNT = new Context("source", "tnt");
+    static final Context SOURCE_CREEPER = new Context("source", "creeper");
+    static final Context SOURCE_ENDERDRAGON = new Context("source", "enderdragon");
+    static final Context SOURCE_GHAST = new Context("source", "ghast");
+    static final Context SOURCE_ENDERMAN = new Context("source", "enderman");
+    static final Context SOURCE_SNOWMAN = new Context("source", "snowman");
+    static final Context SOURCE_MONSTER = new Context("source", "monster");
+    static final Context SOURCE_WITHER = new Context("source", "wither");
+    static final Context SOURCE_LAVA = new Context("source", "flowing_lava");
+    static final Context SOURCE_WATER = new Context("source", "flowing_water");
+    static final Context SOURCE_LIGHTNING_BOLT = new Context("source", "lightning_bolt");
+    static final Context SOURCE_FALL = new Context("source", "fall");
+    static final Context SOURCE_FIREWORKS = new Context("source", "fireworks");
+
+    // Block States
+    static final Context STATE_FARMLAND_DRY = new Context("state", "moisture:0");
+
+    // Targets
+    static final Context TARGET_BED = new Context("target", "bed");
+    static final Context TARGET_BOAT = new Context("target", "boat");
+    static final Context TARGET_CHEST = new Context("target", "chest");
+    static final Context TARGET_CHORUS_FRUIT = new Context("target", "chorus_fruit");
+    static final Context TARGET_ENDERPEARL = new Context("target", "enderpearl");
+    static final Context TARGET_FARMLAND = new Context("target", "farmland");
+    static final Context TARGET_FLINTANDSTEEL = new Context("target", "flint_and_steel");
+    static final Context TARGET_GRASS= new Context("target", "grass");
+    static final Context TARGET_ITEM_FRAME = new Context("target", "item_frame");
+    static final Context TARGET_MINECART = new Context("target", "minecart");
+    static final Context TARGET_PAINTING = new Context("target", "painting");
+    static final Context TARGET_PLAYER = new Context("target", "player");
+    static final Context TARGET_ICE_FORM = new Context("target", "ice");
+    static final Context TARGET_ICE_MELT = new Context("target", "water");
+    static final Context TARGET_SNOW_LAYER = new Context("target", "snow_layer");
+    static final Context TARGET_TURTLE_EGG = new Context("target", "turtle_egg");
+    static final Context TARGET_VINE = new Context("target", "vine");
+    static final Context TARGET_XP_ORB = new Context("target", "xp_orb");
+    static final Context TARGET_TYPE_ANIMAL = new Context("target", "#animal");
+    static final Context TARGET_TYPE_CROP = new Context("target", "#crop");
+    static final Context TARGET_TYPE_MONSTER = new Context("target", "#monster");
+    static final Context TARGET_TYPE_MUSHROOM = new Context("target", "#mushroom");
+    static final Context TARGET_TYPE_VEHICLE = new Context("target", "#vehicle");
+    static final GDPermissionManager PERMISSION_MANAGER = GDPermissionManager.getInstance();
+
+    public static void migrate(World world) throws FileNotFoundException, ClassNotFoundException {
+        if (!GriefDefenderPlugin.getGlobalConfig().getConfig().migrator.worldGuardMigrator) {
+            return;
+        }
+
+        final DimensionType dimType = world.getDimension().getType();
+        final String worldName = world.getName().toLowerCase();
+        final String dimName = dimType.getName().toLowerCase();
+        final Path path = Paths.get("plugins", "WorldGuard", "worlds", worldName, "regions.yml");
+        List<GDClaim> tempClaimList = new ArrayList<>();
+        int count = 0;
+        try {
+            GriefDefenderPlugin.getInstance().getLogger().info("Starting WorldGuard region data migration for world " + worldName + "...");
+            ConfigurationLoader<ConfigurationNode> regionManager = YAMLConfigurationLoader.builder().setPath(path).build();
+            ConfigurationNode region = regionManager.load().getNode("regions");
+            GriefDefenderPlugin.getInstance().getLogger().info("Scanning WorldGuard regions in world data file '" + path + "'...");
+            for (Object key:region.getChildrenMap().keySet()){
+                String rname = key.toString();
+
+                boolean isWildernessClaim = false;
+                if (rname.equalsIgnoreCase("__global__")) {
+                    isWildernessClaim = true;
+                }
+                if (!region.getNode(rname).hasMapChildren()){
+                    continue;
+                }
+                if (!isWildernessClaim && !region.getNode(rname, "type").getString().equals("cuboid")) {
+                    GriefDefenderPlugin.getInstance().getLogger().info("Unable to migrate region '" + rname + "' as it is not a cuboid. Skipping...");
+                    continue;
+                }
+
+                final Map<Object, ? extends ConfigurationNode> minMap = region.getNode(rname, "min").getChildrenMap();
+                final Map<Object, ? extends ConfigurationNode> maxMap = region.getNode(rname, "max").getChildrenMap();
+                final Map<Object, ? extends ConfigurationNode> flagsMap = region.getNode(rname, "flags").getChildrenMap();
+                final List<UUID> membersList = region.getNode(rname, "members").getNode("unique-ids").getList(TypeToken.of(UUID.class));
+                final List<UUID> ownersList = region.getNode(rname, "owners").getNode("unique-ids").getList(TypeToken.of(UUID.class));
+
+                List<UUID> managers = new ArrayList<UUID>();
+                UUID creator = null;
+                for (UUID uuid : ownersList) {
+                    if (managers.isEmpty()) {
+                        creator = uuid;
+                    } else {
+                        managers.add(uuid);
+                    }
+                }
+
+                // create GP claim data file
+                GriefDefenderPlugin.getInstance().getLogger().info("Migrating WorldGuard region data '" + rname + "'...");
+                GDPermissionUser owner = null;
+                if (!isWildernessClaim) {
+                    try {
+                        // check cache first
+                        owner = PermissionHolderCache.getInstance().getOrCreateUser(creator);
+                    } catch (Throwable e) {
+                        // assume admin claim
+                    }
+                }
+
+                UUID claimUniqueId = isWildernessClaim ? world.getUniqueId() : UUID.randomUUID();
+                ClaimType type = isWildernessClaim ? ClaimTypes.WILDERNESS : owner == null ? ClaimTypes.ADMIN : ClaimTypes.BASIC;
+
+                ClaimDataConfig claimDataConfig = null;
+                GDClaim newClaim = null;
+                if (isWildernessClaim) {
+                    newClaim = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(world.getUniqueId()).getWildernessClaim();
+                } else {
+                    final Vector3i min = new Vector3i(minMap.get("x").getInt(), minMap.get("y").getInt(), minMap.get("z").getInt());
+                    final Vector3i max = new Vector3i(maxMap.get("x").getInt(), maxMap.get("y").getInt(), maxMap.get("z").getInt());
+                    final boolean cuboid = min.getY() == 0 && max.getY() == 256 ? true : false;
+                    newClaim = new GDClaim(world, min, max, claimUniqueId, type, owner == null ? null : owner.getUniqueId(), cuboid);
+                    final ClaimResult claimResult = newClaim.checkArea(false);
+                    if (!claimResult.successful()) {
+                        GriefDefenderPlugin.getInstance().getLogger().info("Could not migrate region '" + rname + "' due to reason: " + claimResult.getResultType());
+                        continue;
+                    }
+
+                    boolean validClaim = true;
+                    Claim parentClaim = null;
+                    Claim childClaim = null;
+                    if (type != ClaimTypes.WILDERNESS) {
+                        for (GDClaim claim : tempClaimList) {
+                            // Search for parent
+                            final ClaimResult result = newClaim.findParent(claim);
+                            if (result.successful()) {
+                                final Claim parent = result.getClaim().get();
+                                if (parent.isSubdivision()) {
+                                    // avoid creating child claim under subdivision
+                                    // instead create admin claim
+                                    break;
+                                }
+                                if (parent.isBasicClaim() || parent.isAdminClaim()) {
+                                    parentClaim = parent;
+                                    if (type == ClaimTypes.BASIC) {
+                                        if (parent.isBasicClaim()) {
+                                            type = ClaimTypes.SUBDIVISION;
+                                            newClaim.setType(type);
+                                        } else {
+                                            type = ClaimTypes.BASIC;
+                                            newClaim.setType(type);
+                                        }
+                                    }
+                                    ((GDClaim) parent).children.add(newClaim);
+                                    newClaim.parent = (GDClaim) parent;
+                                    GriefDefenderPlugin.getInstance().getLogger().info("Found parent region '" + parent.getName().orElse(TextComponent.of("unknown")) + "'. Set current region '" + rname + "' as it's child.");
+                                } else {
+                                    GriefDefenderPlugin.getInstance().getLogger().warn("Could not migrate region '" + rname + "' as it exceeds the maximum level supported by migrator. Skipping...");
+                                    validClaim = false;
+                                }
+                                break;
+                            }
+                            // Search for child
+                            if (claim.isInside(newClaim)) {
+                                if (!claim.getParent().isPresent()) {
+                                    childClaim = claim;
+                                    claim.getClaimStorage().getConfig().setType(ClaimTypes.SUBDIVISION);
+                                    claim.getClaimStorage().getConfig().setParent(newClaim.getUniqueId());
+                                    GriefDefenderPlugin.getInstance().getLogger().info("Found child region '" + claim.getName().orElse(TextComponent.of("unknown")) + "'. Set current region '" + rname + "' as it's parent.");
+                                }
+                            }
+                        }
+                        if (!validClaim) {
+                            continue;
+                        }
+                        tempClaimList.add(newClaim);
+                    }
+
+                    newClaim.initializeClaimData((GDClaim) parentClaim);
+                    if (childClaim != null) {
+                        // Migrate child to parent
+                        ((GDClaim) newClaim).moveChildToParent(newClaim, (GDClaim) childClaim);
+                    }
+                    claimDataConfig = newClaim.getClaimStorage().getConfig();
+                    claimDataConfig.setName(TextComponent.of(rname));
+                    claimDataConfig.setWorldUniqueId(world.getUniqueId());
+                    if (owner != null) {
+                        claimDataConfig.setOwnerUniqueId(owner.getUniqueId());
+                    }
+                    claimDataConfig.setLesserBoundaryCorner(BlockUtil.getInstance().posToString(min));
+                    claimDataConfig.setGreaterBoundaryCorner(BlockUtil.getInstance().posToString(max));
+                    claimDataConfig.setCuboid(cuboid);
+                    claimDataConfig.setDateLastActive(Instant.now());
+                    claimDataConfig.setType(type);
+                }
+                claimDataConfig = newClaim.getClaimStorage().getConfig();
+
+                for (UUID builder : membersList) {
+                    GDPermissionUser builderUser = null;
+                    try {
+                        builderUser = PermissionHolderCache.getInstance().getOrCreateUser(builder);
+                    } catch (Throwable e) {
+                        GriefDefenderPlugin.getInstance().getLogger().warn("Could not locate a valid UUID for user '" + builder + "' in region '" + rname + 
+                                "'. Skipping...");
+                        continue;
+                    }
+                    if (!claimDataConfig.getBuilders().contains(builderUser.getUniqueId()) && owner != null && !builderUser.getUniqueId().equals(owner.getUniqueId())) {
+                        claimDataConfig.getBuilders().add(builderUser.getUniqueId());
+                    }
+                }
+
+                for (UUID manager : managers) {
+                    GDPermissionUser managerUser = null;
+                    try {
+                        managerUser = PermissionHolderCache.getInstance().getOrCreateUser(manager);
+                    } catch (Throwable e) {
+                        GriefDefenderPlugin.getInstance().getLogger().warn("Could not locate a valid UUID for user '" + manager + "' in region '" + rname + 
+                                "'. Skipping...");
+                        continue;
+                    }
+                    if (!claimDataConfig.getManagers().contains(managerUser.getUniqueId()) && owner != null && !managerUser.getUniqueId().equals(owner.getUniqueId())) {
+                        claimDataConfig.getManagers().add(managerUser.getUniqueId());
+                    }
+                }
+                final Set<Context> claimContextSet = new HashSet<>();
+                claimContextSet.add(newClaim.getContext());
+
+                // migrate flags
+                for (Entry<Object, ? extends ConfigurationNode> mapEntry : flagsMap.entrySet()) {
+                    if (!(mapEntry.getKey() instanceof String)) {
+                        continue;
+                    }
+                    final String flag = (String) mapEntry.getKey();
+                    final Set<Context> contexts = new HashSet<>(claimContextSet);
+                    ConfigurationNode valueNode = mapEntry.getValue();
+                    switch (flag) {
+                        case "build":
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_BREAK, Tristate.FALSE, contexts);
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_PLACE, Tristate.FALSE, contexts);
+                                PERMISSION_MANAGER.setFlagPermission(Flags.INTERACT_BLOCK_PRIMARY, Tristate.FALSE, contexts);
+                                PERMISSION_MANAGER.setFlagPermission(Flags.INTERACT_BLOCK_SECONDARY, Tristate.FALSE, contexts);
+                                PERMISSION_MANAGER.setFlagPermission(Flags.INTERACT_ENTITY_PRIMARY, Tristate.FALSE, contexts);
+                                PERMISSION_MANAGER.setFlagPermission(Flags.INTERACT_ENTITY_SECONDARY, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_PLACE, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "interact":
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.INTERACT_BLOCK_PRIMARY, Tristate.FALSE, contexts);
+                                PERMISSION_MANAGER.setFlagPermission(Flags.INTERACT_BLOCK_SECONDARY, Tristate.FALSE, contexts);
+                                PERMISSION_MANAGER.setFlagPermission(Flags.INTERACT_ENTITY_PRIMARY, Tristate.FALSE, contexts);
+                                PERMISSION_MANAGER.setFlagPermission(Flags.INTERACT_ENTITY_SECONDARY, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_PLACE, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "block-break":
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_BREAK, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_BREAK, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "block-place":
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_PLACE, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_PLACE, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "use":
+                            if (valueNode.getString().equals("allow")) {
+                                newClaim.addUserTrust(GriefDefenderPlugin.PUBLIC_UUID, TrustTypes.ACCESSOR);
+                            }
+                            break;
+                        case "damage-animals":
+                            contexts.add(TARGET_TYPE_ANIMAL);
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.ENTITY_DAMAGE, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.ENTITY_DAMAGE, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "chest-access":
+                            contexts.add(TARGET_CHEST);
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.INTERACT_BLOCK_SECONDARY, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.INTERACT_BLOCK_SECONDARY, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "ride":
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.ENTITY_RIDING, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.ENTITY_RIDING, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "pvp":
+                            contexts.add(SOURCE_PLAYER);
+                            contexts.add(TARGET_PLAYER);
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.ENTITY_DAMAGE, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.ENTITY_DAMAGE, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "sleep":
+                            contexts.add(TARGET_BED);
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.INTERACT_BLOCK_SECONDARY, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.INTERACT_BLOCK_SECONDARY, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "tnt":
+                            contexts.add(SOURCE_TNT);
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.EXPLOSION_BLOCK, Tristate.FALSE, contexts);
+                                PERMISSION_MANAGER.setFlagPermission(Flags.EXPLOSION_ENTITY, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.EXPLOSION_BLOCK, Tristate.TRUE, contexts);
+                                PERMISSION_MANAGER.setFlagPermission(Flags.EXPLOSION_ENTITY, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "vehicle-place":
+                            contexts.add(TARGET_TYPE_VEHICLE);
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.INTERACT_BLOCK_SECONDARY, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.INTERACT_BLOCK_SECONDARY, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "lighter":
+                            contexts.add(TARGET_FLINTANDSTEEL);
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.INTERACT_ITEM_SECONDARY, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.INTERACT_ITEM_SECONDARY, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "block-trampling":
+                            contexts.add(TARGET_FARMLAND);
+                            contexts.add(TARGET_TURTLE_EGG);
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.COLLIDE_BLOCK, Tristate.FALSE, contexts);
+                                PERMISSION_MANAGER.setFlagPermission(Flags.COLLIDE_BLOCK, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.COLLIDE_BLOCK, Tristate.TRUE, contexts);
+                                PERMISSION_MANAGER.setFlagPermission(Flags.COLLIDE_BLOCK, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "frosted-ice-form":
+                            break;
+                        case "creeper-explosion":
+                            contexts.add(SOURCE_CREEPER);
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.EXPLOSION_BLOCK, Tristate.FALSE, contexts);
+                                PERMISSION_MANAGER.setFlagPermission(Flags.EXPLOSION_ENTITY, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.EXPLOSION_BLOCK, Tristate.TRUE, contexts);
+                                PERMISSION_MANAGER.setFlagPermission(Flags.EXPLOSION_ENTITY, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "enderdragon-block-damage":
+                            contexts.add(SOURCE_ENDERDRAGON);
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_BREAK, Tristate.FALSE, contexts);
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_MODIFY, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_BREAK, Tristate.TRUE, contexts);
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_MODIFY, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "ghast-fireball":
+                            contexts.add(SOURCE_GHAST);
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_BREAK, Tristate.FALSE, contexts);
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_MODIFY, Tristate.FALSE, contexts);
+                                PERMISSION_MANAGER.setFlagPermission(Flags.ENTITY_DAMAGE, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_BREAK, Tristate.TRUE, contexts);
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_MODIFY, Tristate.TRUE, contexts);
+                                PERMISSION_MANAGER.setFlagPermission(Flags.ENTITY_DAMAGE, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "other-explosion":
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.EXPLOSION_BLOCK, Tristate.FALSE, contexts);
+                                PERMISSION_MANAGER.setFlagPermission(Flags.EXPLOSION_ENTITY, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.EXPLOSION_BLOCK, Tristate.TRUE, contexts);
+                                PERMISSION_MANAGER.setFlagPermission(Flags.EXPLOSION_ENTITY, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "fire-spread":
+                            contexts.add(new Context(ContextKeys.SOURCE, "fire"));
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_SPREAD, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_SPREAD, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "enderman-grief":
+                            contexts.add(SOURCE_ENDERMAN);
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_BREAK, Tristate.FALSE, contexts);
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_MODIFY, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_BREAK, Tristate.TRUE, contexts);
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_MODIFY, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "snowman-trail":
+                            contexts.add(SOURCE_SNOWMAN);
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_MODIFY, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_MODIFY, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "mob-damage": 
+                            contexts.add(TARGET_TYPE_MONSTER);
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.ENTITY_DAMAGE, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.ENTITY_DAMAGE, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "mob-spawning": 
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.ENTITY_SPAWN, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.ENTITY_SPAWN, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "deny-spawn": 
+                            PERMISSION_MANAGER.setFlagPermission(Flags.ENTITY_SPAWN, Tristate.FALSE, contexts);
+                            break;
+                        case "entity-painting-destroy": 
+                            contexts.add(TARGET_PAINTING);
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.ENTITY_DAMAGE, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.ENTITY_DAMAGE, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "entity-item-frame-destroy": 
+                            contexts.add(TARGET_ITEM_FRAME);
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.ENTITY_DAMAGE, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.ENTITY_DAMAGE, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "wither-damage":
+                            contexts.add(SOURCE_WITHER);
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_BREAK, Tristate.FALSE, contexts);
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_MODIFY, Tristate.FALSE, contexts);
+                                PERMISSION_MANAGER.setFlagPermission(Flags.ENTITY_DAMAGE, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_BREAK, Tristate.TRUE, contexts);
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_MODIFY, Tristate.TRUE, contexts);
+                                PERMISSION_MANAGER.setFlagPermission(Flags.ENTITY_DAMAGE, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "lava-fire": 
+                            contexts.add(SOURCE_LAVA);
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_SPREAD, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_SPREAD, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "lightning": 
+                            contexts.add(SOURCE_LIGHTNING_BOLT);
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.ENTITY_DAMAGE, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.ENTITY_DAMAGE, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "water-flow": 
+                            contexts.add(SOURCE_WATER);
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.LIQUID_FLOW, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.LIQUID_FLOW, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "lava-flow": 
+                            contexts.add(SOURCE_LAVA);
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.LIQUID_FLOW, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.LIQUID_FLOW, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "snow-fall": 
+                            contexts.add(TARGET_SNOW_LAYER);
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_PLACE, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_PLACE, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "snow-melt": 
+                            contexts.add(TARGET_SNOW_LAYER);
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_BREAK, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_BREAK, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "ice-form": 
+                            contexts.add(TARGET_ICE_FORM);
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_MODIFY, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_MODIFY, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "ice-melt": 
+                            contexts.add(TARGET_ICE_MELT);
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_MODIFY, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_MODIFY, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "frosted-ice-melt":
+                            break;
+                        case "mushroom-growth":
+                            contexts.add(TARGET_TYPE_MUSHROOM);
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_GROW, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_GROW, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "leaf-decay":
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.LEAF_DECAY, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.LEAF_DECAY, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "grass-growth":
+                            contexts.add(TARGET_GRASS);
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_GROW, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_GROW, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "mycelium-spread":
+                            break;
+                        case "vine-growth":
+                            contexts.add(TARGET_VINE);
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_GROW, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_GROW, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "crop-growth":
+                            contexts.add(TARGET_TYPE_CROP);
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_GROW, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_GROW, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "soil-dry":
+                            contexts.add(STATE_FARMLAND_DRY);
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_MODIFY, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.BLOCK_MODIFY, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "entry":
+                            contexts.add(TARGET_PLAYER);
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.ENTER_CLAIM, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.ENTER_CLAIM, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "exit":
+                            contexts.add(TARGET_PLAYER);
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.EXIT_CLAIM, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.EXIT_CLAIM, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        // These can be handled via GD API
+                        case "exit-override":
+                        case "entry-deny-message":
+                        case "exit-deny-message":
+                        case "notify-enter":
+                        case "notify-exit":
+                            break;
+                        case "greeting":
+                            final String greeting = valueNode.getString();
+                            if (greeting != null && !greeting.equals("")) {
+                                claimDataConfig.setGreeting(LegacyComponentSerializer.legacy().deserialize(greeting, '&'));
+                            }
+                            break;
+                        case "farewell":
+                            final String farewell = valueNode.getString();
+                            if (farewell != null && !farewell.equals("")) {
+                                claimDataConfig.setFarewell(LegacyComponentSerializer.legacy().deserialize(farewell, '&'));
+                            }
+                        case "enderpearl":
+                            contexts.add(TARGET_ENDERPEARL);
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.INTERACT_ITEM_SECONDARY, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.INTERACT_ITEM_SECONDARY, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "chorus-fruit-teleport":
+                            contexts.add(TARGET_CHORUS_FRUIT);
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.INTERACT_ITEM_SECONDARY, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.INTERACT_ITEM_SECONDARY, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "teleport":
+                            final String location = valueNode.getString();
+                            break;
+                        case "spawn":
+                            break;
+                        case "item-pickup":
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.ITEM_PICKUP, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.ITEM_PICKUP, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "item-drop":
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.ITEM_DROP, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.ITEM_DROP, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "exp-drop":
+                            contexts.add(TARGET_XP_ORB);
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.ITEM_DROP, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.ITEM_DROP, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "deny-message":
+                            break;
+                        case "invincible":
+                            contexts.add(TARGET_PLAYER);
+                            if (valueNode.getString().equals("allow")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.ENTITY_DAMAGE, Tristate.FALSE, contexts);
+                            }
+                            break;
+                        case "fall-damage":
+                            contexts.add(SOURCE_FALL);
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.ENTITY_DAMAGE, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.ENTITY_DAMAGE, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "firework-damage":
+                            contexts.add(SOURCE_FIREWORKS);
+                            if (valueNode.getString().equals("deny")) {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.ENTITY_DAMAGE, Tristate.FALSE, contexts);
+                            } else {
+                                PERMISSION_MANAGER.setFlagPermission(Flags.ENTITY_DAMAGE, Tristate.TRUE, contexts);
+                            }
+                            break;
+                        case "game-mode":
+                        case "time-lock":
+                        case "weather-lock":
+                        case "heal-delay":
+                        case "heal-amount":
+                        case "heal-min-health":
+                        case "heal-max-health":
+                        case "feed-delay":
+                        case "feed-amount":
+                        case "feed-min-hunger":
+                        case "feed-max-hunger":
+                            break;
+                        case "blocked-cmds":
+                            List<String> blocked = valueNode.getList(TypeToken.of(String.class));
+                            for (String cmd : blocked) {
+                                final Context context = new Context(ContextKeys.TARGET, cmd);
+                                contexts.add(context);
+                                PERMISSION_MANAGER.setFlagPermission(Flags.COMMAND_EXECUTE, Tristate.FALSE, contexts);
+                                contexts.remove(context);
+                            }
+                            break;
+                        case "allowed-cmds":
+                            List<String> allowed = valueNode.getList(TypeToken.of(String.class));
+                            for (String cmd : allowed) {
+                                final Context context = new Context(ContextKeys.TARGET, cmd);
+                                contexts.add(context);
+                                PERMISSION_MANAGER.setFlagPermission(Flags.COMMAND_EXECUTE, Tristate.TRUE, contexts);
+                                contexts.remove(context);
+                            }
+                    }
+                }
+
+                claimDataConfig.setRequiresSave(true);
+                newClaim.getClaimStorage().save();
+                GriefDefenderPlugin.getInstance().getLogger().info("Successfully migrated WorldGuard region data '" + rname + "' to '" + newClaim.getClaimStorage().filePath + "'");
+                count++;
+            }
+            GriefDefenderPlugin.getInstance().getLogger().info("Finished WorldGuard region data migration for world '" + world.getName() + "'."
+                    + " Migrated a total of " + count + " regions.");
+        } catch (IOException e) {
+            e.printStackTrace();
+        } catch (ObjectMappingException e) {
+            e.printStackTrace();
+        } 
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/permission/ContextGroupKeys.java b/sponge/src/main/java/com/griefdefender/permission/ContextGroupKeys.java
new file mode 100644
index 0000000..309d288
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/permission/ContextGroupKeys.java
@@ -0,0 +1,36 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.permission;
+
+public class ContextGroupKeys {
+
+    public static final String AMBIENT = "#ambient";
+    public static final String ANIMAL = "#animal";
+    public static final String AQUATIC = "#aquatic";
+    public static final String FOOD = "#food";
+    public static final String MISC = "#misc";
+    public static final String MONSTER = "#monster";
+    public static final String VEHICLE = "#vehicle";
+}
diff --git a/sponge/src/main/java/com/griefdefender/permission/ContextGroups.java b/sponge/src/main/java/com/griefdefender/permission/ContextGroups.java
new file mode 100644
index 0000000..07c33c5
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/permission/ContextGroups.java
@@ -0,0 +1,49 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.permission;
+
+import com.griefdefender.api.permission.Context;
+import com.griefdefender.api.permission.ContextKeys;
+
+public class ContextGroups {
+
+    // Entity groups
+    public static final Context SOURCE_AMBIENT = new Context(ContextKeys.SOURCE, ContextGroupKeys.AMBIENT);
+    public static final Context SOURCE_ANIMAL = new Context(ContextKeys.SOURCE, ContextGroupKeys.ANIMAL);
+    public static final Context SOURCE_AQUATIC = new Context(ContextKeys.SOURCE, ContextGroupKeys.AQUATIC);
+    public static final Context SOURCE_MISC = new Context(ContextKeys.SOURCE, ContextGroupKeys.MISC);
+    public static final Context SOURCE_MONSTER = new Context(ContextKeys.SOURCE, ContextGroupKeys.MONSTER);
+    public static final Context SOURCE_VEHICLE = new Context(ContextKeys.SOURCE, ContextGroupKeys.VEHICLE);
+    public static final Context TARGET_AMBIENT = new Context(ContextKeys.TARGET, ContextGroupKeys.AMBIENT);
+    public static final Context TARGET_ANIMAL = new Context(ContextKeys.TARGET, ContextGroupKeys.ANIMAL);
+    public static final Context TARGET_AQUATIC = new Context(ContextKeys.TARGET, ContextGroupKeys.AQUATIC);
+    public static final Context TARGET_MISC = new Context(ContextKeys.TARGET, ContextGroupKeys.MISC);
+    public static final Context TARGET_MONSTER = new Context(ContextKeys.TARGET, ContextGroupKeys.MONSTER);
+    public static final Context TARGET_VEHICLE = new Context(ContextKeys.TARGET, ContextGroupKeys.VEHICLE);
+
+    // Item groups
+    public static final Context SOURCE_FOOD = new Context(ContextKeys.SOURCE, ContextGroupKeys.FOOD);
+    public static final Context TARGET_FOOD = new Context(ContextKeys.TARGET, ContextGroupKeys.FOOD);
+}
diff --git a/sponge/src/main/java/com/griefdefender/permission/GDPermissionGroup.java b/sponge/src/main/java/com/griefdefender/permission/GDPermissionGroup.java
new file mode 100644
index 0000000..444a559
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/permission/GDPermissionGroup.java
@@ -0,0 +1,47 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.permission;
+
+import com.griefdefender.api.Group;
+
+public class GDPermissionGroup extends GDPermissionHolder implements Group {
+
+    private String groupName;
+
+    public GDPermissionGroup(String groupName) {
+        super(groupName);
+        this.groupName = groupName;
+    }
+
+    @Override
+    public String getName() {
+        return this.groupName;
+    }
+
+    @Override
+    public String getFriendlyName() {
+        return this.groupName;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/permission/GDPermissionHolder.java b/sponge/src/main/java/com/griefdefender/permission/GDPermissionHolder.java
new file mode 100644
index 0000000..63cc6eb
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/permission/GDPermissionHolder.java
@@ -0,0 +1,72 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.permission;
+
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.Subject;
+
+
+public class GDPermissionHolder implements Subject {
+
+    private String identifier;
+    private String friendlyName;
+    private Integer hashCode;
+
+    public GDPermissionHolder(String identifier) {
+        this.identifier = identifier;
+        this.friendlyName = identifier;
+    }
+
+    // used for default
+    public GDPermissionHolder(String objectName, String friendlyName) {
+        this.identifier = objectName;
+        this.friendlyName = friendlyName;
+    }
+
+    @Override
+    public String getFriendlyName() {
+        if (this.friendlyName == null) {
+            return this.identifier;
+        }
+        return this.friendlyName;
+    }
+
+    @Override
+    public String getIdentifier() {
+        return this.identifier;
+    }
+
+    public org.spongepowered.api.service.permission.Subject getDefaultUser() {
+        return GriefDefenderPlugin.getInstance().permissionService.getDefaults();
+    }
+
+    @Override
+    public int hashCode() {
+        if (this.hashCode == null) {
+            this.hashCode = 31 * this.identifier.hashCode();
+        }
+        return this.hashCode;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/permission/GDPermissionManager.java b/sponge/src/main/java/com/griefdefender/permission/GDPermissionManager.java
new file mode 100644
index 0000000..8fd1e5c
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/permission/GDPermissionManager.java
@@ -0,0 +1,1429 @@
+/*
+
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.permission;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.reflect.TypeToken;
+import com.google.inject.Singleton;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.GriefDefender;
+import com.griefdefender.api.Subject;
+import com.griefdefender.api.Tristate;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.ClaimContexts;
+import com.griefdefender.api.claim.ClaimType;
+import com.griefdefender.api.claim.TrustType;
+import com.griefdefender.api.claim.TrustTypes;
+import com.griefdefender.api.permission.Context;
+import com.griefdefender.api.permission.ContextKeys;
+import com.griefdefender.api.permission.PermissionManager;
+import com.griefdefender.api.permission.PermissionResult;
+import com.griefdefender.api.permission.ResultTypes;
+import com.griefdefender.api.permission.flag.Flag;
+import com.griefdefender.api.permission.flag.Flags;
+import com.griefdefender.api.permission.option.Option;
+import com.griefdefender.api.permission.option.type.CreateModeType;
+import com.griefdefender.api.permission.option.type.CreateModeTypes;
+import com.griefdefender.cache.EventResultCache;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.command.CommandHelper;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.configuration.category.BanCategory;
+import com.griefdefender.event.GDCauseStackManager;
+import com.griefdefender.event.GDFlagPermissionEvent;
+import com.griefdefender.internal.registry.GDEntityType;
+import com.griefdefender.internal.util.BlockUtil;
+import com.griefdefender.internal.util.NMSUtil;
+import com.griefdefender.registry.FlagRegistryModule;
+import com.griefdefender.registry.OptionRegistryModule;
+import com.griefdefender.util.EntityUtils;
+import com.griefdefender.util.PermissionUtil;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import net.kyori.text.format.TextColor;
+import net.minecraft.entity.item.EntityItem;
+import net.minecraft.entity.player.EntityPlayer;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.spongepowered.api.CatalogType;
+import org.spongepowered.api.Game;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.block.BlockSnapshot;
+import org.spongepowered.api.block.BlockState;
+import org.spongepowered.api.block.BlockType;
+import org.spongepowered.api.block.tileentity.TileEntity;
+import org.spongepowered.api.command.CommandSource;
+import org.spongepowered.api.data.key.Keys;
+import org.spongepowered.api.entity.Entity;
+import org.spongepowered.api.entity.EntityType;
+import org.spongepowered.api.entity.Item;
+import org.spongepowered.api.entity.living.Living;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.entity.living.player.User;
+import org.spongepowered.api.entity.vehicle.Boat;
+import org.spongepowered.api.entity.vehicle.minecart.Minecart;
+import org.spongepowered.api.event.Event;
+import org.spongepowered.api.event.block.NotifyNeighborBlockEvent;
+import org.spongepowered.api.event.cause.EventContextKey;
+import org.spongepowered.api.event.cause.EventContextKeys;
+import org.spongepowered.api.event.cause.entity.damage.source.DamageSource;
+import org.spongepowered.api.event.cause.entity.damage.source.EntityDamageSource;
+import org.spongepowered.api.item.ItemType;
+import org.spongepowered.api.item.inventory.Inventory;
+import org.spongepowered.api.item.inventory.ItemStack;
+import org.spongepowered.api.item.inventory.ItemStackSnapshot;
+import org.spongepowered.api.item.inventory.equipment.EquipmentTypes;
+import org.spongepowered.api.plugin.PluginContainer;
+import org.spongepowered.api.text.Text;
+import org.spongepowered.api.world.LocatableBlock;
+import org.spongepowered.api.world.Location;
+import org.spongepowered.api.world.World;
+import org.spongepowered.common.SpongeImplHooks;
+import org.spongepowered.common.registry.type.entity.EntityTypeRegistryModule;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+@Singleton
+public class GDPermissionManager implements PermissionManager {
+
+    private static final Pattern BLOCKSTATE_PATTERN = Pattern.compile("(?:\\w+=\\w+,)*\\w+=\\w+", Pattern.MULTILINE);
+
+    private static GDPermissionManager instance;
+    public boolean blacklistCheck = false;
+    private Event currentEvent;
+    private Location<World> eventLocation;
+    private GDPermissionHolder eventSubject;
+    private GDPlayerData eventPlayerData;
+    private String eventSourceId = "none";
+    private String eventTargetId = "none";
+    private Set<Context> eventContexts = new HashSet<>();
+    private Component eventMessage;
+    private static final List<EventContextKey> IGNORED_EVENT_CONTEXTS = Arrays.asList(EventContextKeys.CREATOR, 
+            EventContextKeys.FAKE_PLAYER, EventContextKeys.NOTIFIER, EventContextKeys.OWNER, EventContextKeys.PISTON_EXTEND, 
+            EventContextKeys.PISTON_RETRACT, EventContextKeys.SERVICE_MANAGER, EventContextKeys.PLAYER_BREAK, EventContextKeys.SPAWN_TYPE);
+    private static final Pattern PATTERN_META = Pattern.compile("\\.[\\d+]*$");
+    private static final List<Context> CONTEXT_LIST = Arrays.asList(
+            ClaimContexts.ADMIN_DEFAULT_CONTEXT, ClaimContexts.ADMIN_OVERRIDE_CONTEXT,
+            ClaimContexts.BASIC_DEFAULT_CONTEXT, ClaimContexts.BASIC_OVERRIDE_CONTEXT,
+            ClaimContexts.TOWN_DEFAULT_CONTEXT, ClaimContexts.TOWN_OVERRIDE_CONTEXT,
+            ClaimContexts.WILDERNESS_OVERRIDE_CONTEXT, ClaimContexts.WILDERNESS_DEFAULT_CONTEXT);
+
+    private enum BanType {
+        BLOCK,
+        ENTITY,
+        ITEM
+    }
+
+    public GDPermissionHolder getDefaultHolder() {
+        return GriefDefenderPlugin.DEFAULT_HOLDER;
+    }
+
+    @Override
+    public Tristate getActiveFlagPermissionValue(Claim claim, Subject subject, Flag flag, Object source, Object target, Set<Context> contexts, TrustType type, boolean checkOverride) {
+        return getFinalPermission(null, null, contexts, claim, flag.getPermission(), source, target, (GDPermissionHolder) subject, null, checkOverride);
+    }
+
+    public Tristate getFinalPermission(Event event, Location<World> location, Claim claim, String flagPermission, Object source, Object target, GDPermissionHolder permissionHolder) {
+        return getFinalPermission(event, location, claim, flagPermission, source, target, permissionHolder, null, false);
+    }
+
+    public Tristate getFinalPermission(Event event, Location<World> location, Claim claim, String flagPermission, Object source, Object target, Player player) {
+        final GDPermissionHolder permissionHolder = PermissionHolderCache.getInstance().getOrCreateUser(player);
+        return getFinalPermission(event, location, claim, flagPermission, source, target, permissionHolder, null, false);
+    }
+
+    public Tristate getFinalPermission(Event event, Location<World> location, Claim claim, String flagPermission, Object source, Object target, Player player, boolean checkOverride) {
+        final GDPermissionHolder permissionHolder = PermissionHolderCache.getInstance().getOrCreateUser(player);
+        return getFinalPermission(event, location, claim, flagPermission, source, target, permissionHolder, null, checkOverride);
+    }
+
+    public Tristate getFinalPermission(Event event, Location<World> location, Claim claim, String flagPermission, Object source, Object target, GDPermissionHolder permissionHolder, boolean checkOverride) {
+        return getFinalPermission(event, location, claim, flagPermission, source, target, permissionHolder, null, checkOverride);
+    }
+
+    public Tristate getFinalPermission(Event event, Location<World> location, Claim claim, String flagPermission, Object source, Object target, User user) {
+        final GDPermissionHolder permissionHolder = PermissionHolderCache.getInstance().getOrCreateUser(user);
+        return getFinalPermission(event, location, claim, flagPermission, source, target, permissionHolder, null, false);
+    }
+
+    public Tristate getFinalPermission(Event event, Location<World> location, Claim claim, String flagPermission, Object source, Object target, User user, boolean checkOverride) {
+        final GDPermissionHolder permissionHolder = PermissionHolderCache.getInstance().getOrCreateUser(user);
+        return getFinalPermission(event, location, claim, flagPermission, source, target, permissionHolder, null, checkOverride);
+    }
+
+    public Tristate getFinalPermission(Event event, Location<World> location, Claim claim, String flagPermission, Object source, Object target, Player player, TrustType type, boolean checkOverride) {
+        final GDPermissionHolder permissionHolder = PermissionHolderCache.getInstance().getOrCreateUser(player);
+        return getFinalPermission(event, location, claim, flagPermission, source, target, permissionHolder, type, checkOverride);
+    }
+
+    public Tristate getFinalPermission(Event event, Location<World> location, Claim claim, String flagPermission, Object source, Object target, User user, TrustType type, boolean checkOverride) {
+        final GDPermissionHolder permissionHolder = PermissionHolderCache.getInstance().getOrCreateUser(user);
+        return getFinalPermission(event, location, claim, flagPermission, source, target, permissionHolder, type, checkOverride);
+    }
+
+    public Tristate getFinalPermission(Event event, Location<World> location, Claim claim, String flagPermission, Object source, Object target, GDPermissionHolder subject, TrustType type, boolean checkOverride) {
+        final Set<Context> contexts = new HashSet<>();
+        for (Map.Entry<EventContextKey<?>, Object> mapEntry : event.getContext().asMap().entrySet()) {
+            if (IGNORED_EVENT_CONTEXTS.contains(mapEntry.getKey())) {
+                continue;
+            }
+            final String[] parts = mapEntry.getKey().getId().split(":");
+            String contextId = getPermissionIdentifier(mapEntry.getValue(), false);
+            final int index = contextId.indexOf(".");
+            if (index > -1) {
+                contextId = contextId.substring(0, index);
+            }
+            if (contextId.isEmpty()) {
+                continue;
+            }
+            final Context context = new Context(parts[1], contextId);
+            contexts.add(context);
+        }
+        return getFinalPermission(event, location, contexts, claim, flagPermission, source, target, subject, type, checkOverride);
+    }
+
+    public Tristate getFinalPermission(Event event, Location<World> location, Set<Context> contexts, Claim claim, String flagPermission, Object source, Object target, GDPermissionHolder permissionHolder, TrustType type, boolean checkOverride) {
+        if (claim == null) {
+            return Tristate.TRUE;
+        }
+
+        GDPlayerData playerData = null;
+        final GDPermissionUser user = permissionHolder instanceof GDPermissionUser ? (GDPermissionUser) permissionHolder : null;
+        this.eventSubject = null;
+        boolean isFakePlayer = false;
+        if (user != null) {
+            this.eventSubject = user;
+            if (user.getOnlinePlayer() != null) {
+                playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(((GDClaim) claim).getWorldUniqueId(), user.getUniqueId());
+                if (NMSUtil.getInstance().isFakePlayer((Entity) user.getOnlinePlayer())) {
+                    isFakePlayer = true;
+                }
+            }
+        }
+
+        this.currentEvent = event;
+        this.eventLocation = location;
+        // refresh contexts
+        this.eventContexts = new HashSet<>();
+        /*final ItemStackSnapshot usedItem = event.getContext().get(EventContextKeys.USED_ITEM).orElse(null);
+        final DamageType damageType = event.getContext().get(EventContextKeys.DAMAGE_TYPE).orElse(null);
+        if (usedItem != null) {
+            //final String id = getPermissionIdentifier(usedItem);
+            this.eventContexts.add(new Context("used_item", usedItem.getType().getId()));
+        }
+        if (damageType != null) {
+            //final String id = getPermissionIdentifier(damageType);
+            this.eventContexts.add(new Context("damage_type", damageType.getId()));
+        }*/
+        if (isFakePlayer && source instanceof Game) {
+            source = user.getOnlinePlayer();
+        }
+        if (user != null) {
+            if (user.getOnlinePlayer() != null) {
+                this.addPlayerContexts(user.getOnlinePlayer(), contexts);
+            }
+        }
+
+        final Set<Context> sourceContexts = this.getPermissionContexts((GDClaim) claim, source, true);
+        if (sourceContexts == null) {
+            return Tristate.FALSE;
+        }
+
+        final Set<Context> targetContexts = this.getPermissionContexts((GDClaim) claim, target, false);
+        if (targetContexts == null) {
+            return Tristate.FALSE;
+        }
+        contexts.addAll(sourceContexts);
+        contexts.addAll(targetContexts);
+        this.eventContexts = contexts;
+        this.eventPlayerData = playerData;
+
+        String targetPermission = flagPermission;
+        /*if (!targetId.isEmpty()) {
+            //String[] parts = targetId.split(":");
+            //String targetMod = parts[0];
+            // move target meta to end of permission
+            Matcher m = PATTERN_META.matcher(targetId);
+            String targetMeta = "";
+            if (!flagPermission.contains("command-execute")) {
+                if (m.find()) {
+                    targetMeta = m.group(0);
+                    targetId = StringUtils.replace(targetId, targetMeta, "");
+                }
+            }
+            targetPermission += "." + targetId + targetMeta;
+        }*/
+
+        targetPermission = StringUtils.replace(targetPermission, ":", ".");
+        // If player can ignore admin claims and is currently ignoring , allow
+        if (user != null && playerData != null && !playerData.debugClaimPermissions && playerData.canIgnoreClaim(claim)) {
+            return processResult(claim, targetPermission, "ignore", Tristate.TRUE, user);
+        }
+        if (checkOverride) {
+            Tristate override = Tristate.UNDEFINED;
+            // First check for claim flag overrides
+            override = getFlagOverride(claim, permissionHolder == null ? GriefDefenderPlugin.DEFAULT_HOLDER : permissionHolder, playerData, targetPermission);
+            if (override != Tristate.UNDEFINED) {
+                return override;
+            }
+        }
+
+        if (playerData != null && user != null) {
+            if (playerData.debugClaimPermissions) {
+                if (type != null && claim.isUserTrusted(user.getUniqueId(), type)) {
+                    return processResult(claim, targetPermission, type.getName().toLowerCase(), Tristate.TRUE, user);
+                }
+                return getClaimFlagPermission(claim, targetPermission);
+            }
+             // Check for ignoreclaims after override and debug checks
+            if (playerData.canIgnoreClaim(claim)) {
+                return processResult(claim, targetPermission, "ignore", Tristate.TRUE, user);
+            }
+        }
+        if (user != null) {
+            if (type != null) {
+                if (((GDClaim) claim).isUserTrusted(user, type)) {
+                    return processResult(claim, targetPermission, type.getName().toLowerCase(), Tristate.TRUE, permissionHolder);
+                }
+            }
+            return getUserPermission(user, claim, targetPermission);
+        }
+
+        return getClaimFlagPermission(claim, targetPermission);
+    }
+
+    private Tristate getUserPermission(GDPermissionHolder holder, Claim claim, String permission) {
+        final List<Claim> inheritParents = claim.getInheritedParents();
+        final Set<Context> contexts = new HashSet<>();
+        contexts.addAll(this.eventContexts);
+
+        for (Claim parentClaim : inheritParents) {
+            GDClaim parent = (GDClaim) parentClaim;
+            // check parent context
+            contexts.add(parent.getContext());
+            Tristate value = PermissionUtil.getInstance().getPermissionValue((GDClaim) claim, holder, permission, contexts);
+            if (value != Tristate.UNDEFINED) {
+                return processResult(claim, permission, value, holder);
+            }
+
+            contexts.remove(parent.getContext());
+        }
+
+        contexts.add(claim.getContext());
+        contexts.add(claim.getType().getContext());
+        Tristate value = PermissionUtil.getInstance().getPermissionValue((GDClaim) claim, holder, permission, contexts);
+        if (value != Tristate.UNDEFINED) {
+            return processResult(claim, permission, value, holder);
+        }
+
+        if (holder == GriefDefenderPlugin.DEFAULT_HOLDER) {
+            return getFlagDefaultPermission(claim, permission, contexts);
+        }
+
+        return getClaimFlagPermission(claim, permission, contexts);
+    }
+
+    private Tristate getClaimFlagPermission(Claim claim, String permission) {
+        return this.getClaimFlagPermission(claim, permission, new HashSet<>());
+    }
+
+    private Tristate getClaimFlagPermission(Claim claim, String permission, Set<Context> contexts) {
+        if (contexts.isEmpty()) {
+            final List<Claim> inheritParents = claim.getInheritedParents();
+            contexts.addAll(this.eventContexts);
+            for (Claim parentClaim : inheritParents) {
+                GDClaim parent = (GDClaim) parentClaim;
+                // check parent context
+                contexts.add(parent.getContext());
+                Tristate value = PermissionUtil.getInstance().getPermissionValue((GDClaim) claim, GriefDefenderPlugin.DEFAULT_HOLDER, permission, contexts);
+                if (value != Tristate.UNDEFINED) {
+                    return processResult(claim, permission, value, GriefDefenderPlugin.DEFAULT_HOLDER);
+                }
+
+                contexts.remove(parent.getContext());
+            }
+            contexts.add(claim.getContext());
+        }
+
+        Tristate value = PermissionUtil.getInstance().getPermissionValue((GDClaim) claim, GriefDefenderPlugin.DEFAULT_HOLDER, permission, contexts);
+        if (value != Tristate.UNDEFINED) {
+            return processResult(claim, permission, value, GriefDefenderPlugin.DEFAULT_HOLDER);
+        }
+
+        return getFlagDefaultPermission(claim, permission, contexts);
+    }
+
+    // Only uses world and claim type contexts
+    private Tristate getFlagDefaultPermission(Claim claim, String permission, Set<Context> contexts) {
+        contexts.add(claim.getDefaultTypeContext());
+        Tristate value = PermissionUtil.getInstance().getPermissionValue((GDClaim) claim, GriefDefenderPlugin.DEFAULT_HOLDER, permission, contexts);
+        if (value != Tristate.UNDEFINED) {
+            return processResult(claim, permission, value, GriefDefenderPlugin.DEFAULT_HOLDER);
+        }
+        contexts.remove(claim.getDefaultTypeContext());
+        contexts.add(ClaimContexts.GLOBAL_DEFAULT_CONTEXT);
+        value = PermissionUtil.getInstance().getPermissionValue((GDClaim) claim, GriefDefenderPlugin.DEFAULT_HOLDER, permission, contexts);
+        if (value != Tristate.UNDEFINED) {
+            return processResult(claim, permission, value, GriefDefenderPlugin.DEFAULT_HOLDER);
+        }
+
+        return processResult(claim, permission, Tristate.UNDEFINED, GriefDefenderPlugin.DEFAULT_HOLDER);
+    }
+
+    private Tristate getFlagOverride(Claim claim, GDPermissionHolder permissionHolder, GDPlayerData playerData, String flagPermission) {
+        if (!((GDClaim) claim).getInternalClaimData().allowFlagOverrides()) {
+            return Tristate.UNDEFINED;
+        }
+
+        Player player = null;
+        Set<Context> contexts = new HashSet<>();
+        if (claim.isAdminClaim()) {
+            contexts.add(ClaimContexts.ADMIN_OVERRIDE_CONTEXT);
+            //contexts.add(claim.world.getContext());
+        } else if (claim.isTown()) {
+            contexts.add(ClaimContexts.TOWN_OVERRIDE_CONTEXT);
+            //contexts.add(claim.world.getContext());
+        } else if (claim.isBasicClaim()) {
+            contexts.add(ClaimContexts.BASIC_OVERRIDE_CONTEXT);
+            //contexts.add(claim.world.getContext());
+        } else if (claim.isWilderness()) {
+            contexts.add(ClaimContexts.WILDERNESS_OVERRIDE_CONTEXT);
+            player = permissionHolder instanceof GDPermissionUser ? ((GDPermissionUser) permissionHolder).getOnlinePlayer() : null;
+        }
+
+        contexts.add(claim.getOverrideClaimContext());
+        contexts.add(ClaimContexts.GLOBAL_OVERRIDE_CONTEXT);
+        contexts.addAll(this.eventContexts);
+
+        Tristate value = PermissionUtil.getInstance().getPermissionValue((GDClaim) claim, permissionHolder, flagPermission, contexts);
+       /* if (value == Tristate.UNDEFINED) {
+            // Check claim specific override
+            contexts = PermissionUtils.getActiveContexts(subject, playerData, claim);
+            contexts.add(claim.getContext());
+            contexts.add(ClaimContexts.CLAIM_OVERRIDE_CONTEXT);
+            value = subject.getPermissionValue(contexts, flagPermission);
+        }*/
+        if (value != Tristate.UNDEFINED) {
+            if (value == Tristate.FALSE) {
+                this.eventMessage = MessageCache.getInstance().PERMISSION_OVERRIDE_DENY;
+            }
+            return processResult(claim, flagPermission, value, permissionHolder);
+        }
+        if (permissionHolder != GriefDefenderPlugin.DEFAULT_HOLDER) {
+            return getFlagOverride(claim, GriefDefenderPlugin.DEFAULT_HOLDER, playerData, flagPermission);
+        }
+
+        return Tristate.UNDEFINED;
+    }
+
+    public Tristate processResult(Claim claim, String permission, Tristate permissionValue, GDPermissionHolder permissionHolder) {
+        return processResult(claim, permission, null, permissionValue, permissionHolder);
+    }
+
+    public Tristate processResult(Claim claim, String permission, String trust, Tristate permissionValue, GDPermissionHolder permissionHolder) {
+        if (GriefDefenderPlugin.debugActive) {
+            // Use the event subject always if available
+            // This prevents debug showing 'default' for users
+            if (eventSubject != null) {
+                permissionHolder = eventSubject;
+            } else if (permissionHolder == null) {
+                final Object source = GDCauseStackManager.getInstance().getCurrentCause().root();
+                if (source instanceof GDPermissionUser) {
+                    permissionHolder = (GDPermissionUser) source;
+                } else {
+                    permissionHolder = GriefDefenderPlugin.DEFAULT_HOLDER;
+                }
+            }
+
+            if (this.currentEvent != null && (this.currentEvent instanceof NotifyNeighborBlockEvent)) {
+                if (((GDClaim) claim).getWorld().getProperties().getTotalTime() % 100 != 0L) {
+                    return permissionValue;
+                }
+            }
+
+            GriefDefenderPlugin.addEventLogEntry(this.currentEvent, this.eventLocation, this.eventSourceId, this.eventTargetId, permissionHolder, permission, trust, permissionValue);
+        }
+
+        if (eventPlayerData != null && eventPlayerData.eventResultCache != null) {
+            final Flag flag = FlagRegistryModule.getInstance().getById(permission).orElse(null);
+            if (flag != null) {
+                eventPlayerData.eventResultCache = new EventResultCache((GDClaim) claim, flag.getName().toLowerCase(), permissionValue, trust);
+            }
+        }
+        return permissionValue;
+    }
+
+    public void addEventLogEntry(Event event, Location<World> location, Object source, Object target, User user, String permission, String trust, Tristate result) {
+        final GDPermissionHolder holder = PermissionHolderCache.getInstance().getOrCreateUser(user);
+        addEventLogEntry(event, location, source, target, holder, permission, trust, result);
+    }
+
+    // Used for situations where events are skipped for perf reasons
+    public void addEventLogEntry(Event event, Location<World> location, Object source, Object target, GDPermissionHolder permissionSubject, String permission, String trust, Tristate result) {
+        if (GriefDefenderPlugin.debugActive) {
+            String sourceId = getPermissionIdentifier(source, true);
+            String targetPermission = permission;
+            String targetId = getPermissionIdentifier(target);
+            /*if (!targetId.isEmpty()) {
+                // move target meta to end of permission
+                Matcher m = PATTERN_META.matcher(targetId);
+                String targetMeta = "";
+                if (!permission.contains("command-execute")) {
+                    if (m.find()) {
+                        targetMeta = m.group(0);
+                        targetId = StringUtils.replace(targetId, targetMeta, "");
+                    }
+                }
+                targetPermission += "." + targetId + targetMeta;
+            }*/
+            if (permissionSubject == null) {
+                permissionSubject = GriefDefenderPlugin.DEFAULT_HOLDER;
+            }
+            GriefDefenderPlugin.addEventLogEntry(event, location, sourceId, targetId, permissionSubject, targetPermission, trust, result);
+        }
+    }
+
+    public String getPermissionIdentifier(Object obj) {
+        return getPermissionIdentifier(obj, false);
+    }
+
+    public String getPermissionIdentifier(Object obj, boolean isSource) {
+        if (obj != null) {
+            if (obj instanceof Entity) {
+                return populateEventSourceTarget(NMSUtil.getInstance().getEntityId((Entity) obj, isSource), isSource);
+            } else if (obj instanceof EntityType) {
+                final String id = ((EntityType) obj).getId();
+                return populateEventSourceTarget(id, isSource);
+            } else if (obj instanceof BlockType) {
+                final String id = ((BlockType) obj).getId();
+                return populateEventSourceTarget(id, isSource);
+            } else if (obj instanceof BlockSnapshot) {
+                final BlockSnapshot blockSnapshot = (BlockSnapshot) obj;
+                final BlockState blockstate = blockSnapshot.getState();
+                String id = blockstate.getType().getId() + "." + BlockUtil.getInstance().getBlockStateMeta(blockstate);
+                return populateEventSourceTarget(id, isSource);
+            } else if (obj instanceof BlockState) {
+                final BlockState blockstate = (BlockState) obj;
+                final String id = blockstate.getType().getId() + "." + BlockUtil.getInstance().getBlockStateMeta(blockstate);
+                return populateEventSourceTarget(id, isSource);
+            } else if (obj instanceof LocatableBlock) {
+                final LocatableBlock locatableBlock = (LocatableBlock) obj;
+                final BlockState blockstate = locatableBlock.getBlockState();
+                final String id = blockstate.getType().getId() + "." + BlockUtil.getInstance().getBlockStateMeta(blockstate);
+                return populateEventSourceTarget(id, isSource);
+            } else if (obj instanceof TileEntity) {
+                TileEntity tileEntity = (TileEntity) obj;
+                final String id = tileEntity.getType().getId().toLowerCase();
+                return populateEventSourceTarget(id, isSource);
+            } else if (obj instanceof ItemStack) {
+                return populateEventSourceTarget(NMSUtil.getInstance().getItemStackId((ItemStack) obj), isSource);
+            } else if (obj instanceof ItemType) {
+                final String id = ((ItemType) obj).getId().toLowerCase();
+                populateEventSourceTarget(id, isSource);
+                return id;
+            } else if (obj instanceof EntityDamageSource) {
+                final EntityDamageSource damageSource = (EntityDamageSource) obj;
+                Entity sourceEntity = damageSource.getSource();
+
+                if (this.eventSubject == null && sourceEntity instanceof User) {
+                    this.eventSubject = PermissionHolderCache.getInstance().getOrCreateUser((User) sourceEntity);
+                }
+
+                return getPermissionIdentifier(sourceEntity, isSource);
+            } else if (obj instanceof DamageSource) {
+                final DamageSource damageSource = (DamageSource) obj;
+                String id = damageSource.getType().getId();
+                if (!id.contains(":")) {
+                    id = "minecraft:" + id;
+                }
+
+                return populateEventSourceTarget(id, isSource);
+            } else if (obj instanceof ItemStackSnapshot) {
+                final ItemStackSnapshot itemSnapshot = ((ItemStackSnapshot) obj);
+                return getPermissionIdentifier(itemSnapshot.createStack(), isSource);
+            } else if (obj instanceof CatalogType) {
+                final String id = ((CatalogType) obj).getId();
+                return populateEventSourceTarget(id, isSource);
+            } else if (obj instanceof String) {
+                final String id = obj.toString().toLowerCase();
+                return populateEventSourceTarget(id, isSource);
+            } else if (obj instanceof PluginContainer) {
+                final String id = ((PluginContainer) obj).getId();
+                return populateEventSourceTarget(id, isSource);
+            } else if (obj instanceof Inventory) {
+                return ((Inventory) obj).getArchetype().getId();
+            } else if (obj instanceof Location) {
+                return getPermissionIdentifier(((Location) obj).getBlock(), isSource);
+            }
+        }
+
+        populateEventSourceTarget("none", isSource);
+        return "";
+    }
+
+    public Set<Context> getPermissionContexts(GDClaim claim, Object obj, boolean isSource) {
+        final Set<Context> contexts = new HashSet<>();
+        if (obj != null) {
+            if (obj instanceof Entity) {
+
+                Entity targetEntity = (Entity) obj;
+                net.minecraft.entity.Entity mcEntity = null;
+                if (targetEntity instanceof net.minecraft.entity.Entity) {
+                    mcEntity = (net.minecraft.entity.Entity) targetEntity;
+                }
+                String id = "";
+                if (targetEntity.getType() != null) {
+                    id = targetEntity.getType().getId();
+                }
+
+                if (mcEntity != null && id.contains("unknown") && SpongeImplHooks.isFakePlayer(mcEntity)) {
+                    final String modId = SpongeImplHooks.getModIdFromClass(mcEntity.getClass());
+                    id = modId + ":fakeplayer_" + EntityUtils.getFriendlyName(mcEntity).toLowerCase();
+                } else if (id.equals("unknown:unknown") && obj instanceof EntityPlayer) {
+                    id = "minecraft:player";
+                }
+
+                if (mcEntity != null && targetEntity instanceof Living) {
+                    String[] parts = id.split(":");
+                    if (parts.length > 1) {
+                        final String modId = parts[0];
+                        String name = parts[1];
+                        if (modId.equalsIgnoreCase("pixelmon") && modId.equalsIgnoreCase(name)) {
+                            name = EntityUtils.getFriendlyName(mcEntity).toLowerCase();
+                            id = modId + ":" + name;
+                        }
+                    }
+                }
+
+                if (this.isObjectIdBanned(claim, id, BanType.ENTITY)) {
+                    return null;
+                }
+                return populateEventSourceTargetContext(contexts, id, isSource);
+            } else if (obj instanceof EntityType) {
+                final String id = ((EntityType) obj).getId();
+                return populateEventSourceTargetContext(contexts, id, isSource);
+            } else if (obj instanceof BlockType) {
+                final String id = ((BlockType) obj).getId();
+                if (this.isObjectIdBanned(claim, id, BanType.BLOCK)) {
+                    return null;
+                }
+                populateEventSourceTargetContext(contexts, id, isSource);
+            } else if (obj instanceof BlockSnapshot) {
+                final BlockSnapshot blockSnapshot = (BlockSnapshot) obj;
+                final BlockState blockstate = blockSnapshot.getState();
+                return this.getPermissionContexts(claim, blockstate, isSource);
+            } else if (obj instanceof BlockState) {
+                final BlockState blockstate = (BlockState) obj;
+                if (this.isObjectIdBanned(claim, blockstate.getType().getId(), BanType.BLOCK)) {
+                    return null;
+                }
+                final String id = blockstate.getType().getId();
+                contexts.add(new Context("meta", String.valueOf(BlockUtil.getInstance().getBlockStateMeta(blockstate))));
+                this.addBlockPropertyContexts(contexts, blockstate);
+                if (this.isObjectIdBanned(claim, id, BanType.BLOCK)) {
+                    return null;
+                }
+                return populateEventSourceTargetContext(contexts, id, isSource);
+            } else if (obj instanceof LocatableBlock) {
+                final LocatableBlock locatableBlock = (LocatableBlock) obj;
+                final BlockState blockstate = locatableBlock.getBlockState();
+                return this.getPermissionContexts(claim, blockstate, isSource);
+            } else if (obj instanceof TileEntity) {
+                TileEntity tileEntity = (TileEntity) obj;
+                final String id = tileEntity.getType().getId().toLowerCase();
+                return populateEventSourceTargetContext(contexts, id, isSource);
+            } else if (obj instanceof ItemStack) {
+                final ItemStack itemstack = (ItemStack) obj;
+                if (NMSUtil.getInstance().isItemFood(itemstack.getType())) {
+                    if (isSource) {
+                        contexts.add(ContextGroups.SOURCE_FOOD);
+                    } else {
+                        contexts.add(ContextGroups.TARGET_FOOD);
+                    }
+                }
+                if (this.isObjectIdBanned(claim, itemstack.getType().getId(), BanType.ITEM)) {
+                    return null;
+                }
+                final String id = NMSUtil.getInstance().getItemStackId(itemstack);
+                contexts.add(new Context("meta", NMSUtil.getInstance().getItemStackMeta(itemstack)));
+                if (this.isObjectIdBanned(claim, id, BanType.ITEM)) {
+                    return null;
+                }
+                return populateEventSourceTargetContext(contexts, id, isSource);
+            } else if (obj instanceof ItemType) {
+                final String id = ((ItemType) obj).getId().toLowerCase();
+                if (NMSUtil.getInstance().isItemFood(((ItemType) obj))) {
+                    if (isSource) {
+                        contexts.add(ContextGroups.SOURCE_FOOD);
+                    } else {
+                        contexts.add(ContextGroups.TARGET_FOOD);
+                    }
+                }
+                if (this.isObjectIdBanned(claim, id, BanType.ITEM)) {
+                    return null;
+                }
+                return populateEventSourceTargetContext(contexts, id, isSource);
+            } else if (obj instanceof EntityDamageSource) {
+                final EntityDamageSource damageSource = (EntityDamageSource) obj;
+                Entity sourceEntity = damageSource.getSource();
+
+                if (this.eventSubject == null && sourceEntity instanceof User) {
+                    this.eventSubject = PermissionHolderCache.getInstance().getOrCreateUser((User) sourceEntity);
+                }
+
+                return this.getPermissionContexts(claim, sourceEntity, isSource);
+            } else if (obj instanceof DamageSource) {
+                final DamageSource damageSource = (DamageSource) obj;
+                String id = damageSource.getType().getId();
+                if (!id.contains(":")) {
+                    id = "minecraft:" + id;
+                }
+
+                return populateEventSourceTargetContext(contexts, id, isSource);
+            } else if (obj instanceof ItemStackSnapshot) {
+                final ItemStackSnapshot itemSnapshot = ((ItemStackSnapshot) obj);
+                return this.getPermissionContexts(claim, itemSnapshot.createStack(), isSource);
+            } else if (obj instanceof CatalogType) {
+                final String id = ((CatalogType) obj).getId();
+                return populateEventSourceTargetContext(contexts, id, isSource);
+            } else if (obj instanceof String) {
+                final String id = obj.toString().toLowerCase();
+                return populateEventSourceTargetContext(contexts, id, isSource);
+            } else if (obj instanceof PluginContainer) {
+                final String id = ((PluginContainer) obj).getId();
+                return populateEventSourceTargetContext(contexts, id, isSource);
+            } else if (obj instanceof Inventory) {
+                return populateEventSourceTargetContext(contexts, ((Inventory) obj).getArchetype().getId(), isSource);
+            } else if (obj instanceof Location) {
+                return this.getPermissionContexts(claim, ((Location) obj).getBlock(), isSource);
+            }
+        }
+
+        return contexts;
+    }
+
+    public boolean isObjectIdBanned(GDClaim claim, String id, BanType type) {
+        if (id.equalsIgnoreCase("player")) {
+            return false;
+        }
+
+        GDPermissionUser user = null;
+        if (this.eventSubject != null && this.eventSubject instanceof GDPermissionUser) {
+            user = (GDPermissionUser) this.eventSubject;
+            if (user.getInternalPlayerData() != null && user.getInternalPlayerData().canIgnoreClaim(claim)) {
+                return false;
+            }
+        }
+
+        final String permission = StringUtils.replace(id, ":", ".");
+        Component banReason = null;
+        final BanCategory banCategory = GriefDefenderPlugin.getGlobalConfig().getConfig().bans;
+        if (type == BanType.BLOCK) {
+            for (Entry<String, Component> banId : banCategory.getBlockMap().entrySet()) {
+                if (FilenameUtils.wildcardMatch(id, banId.getKey())) {
+                    banReason = GriefDefenderPlugin.getGlobalConfig().getConfig().bans.getBlockBanReason(banId.getKey());
+                    if (banReason != null && banReason.equals(TextComponent.empty())) {
+                        banReason = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.PERMISSION_BAN_BLOCK, 
+                                ImmutableMap.of("id", TextComponent.of(id, TextColor.GOLD)));
+                    }
+                    break;
+                }
+            }
+        } else if (type == BanType.ITEM) {
+            for (Entry<String, Component> banId : banCategory.getItemMap().entrySet()) {
+                if (FilenameUtils.wildcardMatch(id, banId.getKey())) {
+                    banReason = GriefDefenderPlugin.getGlobalConfig().getConfig().bans.getItemBanReason(banId.getKey());
+                    if (banReason != null && banReason.equals(TextComponent.empty())) {
+                        banReason = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.PERMISSION_BAN_ITEM, 
+                                ImmutableMap.of("id", TextComponent.of(id, TextColor.GOLD)));
+                    }
+                }
+            }
+        } else if (type == BanType.ENTITY) {
+            for (Entry<String, Component> banId : banCategory.getEntityMap().entrySet()) {
+                if (FilenameUtils.wildcardMatch(id, banId.getKey())) {
+                    banReason = GriefDefenderPlugin.getGlobalConfig().getConfig().bans.getEntityBanReason(banId.getKey());
+                    if (banReason != null && banReason.equals(TextComponent.empty())) {
+                        banReason = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.PERMISSION_BAN_ENTITY, 
+                                ImmutableMap.of("id", TextComponent.of(id, TextColor.GOLD)));
+                    }
+                }
+            }
+        }
+
+        if (banReason != null && user != null) {
+            final Player player = user.getOnlinePlayer();
+            if (player != null) {
+                if (banReason.equals(TextComponent.empty())) {
+                    banReason = MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.PERMISSION_BAN_BLOCK, 
+                            ImmutableMap.of("id", id));
+                }
+                TextAdapter.sendComponent(player, banReason);
+                this.processResult(claim, permission, "banned", Tristate.FALSE, user);
+                return true;
+            }
+        }
+        if (banReason != null) {
+            // Detected ban
+            this.processResult(claim, permission, "banned", Tristate.FALSE, this.eventSubject);
+            return true;
+        }
+        return false;
+    }
+
+    public void addCustomEntityTypeContexts(Entity targetEntity, Set<Context> contexts, GDEntityType type, boolean isSource) {
+        // check vehicle
+        if (targetEntity instanceof Boat || targetEntity instanceof Minecart) {
+            if (isSource) {
+                contexts.add(ContextGroups.SOURCE_VEHICLE);
+            } else {
+                contexts.add(ContextGroups.TARGET_VEHICLE);
+            }
+        }
+        final String creatureType = type.getEnumCreatureTypeId();
+        if (creatureType == null) {
+            return;
+        }
+
+        final String contextKey = isSource ? "source" : "target";
+        //contexts.add(new Context(contextKey, "#" + creatureType));
+        if (creatureType.contains("animal")) {
+            if (isSource) {
+                contexts.add(ContextGroups.SOURCE_ANIMAL);
+            } else {
+                contexts.add(ContextGroups.TARGET_ANIMAL);
+            }
+        } else if (creatureType.contains("aquatic")) {
+            if (isSource) {
+                contexts.add(ContextGroups.SOURCE_AQUATIC);
+            } else {
+                contexts.add(ContextGroups.TARGET_AQUATIC);
+            }
+        } else if (creatureType.contains("monster")) {
+            if (isSource) {
+                contexts.add(ContextGroups.SOURCE_MONSTER);
+            } else {
+                contexts.add(ContextGroups.TARGET_MONSTER);
+            }
+        }  else if (creatureType.contains("ambient")) {
+            if (isSource) {
+                contexts.add(ContextGroups.SOURCE_AMBIENT);
+            } else {
+                contexts.add(ContextGroups.TARGET_AMBIENT);
+            }
+        } else {
+            if (isSource) {
+                contexts.add(ContextGroups.SOURCE_MISC);
+            } else {
+                contexts.add(ContextGroups.TARGET_MISC);
+            }
+        }
+    }
+
+    private void addPlayerContexts(Player player, Set<Context> contexts) {
+        if(!PermissionUtil.getInstance().containsKey(contexts, "used_item") && NMSUtil.getInstance().getActiveItem(player, this.currentEvent) != null) {
+            final ItemStack stack = NMSUtil.getInstance().getActiveItem(player, this.currentEvent);
+            if (!stack.isEmpty()) {
+                contexts.add(new Context("used_item", getPermissionIdentifier(stack)));
+                final Text displayName = stack.get(Keys.DISPLAY_NAME).orElse(null);
+                if (displayName != null) {
+                    String itemName = displayName.toPlain().replaceAll("[^A-Za-z0-9]", "").toLowerCase();
+                    if (itemName != null && !itemName.isEmpty()) {
+                        if (!itemName.contains(":")) {
+                            itemName = "minecraft:" + itemName;
+                        }
+                        contexts.add(new Context("item_name", itemName));
+                    }
+                }
+            }
+        }
+        final ItemStack helmet = player.getEquipped(EquipmentTypes.HEADWEAR).orElse(null);
+        final ItemStack chestplate = player.getEquipped(EquipmentTypes.CHESTPLATE).orElse(null);
+        final ItemStack leggings = player.getEquipped(EquipmentTypes.LEGGINGS).orElse(null);
+        final ItemStack boots = player.getEquipped(EquipmentTypes.BOOTS).orElse(null);
+        if (helmet != null && !helmet.isEmpty()) {
+            contexts.add(new Context("helmet", getPermissionIdentifier(helmet)));
+        }
+        if (chestplate != null && !chestplate.isEmpty()) {
+            contexts.add(new Context("chestplate", getPermissionIdentifier(chestplate)));
+        }
+        if (leggings != null && !leggings.isEmpty()) {
+            contexts.add(new Context("leggings", getPermissionIdentifier(leggings)));
+        }
+        if (boots != null && !boots.isEmpty()) {
+            contexts.add(new Context("boots", getPermissionIdentifier(boots)));
+        }
+    }
+
+    private Set<Context> addBlockPropertyContexts(Set<Context> contexts, BlockState block) {
+        Matcher matcher = BLOCKSTATE_PATTERN.matcher(block.toString());
+        if (matcher.find()) {
+            final String properties[] = matcher.group(0).split(",");
+            for (String property : properties) {
+                contexts.add(new Context("state", property.replace("=", ":")));
+            }
+        }
+        return contexts;
+    }
+
+    public String getSourcePermission(String flagPermission) {
+        final int index = flagPermission.indexOf(".source.");
+        if (index != -1) {
+            return flagPermission.substring(index + 8);
+        }
+
+        return null;
+    }
+
+    public String getTargetPermission(String flagPermission) {
+        flagPermission = StringUtils.replace(flagPermission, "griefdefender.flag.", "");
+        boolean found = false;
+        for (Flag flag : FlagRegistryModule.getInstance().getAll()) {
+            if (flagPermission.contains(flag.toString() + ".")) {
+                found = true;
+            }
+            flagPermission = StringUtils.replace(flagPermission, flag.toString() + ".", "");
+        }
+        if (!found) {
+            return null;
+        }
+        final int sourceIndex = flagPermission.indexOf(".source.");
+        if (sourceIndex != -1) {
+            flagPermission = StringUtils.replace(flagPermission, flagPermission.substring(sourceIndex, flagPermission.length()), "");
+        }
+
+        return flagPermission;
+    }
+
+    // Used for debugging
+    public String getPermission(Object source, Object target, String flagPermission) {
+        String sourceId = getPermissionIdentifier(source, true);
+        String targetPermission = flagPermission;
+        String targetId = getPermissionIdentifier(target);
+        if (!targetId.isEmpty()) {
+            if (!sourceId.isEmpty()) {
+                // move target meta to end of permission
+                Matcher m = PATTERN_META.matcher(targetId);
+                String targetMeta = "";
+                if (m.find()) {
+                    targetMeta = m.group(0);
+                    targetId = StringUtils.replace(targetId, targetMeta, "");
+                }
+                targetPermission += "." + targetId + ".source." + sourceId + targetMeta;
+            } else {
+                targetPermission += "." + targetId;
+            }
+        }
+        targetPermission = StringUtils.replace(targetPermission, ":", ".");
+        return targetPermission;
+    }
+
+    public String getIdentifierWithoutMeta(String targetId) {
+        Matcher m = PATTERN_META.matcher(targetId);
+        String targetMeta = "";
+        if (m.find()) {
+            targetMeta = m.group(0);
+            targetId = StringUtils.replace(targetId, targetMeta, "");
+        }
+        return targetId;
+    }
+
+    private Set<Context> populateEventSourceTargetContext(Set<Context> contexts, String id, boolean isSource) {
+        if (!id.contains(":")) {
+            id = "minecraft:" + id;
+        }
+        if (isSource) {
+            this.eventSourceId = id.toLowerCase();
+            contexts.add(new Context("source", this.eventSourceId));
+        } else {
+            this.eventTargetId = id.toLowerCase();
+            contexts.add(new Context("target", this.eventTargetId));
+        }
+        return contexts;
+    }
+
+    public String populateEventSourceTarget(String id, boolean isSource) {
+        if (this.blacklistCheck) {
+            return id;
+        }
+
+        if (!id.contains(":")) {
+            id = "minecraft:" + id;
+        }
+        String[] parts = id.split(":");
+        if (parts != null && parts.length == 3) {
+            if (parts[0].equals(parts[1])) {
+                id = parts[1] + ":" + parts[2];
+            }
+        }
+        if (isSource) {
+            this.eventSourceId = id.toLowerCase();
+        } else {
+            this.eventTargetId = id.toLowerCase();
+        }
+
+        return id;
+    }
+
+    @Override
+    public CompletableFuture<PermissionResult> clearAllFlagPermissions(Subject subject) {
+        CompletableFuture<PermissionResult> result = new CompletableFuture<>();
+        if (subject == null) {
+            result.complete(new GDPermissionResult(ResultTypes.SUBJECT_DOES_NOT_EXIST));
+            return result;
+        }
+
+        GDFlagPermissionEvent.ClearAll event = new GDFlagPermissionEvent.ClearAll(subject);
+        GriefDefender.getEventManager().post(event);
+        if (event.cancelled()) {
+            result.complete(new GDPermissionResult(ResultTypes.EVENT_CANCELLED, event.getMessage().orElse(null)));
+            return result;
+        }
+
+        for (Map.Entry<Set<Context>, Map<String, Boolean>> mapEntry : PermissionUtil.getInstance().getPermanentPermissions((GDPermissionHolder) subject).entrySet()) {
+            final Set<Context> contextSet = mapEntry.getKey();
+            for (Context context : contextSet) {
+                if (context.getValue().equals(subject.getIdentifier())) {
+                    PermissionUtil.getInstance().clearPermissions((GDPermissionHolder) subject, context);
+                }
+            }
+        }
+
+        result.complete(new GDPermissionResult(ResultTypes.SUCCESS));
+        return result;
+    }
+
+    @Override
+    public CompletableFuture<PermissionResult> clearFlagPermissions(Set<Context> contexts) {
+        return clearFlagPermissions(GriefDefenderPlugin.DEFAULT_HOLDER, contexts);
+    }
+
+    @Override
+    public CompletableFuture<PermissionResult> clearFlagPermissions(Subject subject, Set<Context> contexts) {
+        CompletableFuture<PermissionResult> result = new CompletableFuture<>();
+        if (subject == null) {
+            result.complete(new GDPermissionResult(ResultTypes.SUBJECT_DOES_NOT_EXIST));
+        }
+
+        GDFlagPermissionEvent.Clear event = new GDFlagPermissionEvent.Clear(subject, contexts);
+        GriefDefender.getEventManager().post(event);
+        if (event.cancelled()) {
+            result.complete(new GDPermissionResult(ResultTypes.EVENT_CANCELLED, event.getMessage().orElse(null)));
+            return result;
+        }
+
+        //contexts.add(((GDClaim) claim).getWorld().getContext());
+        PermissionUtil.getInstance().clearPermissions((GDPermissionHolder) subject, contexts);
+        result.complete(new GDPermissionResult(ResultTypes.SUCCESS));
+        return result;
+    }
+
+    @Override
+    public CompletableFuture<PermissionResult> setFlagPermission(Flag flag, Tristate value, Set<Context> contexts) {
+        return setPermission(GriefDefenderPlugin.DEFAULT_HOLDER, flag, value, contexts);
+    }
+
+    public CompletableFuture<PermissionResult> setPermission(Subject subject, Flag flag, Tristate value, Set<Context> contexts) {
+        CompletableFuture<PermissionResult> result = new CompletableFuture<>();
+
+        GDFlagPermissionEvent.Set event = new GDFlagPermissionEvent.Set(subject, flag, value, contexts);
+        GriefDefender.getEventManager().post(event);
+        if (event.cancelled()) {
+            result.complete(new GDPermissionResult(ResultTypes.EVENT_CANCELLED, event.getMessage().orElse(null)));
+            return result;
+        }
+
+        result.complete(PermissionUtil.getInstance().setPermissionValue((GDPermissionHolder) subject, flag, value, contexts));
+        return result;
+    }
+
+    // internal
+    public CompletableFuture<PermissionResult> setPermission(Claim claim, GDPermissionHolder subject, Flag flag, String target, Tristate value, Set<Context> contexts) {
+        if (target.equalsIgnoreCase("any:any")) {
+            target = "any";
+        }
+
+        CompletableFuture<PermissionResult> result = new CompletableFuture<>();
+        if (flag != Flags.COMMAND_EXECUTE && flag != Flags.COMMAND_EXECUTE_PVP && !target.contains("pixelmon")) {
+            String[] parts = target.split(":");
+            if (!target.startsWith("#") && parts.length > 1 && parts[0].equalsIgnoreCase("minecraft")) {
+                target = parts[1];
+            }
+
+            if (target != null && !GriefDefenderPlugin.ID_MAP.contains(target)) {
+                result.complete(new GDPermissionResult(ResultTypes.TARGET_NOT_VALID));
+                return result;
+            }
+        }
+
+        contexts.add(new Context(ContextKeys.TARGET, target));
+        GDFlagPermissionEvent.Set event = new GDFlagPermissionEvent.Set(subject, flag, value, contexts);
+        GriefDefender.getEventManager().post(event);
+        if (event.cancelled()) {
+            result.complete(new GDPermissionResult(ResultTypes.EVENT_CANCELLED, event.getMessage().orElse(null)));
+            return result;
+        }
+
+        CommandSource commandSource = Sponge.getServer().getConsole();
+        final Object root = GDCauseStackManager.getInstance().getCurrentCause().root();
+        if (root instanceof CommandSource) {
+            commandSource = (CommandSource) root;
+        }
+        result.complete(CommandHelper.addFlagPermission(commandSource, subject, claim, flag, target, value, contexts));
+        return result;
+    }
+
+    @Override
+    public Tristate getFlagPermissionValue(Flag flag, Set<Context> contexts) {
+        return getPermissionValue(GriefDefenderPlugin.DEFAULT_HOLDER, flag, contexts);
+    }
+
+    public Tristate getPermissionValue(GDPermissionHolder subject, Flag flag, Set<Context> contexts) {
+        return PermissionUtil.getInstance().getPermissionValue(subject, flag.getPermission(), contexts);
+    }
+
+    @Override
+    public Map<String, Boolean> getFlagPermissions(Set<Context> contexts) {
+        return getFlagPermissions(GriefDefenderPlugin.DEFAULT_HOLDER, contexts);
+    }
+
+    @Override
+    public Map<String, Boolean> getFlagPermissions(Subject subject, Set<Context> contexts) {
+        if (subject == null) {
+            return new HashMap<>();
+        }
+        return PermissionUtil.getInstance().getPermissions((GDPermissionHolder) subject, contexts);
+    }
+
+    public static GDPermissionManager getInstance() {
+        return instance;
+    }
+
+    static {
+        instance = new GDPermissionManager();
+    }
+
+    @Override
+    public Optional<String> getOptionValue(Option option, Set<Context> contexts) {
+        return Optional.empty();
+    }
+
+    @Override
+    public Optional<String> getOptionValue(Subject subject, Option option, Set<Context> contexts) {
+        return Optional.empty();
+    }
+
+    public <T> T getInternalOptionValue(TypeToken<T> type, User player, Option<T> option) {
+        return getInternalOptionValue(type, player, option, null);
+    }
+
+    public <T> T getInternalOptionValue(TypeToken<T> type, User player, Option<T> option, Claim claim) {
+        final GDPermissionHolder holder = PermissionHolderCache.getInstance().getOrCreateHolder(player.getUniqueId().toString());
+        if (claim != null) {
+            return this.getInternalOptionValue(type, holder, option, claim, claim.getType(), new HashSet<>());
+        }
+        return this.getInternalOptionValue(type, holder, option, (ClaimType) null);
+    }
+
+    public <T> T getInternalOptionValue(TypeToken<T> type, GDPermissionHolder holder, Option<T> option) {
+        return this.getInternalOptionValue(type, holder, option, (ClaimType) null);
+    }
+
+    public <T> T getInternalOptionValue(TypeToken<T> type, GDPermissionHolder holder, Option<T> option, Claim claim) {
+        if (claim != null) {
+            return this.getInternalOptionValue(type, holder, option, claim, claim.getType(), new HashSet<>());
+        }
+        return this.getInternalOptionValue(type, holder, option, (ClaimType) null);
+    }
+
+    public <T> T getInternalOptionValue(TypeToken<T> type, GDPermissionHolder holder, Option<T> option, Claim claim, Set<Context> contexts) {
+        return getInternalOptionValue(type, holder, option, claim, null, contexts);
+    }
+
+    public <T> T getInternalOptionValue(TypeToken<T> type, GDPermissionHolder holder, Option<T> option, ClaimType claimType) {
+        return this.getInternalOptionValue(type, holder, option, null, claimType, new HashSet<>());
+    }
+
+    public <T> T getInternalOptionValue(TypeToken<T> type, GDPermissionHolder holder, Option<T> option, Claim claim, ClaimType claimType, Set<Context> contexts) {
+        if (holder != GriefDefenderPlugin.DEFAULT_HOLDER && holder instanceof GDPermissionUser) {
+            final GDPermissionUser user = (GDPermissionUser) holder;
+            final GDPlayerData playerData = (GDPlayerData) user.getPlayerData();
+            if (playerData != null) {
+                playerData.ignoreActiveContexts = true;
+            }
+            //contexts.addAll(PermissionUtil.getInstance().getActiveContexts(holder));
+            PermissionUtil.getInstance().addActiveContexts(contexts, holder, playerData, claim);
+        }
+
+        if (!option.isGlobal() && (claim != null || claimType != null)) {
+            // check claim
+            if (claim != null) {
+                contexts.add(claim.getContext());
+                String value = PermissionUtil.getInstance().getOptionValue(holder, option, contexts);
+                if (value != null) {
+                    return this.getOptionTypeValue(type, value);
+                }
+                contexts.remove(claim.getContext());
+            }
+
+            // check claim type
+            if (claimType != null) {
+                contexts.add(claimType.getContext());
+                String value = PermissionUtil.getInstance().getOptionValue(holder, option, contexts);
+                if (value != null) {
+                    return this.getOptionTypeValue(type, value);
+                }
+                contexts.remove(claimType.getContext());
+            }
+        }
+
+        String value = PermissionUtil.getInstance().getOptionValue(holder, option, contexts);
+        // Check only active contexts
+        if (value != null) {
+            return this.getOptionTypeValue(type, value);
+        }
+
+        // Check type/global default context
+        if (claimType != null) {
+            contexts.add(claimType.getDefaultContext());
+        }
+        contexts.add(ClaimContexts.GLOBAL_DEFAULT_CONTEXT);
+        value = PermissionUtil.getInstance().getOptionValue(holder, option, contexts);
+        if (value != null) {
+            return this.getOptionTypeValue(type, value);
+        }
+        contexts.remove(ClaimContexts.GLOBAL_DEFAULT_CONTEXT);
+        if (claimType != null) {
+            contexts.remove(claimType.getDefaultContext());
+        }
+
+        // Check global
+        if (holder != GriefDefenderPlugin.DEFAULT_HOLDER) {
+            return getInternalOptionValue(type, GriefDefenderPlugin.DEFAULT_HOLDER, option, claim, claimType, contexts);
+        }
+
+        return option.getDefaultValue();
+    }
+
+    private <T> T getOptionTypeValue(TypeToken<T> type, String value) {
+        if (type.getRawType().isAssignableFrom(Double.class)) {
+            return (T) Double.valueOf(value);
+        }
+        if (type.getRawType().isAssignableFrom(Integer.class)) {
+            if (value.equalsIgnoreCase("undefined")) {
+                return (T) Integer.valueOf(-1);
+            }
+            Integer val  = null;
+            try {
+                val = Integer.valueOf(value);
+            } catch (NumberFormatException e) {
+                return (T) Integer.valueOf(-1);
+            }
+            return (T) Integer.valueOf(value);
+        }
+        if (type.getRawType().isAssignableFrom(String.class)) {
+            return (T) value;
+        }
+        if (type.getRawType().isAssignableFrom(Tristate.class)) {
+            if (value.equalsIgnoreCase("true")) {
+                return (T) Tristate.TRUE;
+            }
+            if (value.equalsIgnoreCase("false")) {
+                return (T) Tristate.FALSE;
+            }
+            int permValue = 0;
+            try {
+                permValue = Integer.parseInt(value);
+            } catch (NumberFormatException e) {
+                
+            }
+            if (permValue == 0) {
+                return (T) Tristate.UNDEFINED;
+            }
+            return (T) (permValue == 1 ? Tristate.TRUE : Tristate.FALSE);
+        }
+        if (type.getRawType().isAssignableFrom(CreateModeType.class)) {
+            if (value.equalsIgnoreCase("undefined")) {
+                return (T) CreateModeTypes.AREA;
+            }
+            int permValue = 0;
+            try {
+                permValue = Integer.parseInt(value);
+            } catch (NumberFormatException e) {
+                
+            }
+            if (permValue == 0) {
+                return (T) CreateModeTypes.AREA;
+            }
+            return (T) (permValue == 1 ? CreateModeTypes.VOLUME : CreateModeTypes.AREA);
+        }
+        if (type.getRawType().isAssignableFrom(Boolean.class)) {
+            return (T) Boolean.valueOf(Boolean.parseBoolean(value));
+        }
+        return (T) value;
+    }
+
+    // Uses passed contexts and only adds active contexts
+    public Double getActualOptionValue(GDPermissionHolder holder, Option option, Claim claim, GDPlayerData playerData, Set<Context> contexts) {
+        if (holder != GriefDefenderPlugin.DEFAULT_HOLDER) {
+            if (playerData != null) {
+                playerData.ignoreActiveContexts = true;
+            }
+            //contexts.addAll(PermissionUtil.getInstance().getActiveContexts(holder));
+            PermissionUtil.getInstance().addActiveContexts(contexts, holder, playerData, claim);
+        }
+
+        final String value = PermissionUtil.getInstance().getOptionValue(holder, option, contexts);
+        if (value != null) {
+            return this.getDoubleValue(value);
+        }
+
+        return Double.valueOf(option.getDefaultValue().toString());
+    }
+
+    private Double getDoubleValue(String option) {
+        if (option == null) {
+            return null;
+        }
+
+        double optionValue = 0.0;
+        try {
+            optionValue = Double.parseDouble(option);
+        } catch (NumberFormatException e) {
+
+        }
+        return optionValue;
+    }
+
+    public Optional<Flag> getFlag(String value) {
+        if (value == null) {
+            return Optional.empty();
+        }
+
+        value = value.replace("griefdefender.flag.", "");
+        String[] parts = value.split("\\.");
+        if (parts.length > 0) {
+            value = parts[0];
+        }
+
+        return FlagRegistryModule.getInstance().getById(value);
+    }
+
+    public Optional<Option> getOption(String value) {
+        if (value == null) {
+            return Optional.empty();
+        }
+
+        value = value.replace("griefdefender.", "");
+        String[] parts = value.split("\\.");
+        if (parts.length > 0) {
+            value = parts[0];
+        }
+
+        return GriefDefender.getRegistry().getType(Option.class, value);
+    }
+
+    public Component getEventMessage() {
+        return this.eventMessage;
+    }
+
+    @Override
+    public CompletableFuture<PermissionResult> clearOptions() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public CompletableFuture<PermissionResult> clearOptions(Set<Context> contexts) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public Tristate getFlagPermissionValue(Flag flag, Subject subject, Set<Context> contexts) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public CompletableFuture<PermissionResult> setFlagPermission(Flag flag, Subject subject, Tristate value,
+            Set<Context> contexts) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public CompletableFuture<PermissionResult> setOption(Option option, String value, Set<Context> contexts) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public CompletableFuture<PermissionResult> setOption(Option option, Subject subject, String value, Set<Context> contexts) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public <T> Optional<T> getOptionValue(TypeToken<T> type, Option<T> option, Set<Context> contexts) {
+        String value = PermissionUtil.getInstance().getOptionValue(GriefDefenderPlugin.DEFAULT_HOLDER, option, contexts);
+        if (value != null) {
+            return Optional.of(this.getOptionTypeValue(type, value));
+        }
+
+        return Optional.empty();
+    }
+
+    @Override
+    public <T> Optional<T> getOptionValue(TypeToken<T> type, Subject subject, Option<T> option, Set<Context> contexts) {
+        String value = PermissionUtil.getInstance().getOptionValue((GDPermissionHolder) subject, option, contexts);
+        if (value != null) {
+            return Optional.of(this.getOptionTypeValue(type, value));
+        }
+
+        return Optional.empty();
+    }
+
+    @Override
+    public <T> T getActiveOptionValue(TypeToken<T> type, Option<T> option, Subject subject, Claim claim,
+            Set<Context> contexts) {
+        return this.getInternalOptionValue(type, (GDPermissionHolder) subject, option, claim, claim.getType(), contexts);
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/permission/GDPermissionResult.java b/sponge/src/main/java/com/griefdefender/permission/GDPermissionResult.java
new file mode 100644
index 0000000..f10d2d0
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/permission/GDPermissionResult.java
@@ -0,0 +1,57 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.permission;
+
+import com.griefdefender.api.permission.PermissionResult;
+import com.griefdefender.api.permission.ResultType;
+import net.kyori.text.Component;
+
+import java.util.Optional;
+
+public class GDPermissionResult implements PermissionResult {
+
+    private final ResultType resultType;
+    private final Component eventMessage;
+
+    public GDPermissionResult(ResultType type) {
+        this(type, null);
+    }
+
+    public GDPermissionResult(ResultType type, Component message) {
+        this.resultType = type;
+        this.eventMessage = message;
+    }
+
+    @Override
+    public ResultType getResultType() {
+        return this.resultType;
+    }
+
+    @Override
+    public Optional<Component> getMessage() {
+        return Optional.ofNullable(this.eventMessage);
+    }
+
+}
diff --git a/sponge/src/main/java/com/griefdefender/permission/GDPermissionUser.java b/sponge/src/main/java/com/griefdefender/permission/GDPermissionUser.java
new file mode 100644
index 0000000..b9ae656
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/permission/GDPermissionUser.java
@@ -0,0 +1,156 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.permission;
+
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.service.user.UserStorageService;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.User;
+import com.griefdefender.api.data.PlayerData;
+
+import java.util.UUID;
+
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+public class GDPermissionUser extends GDPermissionHolder implements User {
+
+    private String userName;
+    private UUID uniqueId;
+    private UUID worldUniqueId;
+    private org.spongepowered.api.entity.living.player.User user;
+    private GDPlayerData playerData;
+
+    public GDPermissionUser(Player player) {
+        super(player.getUniqueId().toString());
+        this.uniqueId = player.getUniqueId();
+        this.worldUniqueId = player.getWorld().getUniqueId();
+        this.user = player;
+        this.userName = player.getName();
+    }
+
+    public GDPermissionUser(org.spongepowered.api.entity.living.player.User user) {
+        super(user.getUniqueId().toString());
+        this.uniqueId = user.getUniqueId();
+        this.userName = user.getName();
+        this.user = user;
+    }
+
+    public GDPermissionUser(UUID uuid, String objectName, String friendlyName) {
+        super(objectName);
+        this.uniqueId = uuid;
+        this.userName = objectName;
+    }
+
+    public GDPermissionUser(UUID uuid) {
+        super(uuid.toString());
+        this.uniqueId = uuid;
+    }
+
+    // Used for Public/World user
+    public GDPermissionUser(UUID uuid, String name) {
+        super(uuid.toString());
+        this.uniqueId = uuid;
+        this.userName = name;
+    }
+
+    public String getName() {
+        if (this.userName == null) {
+            if (this.uniqueId.equals(GriefDefenderPlugin.PUBLIC_UUID)) {
+                this.userName = "public";
+            } else if (this.uniqueId.equals(GriefDefenderPlugin.ADMIN_USER_UUID) || this.uniqueId.equals(GriefDefenderPlugin.WORLD_USER_UUID)) {
+                this.userName = "administrator";
+            } else if (this.user != null) {
+                this.userName = this.user.getName();
+            } else {
+                if (this.user == null) {
+                    this.user = this.getOfflinePlayer();
+                    if (this.user != null) {
+                        this.userName = this.user.getName();
+                        return this.userName;
+                    }
+                }
+                // fallback to LP
+                this.userName = super.getFriendlyName();
+            }
+            if (this.userName == null) {
+                this.userName = "unknown";
+            }
+        }
+
+        return this.userName;
+    }
+
+    public String getFriendlyName() {
+        return this.getName();
+    }
+
+    @Nullable
+    public Player getOnlinePlayer() {
+        return Sponge.getServer().getPlayer(this.uniqueId).orElse(null);
+    }
+
+    public org.spongepowered.api.entity.living.player.User getOfflinePlayer() {
+        final org.spongepowered.api.entity.living.player.User player = this.getOnlinePlayer();
+        if (player != null) {
+            return player;
+        }
+
+        if (this.user == null) {
+            this.user = Sponge.getGame().getServiceManager().provide(UserStorageService.class).get().get(this.uniqueId).orElse(null);
+            if (user != null) {
+                return user;
+            }
+        }
+        return this.user;
+    }
+
+    @Override
+    public UUID getUniqueId() {
+        return this.uniqueId;
+    }
+
+    @Override
+    public PlayerData getPlayerData() {
+        if (this.playerData == null) {
+            if (this.worldUniqueId != null) {
+                this.playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(this.worldUniqueId, this.uniqueId);
+            } else {
+                this.playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreateGlobalPlayerData(this.uniqueId);
+            }
+        }
+        return this.playerData;
+    }
+
+    public GDPlayerData getInternalPlayerData() {
+        return (GDPlayerData) this.getPlayerData();
+    }
+
+    @Override
+    public boolean isOnline() {
+        return this.getOnlinePlayer() != null;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/permission/GDPermissions.java b/sponge/src/main/java/com/griefdefender/permission/GDPermissions.java
new file mode 100644
index 0000000..8f98eb1
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/permission/GDPermissions.java
@@ -0,0 +1,250 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.permission;
+
+import com.griefdefender.api.claim.TrustType;
+import com.griefdefender.api.claim.TrustTypes;
+
+public class GDPermissions {
+
+    // Claims
+    public static final String COMMAND_ABANDON_BASIC = "griefdefender.user.claim.command.abandon.basic";
+    public static final String COMMAND_ABANDON_TOWN = "griefdefender.user.claim.command.abandon.town";
+    public static final String COMMAND_ABANDON_ALL_CLAIMS = "griefdefender.user.claim.command.abandon-all";
+    public static final String COMMAND_ABANDON_TOP_LEVEL_CLAIM = "griefdefender.user.claim.command.abandon-top-level";
+    public static final String COMMAND_CUBOID_CLAIMS = "griefdefender.user.claim.command.cuboid";
+    public static final String COMMAND_CLAIM_LIST = "griefdefender.user.claim.command.list";
+    public static final String COMMAND_BASIC_MODE = "griefdefender.user.claim.command.basic-mode";
+    public static final String COMMAND_GIVE_BLOCKS = "griefdefender.user.claim.command.give.blocks";
+    public static final String COMMAND_GIVE_BOOK = "griefdefender.user.claim.command.give.book";
+    public static final String COMMAND_GIVE_PET = "griefdefender.user.claim.command.give.pet";
+    public static final String COMMAND_CLAIM_BANK = "griefdefender.user.claim.command.bank";
+    public static final String COMMAND_CLAIM_BUY = "griefdefender.user.claim.command.buy";
+    public static final String COMMAND_CLAIM_CONTRACT = "griefdefender.user.claim.command.contract";
+    public static final String COMMAND_CLAIM_EXPAND = "griefdefender.user.claim.command.expand";
+    public static final String COMMAND_CLAIM_INFO_OTHERS = "griefdefender.user.claim.command.info.others";
+    public static final String COMMAND_CLAIM_INFO_BASE = "griefdefender.user.claim.command.info.base";
+    public static final String COMMAND_CLAIM_INFO_TELEPORT_OTHERS = "griefdefender.user.claim.command.info.teleport.others";
+    public static final String COMMAND_CLAIM_INFO_TELEPORT_BASE = "griefdefender.user.claim.command.info.teleport.base";
+    public static final String COMMAND_CLAIM_MODE = "griefdefender.user.claim.command.claim-mode";
+    public static final String COMMAND_CLAIM_OPTIONS_BASE = "griefdefender.user.claim.option.base";
+    public static final String COMMAND_CLAIM_SELL = "griefdefender.user.claim.command.sell";
+    public static final String COMMAND_CLAIM_SPAWN = "griefdefender.user.claim.command.spawn";
+    public static final String COMMAND_CLAIM_SET_SPAWN = "griefdefender.user.claim.command.set-spawn";
+    public static final String COMMAND_CLAIM_TAX = "griefdefender.user.claim.command.claim.tax";
+    public static final String COMMAND_CLAIM_WORLDEDIT = "griefdefender.user.claim.command.worldedit-claim";
+    public static final String COMMAND_SET_CLAIM_NAME = "griefdefender.user.claim.command.name";
+    public static final String COMMAND_SET_CLAIM_FAREWELL = "griefdefender.user.claim.command.farewell";
+    public static final String COMMAND_SET_CLAIM_GREETING = "griefdefender.user.claim.command.greeting";
+    public static final String COMMAND_SUBDIVIDE_CLAIMS = "griefdefender.user.claim.command.subdivide-mode";
+    public static final String COMMAND_TOWN_BANK = "griefdefender.user.town.command.bank";
+    public static final String COMMAND_TOWN_CHAT = "griefdefender.user.town.command.chat";
+    public static final String COMMAND_TOWN_INFO_BASE = "griefdefender.user.town.command.info.base";
+    public static final String COMMAND_TOWN_INFO_OTHERS = "griefdefender.user.town.command.info.others";
+    public static final String COMMAND_TOWN_INFO_TELEPORT_OTHERS = "griefdefender.user.town.command.info.teleport.others";
+    public static final String COMMAND_TOWN_INFO_TELEPORT_BASE = "griefdefender.user.town.command.info.teleport.base";
+    public static final String COMMAND_TOWN_NAME = "griefdefender.user.town.command.name";
+    public static final String COMMAND_TOWN_TAG = "griefdefender.user.town.command.tag";
+    public static final String COMMAND_TOWN_TAX = "griefdefender.user.town.command.tax";
+    public static final String COMMAND_TOWN_MODE = "griefdefender.user.claim.command.town-mode";
+    public static final String COMMAND_TRANSFER_CLAIM = "griefdefender.user.claim.command.transfer";
+    public static final String COMMAND_BUY_CLAIM_BLOCKS = "griefdefender.user.claim.command.buy-blocks";
+    public static final String COMMAND_SELL_CLAIM_BLOCKS = "griefdefender.user.claim.command.sell-blocks";
+    public static final String COMMAND_LIST_CLAIM_FLAGS = "griefdefender.user.claim.command.list-flags";
+    public static final String COMMAND_LIST_CLAIM_OPTIONS = "griefdefender.user.claim.command.list-options";
+    public static final String COMMAND_BAN_ITEM = "griefdefender.user.claim.command.ban-item";
+    public static final String COMMAND_UNBAN_ITEM = "griefdefender.user.claim.command.unban-item";
+    public static final String COMMAND_CLAIM_INHERIT = "griefdefender.user.claim.command.inherit";
+    public static final String CLAIM_CREATE = "griefdefender.user.claim.create.base";
+    public static final String CLAIM_CREATE_BASIC = "griefdefender.user.claim.create.basic";
+    public static final String CLAIM_CREATE_SUBDIVISION = "griefdefender.user.claim.create.subdivision";
+    public static final String CLAIM_CREATE_TOWN = "griefdefender.user.claim.create.town";
+    public static final String CLAIM_CUBOID_BASIC = "griefdefender.user.claim.create.cuboid.basic";
+    public static final String CLAIM_CUBOID_SUBDIVISION = "griefdefender.user.claim.create.cuboid.subdivision";
+    public static final String CLAIM_CUBOID_TOWN = "griefdefender.user.claim.create.cuboid.town";
+    public static final String CLAIM_PVP_OVERRIDE = "griefdefender.user.claim.pvp-override";
+    public static final String CLAIM_RESIZE = "griefdefender.user.claim.resize";
+    public static final String CLAIM_SHOW_TUTORIAL = "griefdefender.user.claim.show-tutorial";
+    public static final String LIST_OTHER_CLAIMS = "griefdefender.user.claim.list.other";
+    public static final String VISUALIZE_CLAIMS = "griefdefender.user.claim.visualize.base";
+    public static final String VISUALIZE_CLAIMS_NEARBY = "griefdefender.user.claim.visualize.nearby";
+    public static final String COMMAND_PLAYER_INFO_BASE = "griefdefender.user.command.info.base";
+    public static final String COMMAND_PLAYER_INFO_OTHERS = "griefdefender.user.command.info.others";
+    public static final String COMMAND_VERSION = "griefdefender.user.command.version";
+
+    // flags
+    public static final String USER_CLAIM_FLAGS = "griefdefender.user.claim.flag";
+    public static final String COMMAND_FLAGS_CLAIM = "griefdefender.user.claim.command.flag.base";
+    public static final String COMMAND_FLAGS_DEBUG = "griefdefender.user.claim.command.flag.debug";
+    public static final String COMMAND_FLAGS_PLAYER = "griefdefender.user.claim.command.flag.player";
+    public static final String COMMAND_FLAGS_GROUP = "griefdefender.user.claim.command.flag.group";
+    public static final String COMMAND_FLAGS_RESET = "griefdefender.user.claim.command.flag.reset";
+
+    public static final String FLAG_CUSTOM_ADMIN_BASE = "griefdefender.admin.custom.flag";
+    public static final String FLAG_CUSTOM_USER_BASE = "griefdefender.user.custom.flag";
+
+    // options
+    public static final String USER_CLAIM_OPTIONS = "griefdefender.user.claim.option";
+    public static final String COMMAND_OPTIONS_CLAIM = "griefdefender.user.claim.command.option.base";
+    public static final String COMMAND_OPTIONS_PLAYER = "griefdefender.user.claim.command.option.player";
+    public static final String COMMAND_OPTIONS_GROUP = "griefdefender.user.claim.command.option.group";
+    public static final String USER_OPTION_PERK_OWNER_FLY_BASIC = "griefdefender.user.option.perk.owner-fly.basic";
+    public static final String USER_OPTION_PERK_OWNER_FLY_TOWN = "griefdefender.user.option.perk.owner-fly.town";
+    public static final String OPTION_BASE = "griefdefender";
+
+    // Admin
+    public static final String ADVANCED_FLAGS = "griefdefender.admin.advanced-flags";
+    public static final String BYPASS_BAN = "griefdefender.admin.bypass.ban";
+    public static final String BYPASS_BORDER_CHECK = "griefdefender.admin.bypass.border-check";
+    public static final String BYPASS_CLAIM_RESIZE = "griefdefender.admin.bypass.override.resize";
+    public static final String BYPASS_CLAIM_LIMIT = "griefdefender.admin.bypass.override.limit";
+    public static final String BYPASS_OPTION = "griefdefender.admin.bypass.option";
+    public static final String CLAIM_CUBOID_ADMIN = "griefdefender.admin.claim.cuboid";
+    public static final String CLAIM_RESIZE_ALL = "griefdefender.admin.claim.resize";
+    public static final String CLAIM_RESIZE_ADMIN = "griefdefender.admin.claim.resize.admin";
+    public static final String CLAIM_RESIZE_ADMIN_SUBDIVISION = "griefdefender.admin.claim.resize.admin.subdivision";
+    public static final String CLAIM_RESIZE_BASIC = "griefdefender.admin.claim.resize.basic";
+    public static final String CLAIM_RESIZE_BASIC_SUBDIVISION = "griefdefender.admin.claim.resize.basic.subdivision";
+    public static final String CLAIM_RESIZE_TOWN = "griefdefender.admin.claim.resize.town";
+    public static final String COMMAND_ADJUST_CLAIM_BLOCKS = "griefdefender.admin.claim.command.adjust-claim-blocks";
+    public static final String COMMAND_ADMIN_CLAIMS = "griefdefender.admin.claim.command.admin-mode";
+    public static final String COMMAND_ADMIN_DEBUG = "griefdefender.admin.claim.command.debug";
+    public static final String COMMAND_CLAIM_BAN = "griefdefender.admin.claim.command.ban";
+    public static final String COMMAND_CLAIM_CLEAR = "griefdefender.admin.claim.command.clear";
+    public static final String COMMAND_CLAIM_PERMISSION_GROUP = "griefdefender.admin.claim.command.permission-group";
+    public static final String COMMAND_CLAIM_PERMISSION_PLAYER = "griefdefender.admin.claim.command.permission-player";
+    public static final String COMMAND_CLAIM_SCHEMATIC = "griefdefender.admin.claim.command.schematic";
+    public static final String COMMAND_CLAIM_OPTIONS_GROUP_BASE = "griefdefender.admin.claim.command.option.group.base";
+    public static final String COMMAND_CLAIM_OPTIONS_GROUP_ADMIN = "griefdefender.admin.claim.command.option.group.admin";
+    public static final String COMMAND_CLAIM_OPTIONS_GROUP_BASIC = "griefdefender.admin.claim.command.option.group.basic";
+    public static final String COMMAND_CLAIM_OPTIONS_GROUP_SUBDIVISION = "griefdefender.admin.claim.command.option.group.subdivision";
+    public static final String COMMAND_CLAIM_OPTIONS_GROUP_TOWN = "griefdefender.admin.claim.command.option.group.town";
+    public static final String COMMAND_CLAIM_OPTIONS_PLAYER_BASE = "griefdefender.admin.claim.command.option.player.base";
+    public static final String COMMAND_CLAIM_OPTIONS_PLAYER_ADMIN = "griefdefender.admin.claim.command.option.player.admin";
+    public static final String COMMAND_CLAIM_OPTIONS_PLAYER_BASIC = "griefdefender.admin.claim.command.option.player.basic";
+    public static final String COMMAND_CLAIM_OPTIONS_PLAYER_SUBDIVISION = "griefdefender.admin.claim.command.option.player.subdivision";
+    public static final String COMMAND_CLAIM_OPTIONS_PLAYER_TOWN = "griefdefender.admin.claim.command.option.player.town";
+    public static final String COMMAND_IGNORE_CLAIMS = "griefdefender.admin.claim.command.ignore.base";
+    public static final String COMMAND_DELETE_CLAIM_BASE = "griefdefender.admin.claim.command.delete.base";
+    public static final String COMMAND_DELETE_CLAIMS = "griefdefender.admin.claim.command.delete-claims";
+    public static final String COMMAND_DELETE_ADMIN_CLAIMS = "griefdefender.admin.command.delete-admin-claims";
+    public static final String COMMAND_SET_ACCRUED_CLAIM_BLOCKS = "griefdefender.admin.command.set-accrued-claim-blocks";
+    public static final String COMMAND_RESTORE_CLAIM = "griefdefender.admin.command.restore-claim.base";
+    public static final String COMMAND_RESTORE_NATURE = "griefdefender.admin.command.restore-nature.base";
+    public static final String COMMAND_RESTORE_NATURE_AGGRESSIVE = "griefdefender.admin.command.restore-nature.aggressive";
+    public static final String COMMAND_RESTORE_NATURE_FILL = "griefdefender.admin.command.restore-nature.fill";
+    public static final String COMMAND_RELOAD = "griefdefender.admin.command.reload";
+    public static final String DELETE_CLAIM_BASIC = "griefdefender.admin.claim.command.delete.basic";
+    public static final String DELETE_CLAIM_ADMIN = "griefdefender.admin.claim.command.delete.admin";
+    public static final String EAVES_DROP_SIGNS = "griefdefender.admin.eavesdrop.signs";
+    public static final String IGNORE_CLAIMS_BASIC = "griefdefender.admin.claim.command.ignore.basic";
+    public static final String IGNORE_CLAIMS_ADMIN = "griefdefender.admin.claim.command.ignore.admin";
+    public static final String IGNORE_CLAIMS_TOWN = "griefdefender.admin.claim.command.ignore.town";
+    public static final String IGNORE_CLAIMS_WILDERNESS = "griefdefender.admin.claim.command.ignore.wilderness";
+    public static final String LIST_ADMIN_CLAIMS = "griefdefender.admin.claim.list.admin";
+    public static final String MANAGE_FLAG_DEFAULTS = "griefdefender.admin.flag-defaults";
+    public static final String MANAGE_FLAG_OVERRIDES = "griefdefender.admin.flag-overrides";
+    public static final String MANAGE_WILDERNESS = "griefdefender.admin.claim.wilderness";
+    public static final String MANAGE_ADMIN_OPTIONS = "griefdefender.admin.claim.option.admin";
+    public static final String MANAGE_GLOBAL_OPTIONS = "griefdefender.admin.claim.option.global";
+    public static final String MANAGE_OVERRIDE_OPTIONS = "griefdefender.admin.claim.option.override";
+    public static final String SET_ADMIN_FLAGS = "griefdefender.admin.claim.set-admin-flags";
+
+    // Misc
+    public static final String COMMAND_HELP = "griefdefender.user.command.help";
+
+    // Trust
+    public static final String COMMAND_TRUST_GROUP = "griefdefender.user.claim.command.trust.group";
+    public static final String COMMAND_TRUST_PLAYER = "griefdefender.user.claim.command.trust.player";
+    public static final String COMMAND_LIST_TRUST = "griefdefender.user.claim.command.trust.list";
+    public static final String COMMAND_TRUSTALL_GROUP = "griefdefender.user.claim.command.trustall.group";
+    public static final String COMMAND_TRUSTALL_PLAYER = "griefdefender.user.claim.command.trustall.player";
+    public static final String COMMAND_UNTRUST_GROUP = "griefdefender.user.claim.command.untrust.group";
+    public static final String COMMAND_UNTRUST_PLAYER = "griefdefender.user.claim.command.untrust.player";
+    public static final String COMMAND_UNTRUSTALL_GROUP = "griefdefender.user.claim.command.untrustall.group";
+    public static final String COMMAND_UNTRUSTALL_PLAYER = "griefdefender.user.claim.command.untrustall.player";
+    public static final String GIVE_ACCESS_TRUST = "griefdefender.user.claim.trust.accessor";
+    public static final String GIVE_CONTAINER_TRUST = "griefdefender.user.claim.trust.container";
+    public static final String GIVE_BUILDER_TRUST = "griefdefender.user.claim.trust.builder";
+    public static final String GIVE_MANAGER_TRUST = "griefdefender.user.claim.trust.manager";
+    public static final String REMOVE_TRUST = "griefdefender.user.claim.trust.remove";
+    public static final String TRUST_ACCESSOR = "griefdefender.trust.1.2.3.4";
+    public static final String TRUST_CONTAINER = "griefdefender.trust.1.2.3";
+    public static final String TRUST_BUILDER = "griefdefender.trust.1.2";
+    public static final String TRUST_MANAGER = "griefdefender.trust.1";
+
+    // Flags
+    public static final String BLOCK_BREAK = "griefdefender.flag.block-break";
+    public static final String BLOCK_GROW = "griefdefender.flag.block-grow";
+    public static final String BLOCK_MODIFY = "griefdefender.flag.block-modify";
+    public static final String BLOCK_PLACE = "griefdefender.flag.block-place";
+    public static final String BLOCK_SPREAD = "griefdefender.flag.block-spread";
+    public static final String COLLIDE_BLOCK = "griefdefender.flag.collide-block";
+    public static final String COLLIDE_ENTITY = "griefdefender.flag.collide-entity";
+    public static final String COMMAND_EXECUTE = "griefdefender.flag.command-execute";
+    public static final String COMMAND_EXECUTE_PVP = "griefdefender.flag.command-execute-pvp";
+    public static final String ENTER_CLAIM = "griefdefender.flag.enter-claim";
+    public static final String ENTITY_CHUNK_SPAWN = "griefdefender.flag.entity-chunk-spawn";
+    public static final String ENTITY_DAMAGE = "griefdefender.flag.entity-damage";
+    public static final String ENTITY_RIDING = "griefdefender.flag.entity-riding";
+    public static final String ENTITY_SPAWN = "griefdefender.flag.entity-spawn";
+    public static final String ENTITY_TELEPORT_FROM = "griefdefender.flag.entity-teleport-from";
+    public static final String ENTITY_TELEPORT_TO = "griefdefender.flag.entity-teleport-to";
+    public static final String EXIT_CLAIM = "griefdefender.flag.exit-claim";
+    public static final String EXPLOSION_BLOCK = "griefdefender.flag.explosion-block";
+    public static final String EXPLOSION_ENTITY = "griefdefender.flag.explosion-entity";
+    public static final String FLAG_BASE = "griefdefender.flag";
+    public static final String INTERACT_BLOCK_PRIMARY = "griefdefender.flag.interact-block-primary";
+    public static final String INTERACT_BLOCK_SECONDARY = "griefdefender.flag.interact-block-secondary";
+    public static final String INTERACT_ENTITY_PRIMARY = "griefdefender.flag.interact-entity-primary";
+    public static final String INTERACT_ENTITY_SECONDARY = "griefdefender.flag.interact-entity-secondary";
+    public static final String INTERACT_ITEM_PRIMARY = "griefdefender.flag.interact-item-primary";
+    public static final String INTERACT_ITEM_SECONDARY = "griefdefender.flag.interact-item-secondary";
+    public static final String INVENTORY_CLICK = "griefdefender.flag.interact-inventory-click";
+    public static final String INVENTORY_OPEN = "griefdefender.flag.interact-inventory";
+    public static final String ITEM_DROP = "griefdefender.flag.item-drop";
+    public static final String ITEM_PICKUP = "griefdefender.flag.item-pickup";
+    public static final String ITEM_SPAWN = "griefdefender.flag.item-spawn";
+    public static final String ITEM_USE = "griefdefender.flag.item-use";
+    public static final String LEAF_DECAY = "griefdefender.flag.leaf-decay";
+    public static final String LIQUID_FLOW = "griefdefender.flag.liquid-flow";
+    public static final String PORTAL_USE = "griefdefender.flag.portal-use";
+    public static final String PROJECTILE_IMPACT_BLOCK = "griefdefender.flag.projectile-impact-block";
+    public static final String PROJECTILE_IMPACT_ENTITY = "griefdefender.flag.projectile-impact-entity";
+
+    public static String getTrustPermission(TrustType type) {
+        if (type == TrustTypes.ACCESSOR) {
+            return GDPermissions.TRUST_ACCESSOR;
+        }
+        if (type == TrustTypes.BUILDER) {
+            return GDPermissions.TRUST_BUILDER;
+        }
+        if (type == TrustTypes.CONTAINER) {
+            return GDPermissions.TRUST_CONTAINER;
+        }
+
+        return GDPermissions.TRUST_MANAGER;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/permission/GDResultType.java b/sponge/src/main/java/com/griefdefender/permission/GDResultType.java
new file mode 100644
index 0000000..d0668a3
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/permission/GDResultType.java
@@ -0,0 +1,31 @@
+package com.griefdefender.permission;
+
+import com.griefdefender.api.permission.ResultType;
+import net.kyori.text.Component;
+
+public class GDResultType implements ResultType {
+
+    private final String id;
+    private final String name;
+    private Component description;
+
+    public GDResultType(String id, String name) {
+        this.id = id;
+        this.name = name;
+    }
+
+    @Override
+    public String getId() {
+        return this.id;
+    }
+
+    @Override
+    public String getName() {
+        return this.name;
+    }
+
+    @Override
+    public Component getDescription() {
+        return this.description;
+    }
+}
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/permission/flag/CustomFlagData.java b/sponge/src/main/java/com/griefdefender/permission/flag/CustomFlagData.java
new file mode 100644
index 0000000..9d98f13
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/permission/flag/CustomFlagData.java
@@ -0,0 +1,65 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.permission.flag;
+
+import java.util.Set;
+
+import com.griefdefender.api.permission.Context;
+import com.griefdefender.api.permission.flag.Flag;
+
+public class CustomFlagData {
+
+    private Flag flag;
+    private Set<Context> contexts;
+
+    public CustomFlagData(Flag flag, Set<Context> contexts) {
+        this.flag = flag;
+        this.contexts = contexts;
+    }
+
+    public Set<Context> getContexts() {
+        return this.contexts;
+    }
+
+    public Flag getFlag() {
+        return this.flag;
+    }
+
+    public boolean matches(Flag otherFlag, Set<Context> otherContexts) {
+        for (Context context : this.contexts) {
+            boolean found = false;
+            for (Context other : otherContexts) {
+                if (other.getKey().equals(context.getKey())) {
+                    found = true;
+                    break;
+                }
+            }
+            if (!found) {
+                return false;
+            }
+        }
+        return true;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/permission/flag/FlagContexts.java b/sponge/src/main/java/com/griefdefender/permission/flag/FlagContexts.java
new file mode 100644
index 0000000..047266a
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/permission/flag/FlagContexts.java
@@ -0,0 +1,82 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.permission.flag;
+
+import com.griefdefender.api.permission.Context;
+import com.griefdefender.api.permission.ContextKeys;
+
+public class FlagContexts {
+
+    public static final Context SOURCE_PLAYER = new Context(ContextKeys.SOURCE, "minecraft:player");
+    public static final Context SOURCE_TNT = new Context(ContextKeys.SOURCE, "minecraft:tnt");
+    public static final Context SOURCE_CREEPER = new Context(ContextKeys.SOURCE, "minecraft:creeper");
+    public static final Context SOURCE_ENDERDRAGON = new Context(ContextKeys.SOURCE, "minecraft:enderdragon");
+    public static final Context SOURCE_GHAST = new Context(ContextKeys.SOURCE, "minecraft:ghast");
+    public static final Context SOURCE_ENDERMAN = new Context(ContextKeys.SOURCE, "minecraft:enderman");
+    public static final Context SOURCE_SNOWMAN = new Context(ContextKeys.SOURCE, "minecraft:snowman");
+    public static final Context SOURCE_WITHER = new Context(ContextKeys.SOURCE, "minecraft:wither");
+    public static final Context SOURCE_LAVA = new Context(ContextKeys.SOURCE, "minecraft:flowing_lava");
+    public static final Context SOURCE_WATER = new Context(ContextKeys.SOURCE, "minecraft:flowing_water");
+    public static final Context SOURCE_LIGHTNING_BOLT = new Context(ContextKeys.SOURCE, "minecraft:lightning_bolt");
+    public static final Context SOURCE_FALL = new Context(ContextKeys.SOURCE, "minecraft:fall");
+    public static final Context SOURCE_FIRE = new Context(ContextKeys.SOURCE, "minecraft:fire");
+    public static final Context SOURCE_FIREWORKS = new Context(ContextKeys.SOURCE, "minecraft:fireworks");
+    public static final Context SOURCE_PISTON = new Context(ContextKeys.TARGET, "minecraft:piston");
+    public static final Context SOURCE_VINE = new Context(ContextKeys.SOURCE, "minecraft:vine");
+    public static final Context SOURCE_TYPE_MONSTER = new Context(ContextKeys.SOURCE, "#monster");
+
+    // Block States
+    public static final Context STATE_FARMLAND_DRY = new Context("state", "moisture:0");
+
+    // Targets
+    public static final Context TARGET_BED = new Context(ContextKeys.TARGET, "minecraft:bed");
+    public static final Context TARGET_BOAT = new Context(ContextKeys.TARGET, "minecraft:boat");
+    public static final Context TARGET_CHEST = new Context(ContextKeys.TARGET, "minecraft:chest");
+    public static final Context TARGET_CHORUS_FRUIT = new Context(ContextKeys.TARGET, "minecraft:chorus_fruit");
+    public static final Context TARGET_ENDERPEARL = new Context(ContextKeys.TARGET, "minecraft:enderpearl");
+    public static final Context TARGET_FARMLAND = new Context(ContextKeys.TARGET, "minecraft:farmland");
+    public static final Context TARGET_FLINTANDSTEEL = new Context(ContextKeys.TARGET, "minecraft:flint_and_steel");
+    public static final Context TARGET_GRASS= new Context(ContextKeys.TARGET, "minecraft:grass");
+    public static final Context TARGET_ITEM_FRAME = new Context(ContextKeys.TARGET, "minecraft:item_frame");
+    public static final Context TARGET_MINECART = new Context(ContextKeys.TARGET, "minecraft:minecart");
+    public static final Context TARGET_MYCELIUM = new Context(ContextKeys.TARGET, "minecraft:mycelium");
+    public static final Context TARGET_PAINTING = new Context(ContextKeys.TARGET, "minecraft:painting");
+    public static final Context TARGET_PISTON = new Context(ContextKeys.TARGET, "minecraft:piston");
+    public static final Context TARGET_PLAYER = new Context(ContextKeys.TARGET, "minecraft:player");
+    public static final Context TARGET_ICE_FORM = new Context(ContextKeys.TARGET, "minecraft:ice");
+    public static final Context TARGET_ICE_MELT = new Context(ContextKeys.TARGET, "minecraft:water");
+    public static final Context TARGET_SNOW_LAYER = new Context(ContextKeys.TARGET, "minecraft:snow_layer");
+    public static final Context TARGET_TURTLE_EGG = new Context(ContextKeys.TARGET, "minecraft:turtle_egg");
+    public static final Context TARGET_VINE = new Context(ContextKeys.TARGET, "minecraft:vine");
+    public static final Context TARGET_XP_ORB = new Context(ContextKeys.TARGET, "minecraft:xp_orb");
+    public static final Context TARGET_TYPE_ANIMAL = new Context(ContextKeys.TARGET, "#animal");
+    public static final Context TARGET_TYPE_CROP = new Context(ContextKeys.TARGET, "#crop");
+    public static final Context TARGET_TYPE_AMBIENT = new Context(ContextKeys.TARGET, "#ambient");
+    public static final Context TARGET_TYPE_AQUATIC = new Context(ContextKeys.TARGET, "#aquatic");
+    public static final Context TARGET_TYPE_MONSTER = new Context(ContextKeys.TARGET, "#monster");
+    public static final Context TARGET_TYPE_MUSHROOM = new Context(ContextKeys.TARGET, "#mushroom");
+    public static final Context TARGET_TYPE_PORTAL = new Context(ContextKeys.TARGET, "#portal");
+    public static final Context TARGET_TYPE_VEHICLE = new Context(ContextKeys.TARGET, "#vehicle");
+}
diff --git a/sponge/src/main/java/com/griefdefender/permission/flag/GDActiveFlagData.java b/sponge/src/main/java/com/griefdefender/permission/flag/GDActiveFlagData.java
new file mode 100644
index 0000000..b556b4e
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/permission/flag/GDActiveFlagData.java
@@ -0,0 +1,94 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.permission.flag;
+
+import com.griefdefender.api.Tristate;
+import com.griefdefender.api.permission.Context;
+
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.format.TextColor;
+
+public class GDActiveFlagData {
+
+    public enum Type {
+        CLAIM,
+        DEFAULT,
+        OVERRIDE,
+        UNDEFINED
+    }
+
+    private final CustomFlagData flagData;
+    private final Tristate value;
+    private final Type type;
+
+    public GDActiveFlagData(CustomFlagData flagData, Tristate value, Type type) {
+        this.flagData = flagData;
+        this.value = value;
+        this.type = type;
+    }
+
+    public CustomFlagData getFlagData() {
+        return this.flagData;
+    }
+
+    public Tristate getValue() {
+        return this.value;
+    }
+
+    public TextColor getColor() {
+        if (this.type == Type.CLAIM) {
+            return TextColor.YELLOW;
+        }
+        if (this.type == Type.OVERRIDE) {
+            return TextColor.RED;
+        }
+        if (this.type == Type.DEFAULT) {
+            return TextColor.LIGHT_PURPLE;
+        }
+        return TextColor.GRAY;
+    }
+
+    public Component getComponent() {
+        TextComponent.Builder contextBuilder = TextComponent.builder();
+        int count = 0;
+        for (Context context : this.flagData.getContexts()) {
+            if (count > 0) {
+                contextBuilder.append(", ");
+            }
+            contextBuilder.append(context.getKey().replace("gd_claim_", "").replace("gd_claim", ""), TextColor.GREEN)
+                .append("=")
+                .append(context.getValue(), TextColor.GRAY);
+        }
+        TextComponent.Builder builder = TextComponent.builder();
+        builder
+            .append(this.flagData.getFlag().getName().toLowerCase(), this.getColor())
+            .append("=", TextColor.WHITE)
+            .append(this.value.toString().toLowerCase(), TextColor.GOLD)
+            .append(" ")
+            .append(contextBuilder.build());
+        return builder.build();
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/permission/flag/GDCustomFlagDefinition.java b/sponge/src/main/java/com/griefdefender/permission/flag/GDCustomFlagDefinition.java
new file mode 100644
index 0000000..894fd1b
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/permission/flag/GDCustomFlagDefinition.java
@@ -0,0 +1,108 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.permission.flag;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.Tristate;
+import com.griefdefender.api.permission.Context;
+import com.griefdefender.api.permission.PermissionResult;
+import com.griefdefender.api.permission.flag.Flag;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.category.CustomFlagGroupCategory;
+
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.format.TextColor;
+
+public class GDCustomFlagDefinition {
+
+    private boolean enabled = true;
+    private Set<Context> definitionContexts = new HashSet<>();
+    private List<CustomFlagData> data = new ArrayList<>();
+    private String displayName;
+    private Tristate defaultValue = Tristate.UNDEFINED;
+    private Component description;
+
+    public GDCustomFlagDefinition(Flag flag, Set<Context> contexts, String displayName, Component description) {
+        this.data.add(new CustomFlagData(flag, contexts));
+        this.displayName = displayName;
+        this.description = description;
+    }
+
+    public GDCustomFlagDefinition(List<CustomFlagData> flagData, String displayName, Component description) {
+        this.data = flagData;
+        this.displayName = displayName;
+        this.description = description;
+    }
+
+    public void addFlagData(Flag flag, Set<Context> contexts) {
+        this.data.add(new CustomFlagData(flag, contexts));
+    }
+
+    public List<Flag> getFlags() {
+        List<Flag> flags = new ArrayList<>();
+        for (CustomFlagData flagData : this.data) {
+            flags.add(flagData.getFlag());
+        }
+        return flags;
+    }
+
+    public List<CustomFlagData> getFlagData() {
+        return this.data;
+    }
+
+    public Component getDescription() {
+        return this.description;
+    }
+
+    public Set<Context> getDefinitionContexts() {
+        return this.definitionContexts;
+    }
+
+    public void setDefinitionContexts(Set<Context> contexts) {
+        this.definitionContexts = contexts;
+    }
+
+    public String getDisplayName() {
+        return this.displayName;
+    }
+
+    public boolean isEnabled() {
+        return this.enabled;
+    }
+
+    public void setDefaultValue(Tristate value) {
+        this.defaultValue = value;
+    }
+
+    public void setIsEnabled(boolean val) {
+        this.enabled = val;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/permission/flag/GDCustomFlagDefinitions.java b/sponge/src/main/java/com/griefdefender/permission/flag/GDCustomFlagDefinitions.java
new file mode 100644
index 0000000..4e4a1fd
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/permission/flag/GDCustomFlagDefinitions.java
@@ -0,0 +1,415 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.permission.flag;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import com.griefdefender.api.Tristate;
+import com.griefdefender.api.claim.ClaimContexts;
+import com.griefdefender.api.permission.Context;
+import com.griefdefender.api.permission.flag.Flag;
+import com.griefdefender.api.permission.flag.Flags;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.event.GDCauseStackManager;
+
+public class GDCustomFlagDefinitions {
+
+    // ADMIN
+    public static final GDCustomFlagDefinition BLOCK_BREAK;
+    public static final GDCustomFlagDefinition BLOCK_GROW;
+    public static final GDCustomFlagDefinition BLOCK_PLACE;
+    public static final GDCustomFlagDefinition BLOCK_SPREAD;
+    public static final GDCustomFlagDefinition ENDERPEARL;
+    public static final GDCustomFlagDefinition EXIT_PLAYER;
+    public static final GDCustomFlagDefinition EXPLOSION_BLOCK;
+    public static final GDCustomFlagDefinition EXPLOSION_ENTITY;
+    public static final GDCustomFlagDefinition EXP_DROP;
+    public static final GDCustomFlagDefinition FALL_DAMAGE;
+    public static final GDCustomFlagDefinition INTERACT_BLOCK;
+    public static final GDCustomFlagDefinition INTERACT_ENTITY;
+    public static final GDCustomFlagDefinition INTERACT_INVENTORY;
+    public static final GDCustomFlagDefinition INVINCIBLE;
+    public static final GDCustomFlagDefinition ITEM_DROP;
+    public static final GDCustomFlagDefinition ITEM_PICKUP;
+    public static final GDCustomFlagDefinition MONSTER_DAMAGE;
+    public static final GDCustomFlagDefinition PISTONS;
+    public static final GDCustomFlagDefinition PORTAL_USE;
+    public static final GDCustomFlagDefinition SPAWN_MONSTER;
+    public static final GDCustomFlagDefinition TELEPORT_FROM;
+    public static final GDCustomFlagDefinition TELEPORT_TO;
+    public static final GDCustomFlagDefinition USE;
+    public static final GDCustomFlagDefinition VEHICLE_DESTROY;
+    public static final GDCustomFlagDefinition WITHER_DAMAGE;
+
+    // USER
+    public static final GDCustomFlagDefinition BLOCK_TRAMPLING;
+    public static final GDCustomFlagDefinition CHEST_ACCESS;
+    public static final GDCustomFlagDefinition CHORUS_FRUIT_TELEPORT;
+    public static final GDCustomFlagDefinition CROP_GROWTH;
+    public static final GDCustomFlagDefinition DAMAGE_ANIMALS;
+    public static final GDCustomFlagDefinition ENDERMAN_GRIEF;
+    public static final GDCustomFlagDefinition ENTER_PLAYER;
+    public static final GDCustomFlagDefinition EXPLOSION_CREEPER;
+    public static final GDCustomFlagDefinition EXPLOSION_TNT;
+    public static final GDCustomFlagDefinition FIRE_DAMAGE;
+    public static final GDCustomFlagDefinition FIRE_SPREAD;
+    public static final GDCustomFlagDefinition GRASS_GROWTH;
+    public static final GDCustomFlagDefinition ICE_FORM;
+    public static final GDCustomFlagDefinition ICE_MELT;
+    public static final GDCustomFlagDefinition LAVA_FLOW;
+    public static final GDCustomFlagDefinition LEAF_DECAY;
+    public static final GDCustomFlagDefinition LIGHTNING;
+    public static final GDCustomFlagDefinition LIGHTER;
+    public static final GDCustomFlagDefinition MUSHROOM_GROWTH;
+    public static final GDCustomFlagDefinition MYCELIUM_SPREAD;
+    public static final GDCustomFlagDefinition PVP;
+    public static final GDCustomFlagDefinition RIDE;
+    public static final GDCustomFlagDefinition SLEEP;
+    public static final GDCustomFlagDefinition SNOW_FALL;
+    public static final GDCustomFlagDefinition SNOW_MELT;
+    public static final GDCustomFlagDefinition SNOWMAN_TRAIL;
+    public static final GDCustomFlagDefinition SOIL_DRY;
+    public static final GDCustomFlagDefinition SPAWN_AMBIENT;
+    public static final GDCustomFlagDefinition SPAWN_ANIMAL;
+    public static final GDCustomFlagDefinition SPAWN_AQUATIC;
+    public static final GDCustomFlagDefinition VEHICLE_DESTROY_CLAIM;
+    public static final GDCustomFlagDefinition VEHICLE_PLACE;
+    public static final GDCustomFlagDefinition VINE_GROWTH;
+    public static final GDCustomFlagDefinition WATER_FLOW;
+
+    public static final List<GDCustomFlagDefinition> ADMIN_FLAGS = new ArrayList<>();
+    public static final List<GDCustomFlagDefinition> USER_FLAGS = new ArrayList<>();
+
+    static {
+        Set<Context> contexts = new HashSet<>();
+
+        contexts = new HashSet<>();
+        BLOCK_BREAK = new GDCustomFlagDefinition(Flags.BLOCK_BREAK, contexts, "block-break", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_BLOCK_BREAK);
+
+        contexts = new HashSet<>();
+        BLOCK_PLACE = new GDCustomFlagDefinition(Flags.BLOCK_PLACE, contexts, "block-place", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_BLOCK_PLACE);
+
+        contexts = new HashSet<>();
+        BLOCK_GROW = new GDCustomFlagDefinition(Flags.BLOCK_GROW, contexts, "block-grow", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_BLOCK_GROW);
+
+        contexts = new HashSet<>();
+        BLOCK_SPREAD = new GDCustomFlagDefinition(Flags.BLOCK_SPREAD, contexts, "block-spread", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_BLOCK_SPREAD);
+
+        // ADMIN
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.SOURCE_ENDERMAN);
+        ENDERPEARL = new GDCustomFlagDefinition(Flags.INTERACT_ITEM_SECONDARY, contexts, "enderpearl", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_ENDERPEARL);
+        ENDERPEARL.getDefinitionContexts().add(ClaimContexts.GLOBAL_DEFAULT_CONTEXT);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.SOURCE_PLAYER);
+        ENTER_PLAYER = new GDCustomFlagDefinition(Flags.ENTER_CLAIM, contexts, "enter-player", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_ENTER_PLAYER);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.SOURCE_PLAYER);
+        EXIT_PLAYER = new GDCustomFlagDefinition(Flags.ENTER_CLAIM, contexts, "exit-player", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_EXIT_PLAYER);
+
+        contexts = new HashSet<>();
+        EXPLOSION_BLOCK = new GDCustomFlagDefinition(Flags.EXPLOSION_BLOCK, contexts, "explosion-block", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_EXPLOSION_BLOCK);
+        EXPLOSION_BLOCK.getDefinitionContexts().add(ClaimContexts.GLOBAL_DEFAULT_CONTEXT);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.SOURCE_CREEPER);
+        EXPLOSION_CREEPER = new GDCustomFlagDefinition(Flags.EXPLOSION_BLOCK, contexts, "explosion-creeper", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_EXPLOSION_CREEPER);
+        EXPLOSION_CREEPER.addFlagData(Flags.EXPLOSION_ENTITY, contexts);
+
+        contexts = new HashSet<>();
+        EXPLOSION_ENTITY = new GDCustomFlagDefinition(Flags.EXPLOSION_ENTITY, contexts, "explosion-entity", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_EXPLOSION_ENTITY);
+        EXPLOSION_ENTITY.getDefinitionContexts().add(ClaimContexts.GLOBAL_DEFAULT_CONTEXT);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.SOURCE_TNT);
+        EXPLOSION_TNT = new GDCustomFlagDefinition(Flags.EXPLOSION_BLOCK, contexts, "explosion-tnt", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_EXPLOSION_TNT);
+        EXPLOSION_TNT.addFlagData(Flags.EXPLOSION_ENTITY, contexts);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.TARGET_XP_ORB);
+        EXP_DROP = new GDCustomFlagDefinition(Flags.ENTITY_SPAWN, contexts, "exp-drop", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_EXP_DROP);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.SOURCE_FALL);
+        contexts.add(FlagContexts.TARGET_PLAYER);
+        FALL_DAMAGE = new GDCustomFlagDefinition(Flags.ENTITY_DAMAGE, contexts, "fall-damage", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_FALL_DAMAGE);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.SOURCE_PLAYER);
+        INTERACT_BLOCK = new GDCustomFlagDefinition(Flags.INTERACT_BLOCK_SECONDARY, contexts, "interact-block", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_INTERACT_BLOCK);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.SOURCE_PLAYER);
+        INTERACT_ENTITY = new GDCustomFlagDefinition(Flags.INTERACT_ENTITY_SECONDARY, contexts, "interact-entity", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_INTERACT_ENTITY);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.SOURCE_PLAYER);
+        INTERACT_INVENTORY = new GDCustomFlagDefinition(Flags.INTERACT_INVENTORY, contexts, "interact-inventory", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_INTERACT_INVENTORY);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.TARGET_PLAYER);
+        INVINCIBLE = new GDCustomFlagDefinition(Flags.ENTITY_DAMAGE, contexts, "invincible", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_INVINCIBLE);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.SOURCE_PLAYER);
+        ITEM_DROP = new GDCustomFlagDefinition(Flags.ITEM_DROP, contexts, "item-drop", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_ITEM_DROP);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.SOURCE_PLAYER);
+        ITEM_PICKUP = new GDCustomFlagDefinition(Flags.ITEM_PICKUP, contexts, "item-pickup", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_ITEM_PICKUP);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.SOURCE_TYPE_MONSTER);
+        MONSTER_DAMAGE = new GDCustomFlagDefinition(Flags.ENTITY_DAMAGE, contexts, "monster-damage", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_MONSTER_DAMAGE);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.TARGET_PISTON);
+        PISTONS = new GDCustomFlagDefinition(Flags.INTERACT_BLOCK_SECONDARY, contexts, "pistons", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_PISTONS);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.SOURCE_PLAYER);
+        contexts.add(FlagContexts.TARGET_TYPE_PORTAL);
+        PORTAL_USE = new GDCustomFlagDefinition(Flags.INTERACT_BLOCK_SECONDARY, contexts, "portal-use", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_PORTAL_USE);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.TARGET_TYPE_MONSTER);
+        SPAWN_MONSTER = new GDCustomFlagDefinition(Flags.ENTITY_SPAWN, contexts, "spawn-monster", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_SPAWN_MONSTER);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.TARGET_PLAYER);
+        TELEPORT_FROM = new GDCustomFlagDefinition(Flags.ENTITY_TELEPORT_FROM, contexts, "teleport-from", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_TELEPORT_FROM);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.TARGET_PLAYER);
+        TELEPORT_TO = new GDCustomFlagDefinition(Flags.ENTITY_TELEPORT_TO, contexts, "teleport-to", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_TELEPORT_TO);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.TARGET_TYPE_VEHICLE);
+        VEHICLE_DESTROY = new GDCustomFlagDefinition(Flags.ENTITY_DAMAGE, contexts, "vehicle-destroy", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_VEHICLE_DESTROY);
+        VEHICLE_DESTROY.getDefinitionContexts().add(ClaimContexts.GLOBAL_DEFAULT_CONTEXT);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.SOURCE_WITHER);
+        WITHER_DAMAGE = new GDCustomFlagDefinition(Flags.ENTITY_DAMAGE, contexts, "wither-damage", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_WITHER_DAMAGE);
+        WITHER_DAMAGE.getDefinitionContexts().add(ClaimContexts.GLOBAL_DEFAULT_CONTEXT);
+
+        ADMIN_FLAGS.add(BLOCK_BREAK);
+        ADMIN_FLAGS.add(BLOCK_PLACE);
+        ADMIN_FLAGS.add(BLOCK_GROW);
+        ADMIN_FLAGS.add(BLOCK_SPREAD);
+        ADMIN_FLAGS.add(ENDERPEARL);
+        ADMIN_FLAGS.add(ENTER_PLAYER);
+        ADMIN_FLAGS.add(EXIT_PLAYER);
+        ADMIN_FLAGS.add(EXPLOSION_BLOCK);
+        ADMIN_FLAGS.add(EXPLOSION_CREEPER);
+        ADMIN_FLAGS.add(EXPLOSION_ENTITY);
+        ADMIN_FLAGS.add(EXPLOSION_TNT);
+        ADMIN_FLAGS.add(EXP_DROP);
+        ADMIN_FLAGS.add(FALL_DAMAGE);
+        ADMIN_FLAGS.add(INTERACT_BLOCK);
+        ADMIN_FLAGS.add(INTERACT_ENTITY);
+        ADMIN_FLAGS.add(INTERACT_INVENTORY);
+        ADMIN_FLAGS.add(INVINCIBLE);
+        ADMIN_FLAGS.add(ITEM_DROP);
+        ADMIN_FLAGS.add(ITEM_PICKUP);
+        ADMIN_FLAGS.add(MONSTER_DAMAGE);
+        ADMIN_FLAGS.add(PISTONS);
+        ADMIN_FLAGS.add(SPAWN_MONSTER);
+        ADMIN_FLAGS.add(TELEPORT_FROM);
+        ADMIN_FLAGS.add(TELEPORT_TO);
+        ADMIN_FLAGS.add(VEHICLE_DESTROY);
+        ADMIN_FLAGS.add(WITHER_DAMAGE);
+
+
+        // USER
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.TARGET_FARMLAND);
+        contexts.add(FlagContexts.TARGET_TURTLE_EGG);
+        BLOCK_TRAMPLING = new GDCustomFlagDefinition(Flags.COLLIDE_BLOCK, contexts, "block-trampling", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_BLOCK_TRAMPLING);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.SOURCE_PLAYER);
+        contexts.add(FlagContexts.TARGET_CHEST);
+        CHEST_ACCESS = new GDCustomFlagDefinition(Flags.INTERACT_BLOCK_SECONDARY, contexts, "chest-access", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_CHEST_ACCESS);
+        CHEST_ACCESS.addFlagData(Flags.INTERACT_INVENTORY, contexts);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.TARGET_CHORUS_FRUIT);
+        CHORUS_FRUIT_TELEPORT = new GDCustomFlagDefinition(Flags.INTERACT_ITEM_SECONDARY, contexts, "chorus-fruit-teleport", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_CHORUS_FRUIT_TELEPORT);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.TARGET_TYPE_CROP);
+        CROP_GROWTH = new GDCustomFlagDefinition(Flags.BLOCK_GROW, contexts, "crop-growth", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_CROP_GROWTH);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.TARGET_TYPE_ANIMAL);
+        DAMAGE_ANIMALS = new GDCustomFlagDefinition(Flags.ENTITY_DAMAGE, contexts, "damage-animals", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_DAMAGE_ANIMALS);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.SOURCE_ENDERMAN);
+        ENDERMAN_GRIEF = new GDCustomFlagDefinition(Flags.ENTITY_DAMAGE, contexts, "enderman-grief", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_ENDERMAN_GRIEF);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.SOURCE_FIRE);
+        FIRE_DAMAGE = new GDCustomFlagDefinition(Flags.BLOCK_MODIFY, contexts, "fire-damage", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_FIRE_DAMAGE);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.SOURCE_FIRE);
+        FIRE_SPREAD = new GDCustomFlagDefinition(Flags.BLOCK_SPREAD, contexts, "fire-spread", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_FIRE_SPREAD);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.TARGET_GRASS);
+        GRASS_GROWTH = new GDCustomFlagDefinition(Flags.BLOCK_GROW, contexts, "grass-growth", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_GRASS_GROWTH);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.TARGET_ICE_FORM);
+        ICE_FORM = new GDCustomFlagDefinition(Flags.BLOCK_MODIFY, contexts, "ice-form", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_ICE_FORM);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.TARGET_ICE_MELT);
+        ICE_MELT = new GDCustomFlagDefinition(Flags.BLOCK_MODIFY, contexts, "ice-melt", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_ICE_MELT);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.SOURCE_LAVA);
+        LAVA_FLOW = new GDCustomFlagDefinition(Flags.LIQUID_FLOW, contexts, "lava-flow", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_LAVA_FLOW);
+
+        contexts = new HashSet<>();
+        LEAF_DECAY = new GDCustomFlagDefinition(Flags.LEAF_DECAY, contexts, "leaf-decay", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_LEAF_DECAY);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.SOURCE_LIGHTNING_BOLT);
+        LIGHTNING = new GDCustomFlagDefinition(Flags.ENTITY_DAMAGE, contexts, "lightning", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_LIGHTNING);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.TARGET_FLINTANDSTEEL);
+        LIGHTER = new GDCustomFlagDefinition(Flags.INTERACT_ITEM_SECONDARY, contexts, "lighter", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_LIGHTER);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.TARGET_TYPE_MUSHROOM);
+        MUSHROOM_GROWTH = new GDCustomFlagDefinition(Flags.BLOCK_GROW, contexts, "mushroom-growth", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_MUSHROOM_GROWTH);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.TARGET_MYCELIUM);
+        MYCELIUM_SPREAD = new GDCustomFlagDefinition(Flags.BLOCK_SPREAD, contexts, "mycelium-spread", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_MYCELIUM_SPREAD);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.SOURCE_PLAYER);
+        contexts.add(FlagContexts.TARGET_PLAYER);
+        PVP = new GDCustomFlagDefinition(Flags.ENTITY_DAMAGE, contexts, "pvp", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_PVP);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.SOURCE_PLAYER);
+        contexts.add(FlagContexts.TARGET_TYPE_VEHICLE);
+        RIDE = new GDCustomFlagDefinition(Flags.ENTITY_RIDING, contexts, "ride", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_RIDE);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.SOURCE_PLAYER);
+        contexts.add(FlagContexts.TARGET_BED);
+        SLEEP = new GDCustomFlagDefinition(Flags.INTERACT_BLOCK_SECONDARY, contexts, "sleep", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_SLEEP);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.TARGET_SNOW_LAYER);
+        SNOW_FALL = new GDCustomFlagDefinition(Flags.BLOCK_PLACE, contexts, "snow-fall", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_SNOW_FALL);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.TARGET_SNOW_LAYER);
+        SNOW_MELT = new GDCustomFlagDefinition(Flags.BLOCK_BREAK, contexts, "snow-melt", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_SNOW_MELT);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.SOURCE_SNOWMAN);
+        SNOWMAN_TRAIL = new GDCustomFlagDefinition(Flags.BLOCK_MODIFY, contexts, "snowman-trail", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_SNOWMAN_TRAIL);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.STATE_FARMLAND_DRY);
+        SOIL_DRY = new GDCustomFlagDefinition(Flags.BLOCK_MODIFY, contexts, "soil-dry", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_SOIL_DRY);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.TARGET_TYPE_AMBIENT);
+        SPAWN_AMBIENT = new GDCustomFlagDefinition(Flags.ENTITY_SPAWN, contexts, "spawn-ambient", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_SPAWN_AMBIENT);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.TARGET_TYPE_ANIMAL);
+        SPAWN_ANIMAL = new GDCustomFlagDefinition(Flags.ENTITY_SPAWN, contexts, "spawn-animal", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_SPAWN_ANIMAL);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.TARGET_TYPE_AQUATIC);
+        SPAWN_AQUATIC = new GDCustomFlagDefinition(Flags.ENTITY_SPAWN, contexts, "spawn-aquatic", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_SPAWN_AQUATIC);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.SOURCE_PLAYER);
+        USE = new GDCustomFlagDefinition(Flags.INTERACT_BLOCK_SECONDARY, contexts, "use", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_USE);
+        USE.addFlagData(Flags.INTERACT_ENTITY_SECONDARY, new HashSet<>(contexts));
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.TARGET_TYPE_VEHICLE);
+        VEHICLE_DESTROY_CLAIM = new GDCustomFlagDefinition(Flags.ENTITY_DAMAGE, contexts, "vehicle-destroy", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_VEHICLE_DESTROY);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.TARGET_TYPE_VEHICLE);
+        VEHICLE_PLACE = new GDCustomFlagDefinition(Flags.BLOCK_PLACE, contexts, "vehicle-place", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_VEHICLE_PLACE);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.TARGET_VINE);
+        VINE_GROWTH = new GDCustomFlagDefinition(Flags.BLOCK_GROW, contexts, "vine-growth", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_VINE_GROWTH);
+
+        contexts = new HashSet<>();
+        contexts.add(FlagContexts.SOURCE_WATER);
+        WATER_FLOW = new GDCustomFlagDefinition(Flags.LIQUID_FLOW, contexts, "water-flow", MessageCache.getInstance().FLAG_DESCRIPTION_CUSTOM_WATER_FLOW);
+
+        USER_FLAGS.add(CHEST_ACCESS);
+        USER_FLAGS.add(CHORUS_FRUIT_TELEPORT);
+        USER_FLAGS.add(CROP_GROWTH);
+        USER_FLAGS.add(DAMAGE_ANIMALS);
+        USER_FLAGS.add(ENDERMAN_GRIEF);
+        USER_FLAGS.add(FIRE_DAMAGE);
+        USER_FLAGS.add(FIRE_SPREAD);
+        USER_FLAGS.add(GRASS_GROWTH);
+        USER_FLAGS.add(ICE_FORM);
+        USER_FLAGS.add(ICE_MELT);
+        USER_FLAGS.add(LAVA_FLOW);
+        USER_FLAGS.add(LEAF_DECAY);
+        USER_FLAGS.add(LIGHTER);
+        USER_FLAGS.add(LIGHTNING);
+        USER_FLAGS.add(MYCELIUM_SPREAD);
+        USER_FLAGS.add(PVP);
+        USER_FLAGS.add(RIDE);
+        USER_FLAGS.add(SLEEP);
+        USER_FLAGS.add(SNOW_FALL);
+        USER_FLAGS.add(SNOW_MELT);
+        USER_FLAGS.add(SOIL_DRY);
+        USER_FLAGS.add(SPAWN_AMBIENT);
+        USER_FLAGS.add(SPAWN_ANIMAL);
+        USER_FLAGS.add(SPAWN_AQUATIC);
+        USER_FLAGS.add(USE);
+        USER_FLAGS.add(VEHICLE_DESTROY_CLAIM);
+        USER_FLAGS.add(VEHICLE_PLACE);
+        USER_FLAGS.add(WATER_FLOW);
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/permission/flag/GDFlag.java b/sponge/src/main/java/com/griefdefender/permission/flag/GDFlag.java
new file mode 100644
index 0000000..f6baa5b
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/permission/flag/GDFlag.java
@@ -0,0 +1,119 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.permission.flag;
+
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.claim.ClaimType;
+import com.griefdefender.api.claim.ClaimTypes;
+import com.griefdefender.api.permission.flag.Flag;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+
+public class GDFlag implements Flag {
+
+    private final String id;
+    private final String name;
+    private Component description;
+
+    public GDFlag(String id, String name) {
+        this.id = id;
+        this.name = name.toLowerCase();
+    }
+
+    @Override
+    public String getId() {
+        return this.id;
+    }
+
+    public String getPermission() {
+        return "griefdefender.flag." + this.name.toLowerCase();
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+    public Component getDescription() {
+        if (this.description == null) {
+            this.description = this.createDescription();
+        }
+        return this.description;
+    }
+
+    @Override
+    public String toString() {
+        return this.name;
+    }
+
+    private Component createDescription() {
+        final Component description = GriefDefenderPlugin.getInstance().messageData.getMessage("flag-description-" + this.name.toLowerCase());
+        if (description != null) {
+            return description;
+        }
+        return TextComponent.of("Not defined.");
+    }
+
+    public void reloadDescription() {
+        this.description = null;
+    }
+
+    @Override
+    public boolean getDefaultClaimTypeValue(ClaimType type) {
+        if (type == null || type != ClaimTypes.WILDERNESS) {
+            switch (this.name) {
+                case "block-break" :
+                case "block-modify" :
+                case "block-place" :
+                case "collide-block" :
+                case "collide-entity" :
+                case "entity-damage" :
+                case "explosion-block" :
+                case "explosion-entity" :
+                case "fire-spread" :
+                case "interact-block-primary" :
+                case "interact-block-secondary" :
+                case "interact-entity-primary" :
+                case "interact-inventory" : 
+                case "liquid-flow" : 
+                case "projectile-impact-block" :
+                case "projectile-impact-entity" : 
+                    return false;
+                default :
+                    return true;
+            }
+        }
+        if (type == ClaimTypes.WILDERNESS) {
+            switch (this.name) {
+                case "fire-spread" :
+                    return false;
+
+                default :
+                    return true;
+            }
+        }
+
+        return true;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/permission/flag/GDFlags.java b/sponge/src/main/java/com/griefdefender/permission/flag/GDFlags.java
new file mode 100644
index 0000000..3b7ba2c
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/permission/flag/GDFlags.java
@@ -0,0 +1,107 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.permission.flag;
+
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.permission.flag.Flags;
+
+public class GDFlags {
+
+    public static boolean BLOCK_BREAK;
+    public static boolean BLOCK_GROW;
+    public static boolean BLOCK_MODIFY;
+    public static boolean BLOCK_PLACE;
+    public static boolean BLOCK_SPREAD;
+    public static boolean COLLIDE_BLOCK;
+    public static boolean COLLIDE_ENTITY;
+    public static boolean COMMAND_EXECUTE;
+    public static boolean COMMAND_EXECUTE_PVP;
+    public static boolean ENTER_CLAIM;
+    public static boolean ENTITY_CHUNK_SPAWN;
+    public static boolean ENTITY_DAMAGE;
+    public static boolean ENTITY_RIDING;
+    public static boolean ENTITY_SPAWN;
+    public static boolean ENTITY_TELEPORT_FROM;
+    public static boolean ENTITY_TELEPORT_TO;
+    public static boolean EXIT_CLAIM;
+    public static boolean EXPLOSION_BLOCK;
+    public static boolean EXPLOSION_ENTITY;
+    public static boolean INTERACT_BLOCK_PRIMARY;
+    public static boolean INTERACT_BLOCK_SECONDARY;
+    public static boolean INTERACT_ENTITY_PRIMARY;
+    public static boolean INTERACT_ENTITY_SECONDARY;
+    public static boolean INTERACT_ITEM_PRIMARY;
+    public static boolean INTERACT_ITEM_SECONDARY;
+    public static boolean INTERACT_INVENTORY;
+    public static boolean INTERACT_INVENTORY_CLICK;
+    public static boolean ITEM_DROP;
+    public static boolean ITEM_PICKUP;
+    public static boolean ITEM_SPAWN;
+    public static boolean ITEM_USE;
+    public static boolean LEAF_DECAY;
+    public static boolean LIQUID_FLOW;
+    public static boolean PORTAL_USE;
+    public static boolean PROJECTILE_IMPACT_BLOCK;
+    public static boolean PROJECTILE_IMPACT_ENTITY;
+
+    public static void populateFlagStatus() {
+        BLOCK_BREAK = GriefDefenderPlugin.getGlobalConfig().getConfig().modules.isProtectionModuleEnabled(Flags.BLOCK_BREAK.getName());
+        BLOCK_GROW = GriefDefenderPlugin.getGlobalConfig().getConfig().modules.isProtectionModuleEnabled(Flags.BLOCK_GROW.getName());
+        BLOCK_MODIFY = GriefDefenderPlugin.getGlobalConfig().getConfig().modules.isProtectionModuleEnabled(Flags.BLOCK_MODIFY.getName());
+        BLOCK_PLACE = GriefDefenderPlugin.getGlobalConfig().getConfig().modules.isProtectionModuleEnabled(Flags.BLOCK_PLACE.getName());
+        BLOCK_SPREAD  = GriefDefenderPlugin.getGlobalConfig().getConfig().modules.isProtectionModuleEnabled(Flags.BLOCK_SPREAD.getName());
+        COLLIDE_BLOCK  = GriefDefenderPlugin.getGlobalConfig().getConfig().modules.isProtectionModuleEnabled(Flags.COLLIDE_BLOCK.getName());
+        COLLIDE_ENTITY  = GriefDefenderPlugin.getGlobalConfig().getConfig().modules.isProtectionModuleEnabled(Flags.COLLIDE_ENTITY.getName());
+        COMMAND_EXECUTE  = GriefDefenderPlugin.getGlobalConfig().getConfig().modules.isProtectionModuleEnabled(Flags.COMMAND_EXECUTE.getName());
+        COMMAND_EXECUTE_PVP  = GriefDefenderPlugin.getGlobalConfig().getConfig().modules.isProtectionModuleEnabled(Flags.COMMAND_EXECUTE_PVP.getName());
+        ENTER_CLAIM  = GriefDefenderPlugin.getGlobalConfig().getConfig().modules.isProtectionModuleEnabled(Flags.ENTER_CLAIM.getName());
+        ENTITY_CHUNK_SPAWN  = GriefDefenderPlugin.getGlobalConfig().getConfig().modules.isProtectionModuleEnabled(Flags.ENTITY_CHUNK_SPAWN.getName());
+        ENTITY_DAMAGE  = GriefDefenderPlugin.getGlobalConfig().getConfig().modules.isProtectionModuleEnabled(Flags.ENTITY_DAMAGE.getName());
+        ENTITY_RIDING  = GriefDefenderPlugin.getGlobalConfig().getConfig().modules.isProtectionModuleEnabled(Flags.ENTITY_RIDING.getName());
+        ENTITY_SPAWN  = GriefDefenderPlugin.getGlobalConfig().getConfig().modules.isProtectionModuleEnabled(Flags.ENTITY_SPAWN.getName());
+        ENTITY_TELEPORT_FROM  = GriefDefenderPlugin.getGlobalConfig().getConfig().modules.isProtectionModuleEnabled(Flags.ENTITY_TELEPORT_FROM.getName());
+        ENTITY_TELEPORT_TO  = GriefDefenderPlugin.getGlobalConfig().getConfig().modules.isProtectionModuleEnabled(Flags.ENTITY_TELEPORT_TO.getName());
+        EXIT_CLAIM  = GriefDefenderPlugin.getGlobalConfig().getConfig().modules.isProtectionModuleEnabled(Flags.EXIT_CLAIM.getName());
+        EXPLOSION_BLOCK  = GriefDefenderPlugin.getGlobalConfig().getConfig().modules.isProtectionModuleEnabled(Flags.EXPLOSION_BLOCK.getName());
+        EXPLOSION_ENTITY  = GriefDefenderPlugin.getGlobalConfig().getConfig().modules.isProtectionModuleEnabled(Flags.EXPLOSION_ENTITY.getName());
+        INTERACT_BLOCK_PRIMARY  = GriefDefenderPlugin.getGlobalConfig().getConfig().modules.isProtectionModuleEnabled(Flags.INTERACT_BLOCK_PRIMARY.getName());
+        INTERACT_BLOCK_SECONDARY  = GriefDefenderPlugin.getGlobalConfig().getConfig().modules.isProtectionModuleEnabled(Flags.INTERACT_BLOCK_SECONDARY.getName());
+        INTERACT_ENTITY_PRIMARY  = GriefDefenderPlugin.getGlobalConfig().getConfig().modules.isProtectionModuleEnabled(Flags.INTERACT_ENTITY_PRIMARY.getName());
+        INTERACT_ENTITY_SECONDARY  = GriefDefenderPlugin.getGlobalConfig().getConfig().modules.isProtectionModuleEnabled(Flags.INTERACT_ENTITY_SECONDARY.getName());
+        INTERACT_INVENTORY  = GriefDefenderPlugin.getGlobalConfig().getConfig().modules.isProtectionModuleEnabled(Flags.INTERACT_INVENTORY.getName());
+        INTERACT_INVENTORY_CLICK  = GriefDefenderPlugin.getGlobalConfig().getConfig().modules.isProtectionModuleEnabled(Flags.INTERACT_INVENTORY_CLICK.getName());
+        INTERACT_ITEM_PRIMARY  = GriefDefenderPlugin.getGlobalConfig().getConfig().modules.isProtectionModuleEnabled(Flags.INTERACT_ITEM_PRIMARY.getName());
+        INTERACT_ITEM_SECONDARY  = GriefDefenderPlugin.getGlobalConfig().getConfig().modules.isProtectionModuleEnabled(Flags.INTERACT_ITEM_SECONDARY.getName());
+        ITEM_DROP  = GriefDefenderPlugin.getGlobalConfig().getConfig().modules.isProtectionModuleEnabled(Flags.ITEM_DROP.getName());
+        ITEM_PICKUP  = GriefDefenderPlugin.getGlobalConfig().getConfig().modules.isProtectionModuleEnabled(Flags.ITEM_PICKUP.getName());
+        ITEM_SPAWN  = GriefDefenderPlugin.getGlobalConfig().getConfig().modules.isProtectionModuleEnabled(Flags.ITEM_SPAWN.getName());
+        ITEM_USE  = GriefDefenderPlugin.getGlobalConfig().getConfig().modules.isProtectionModuleEnabled(Flags.ITEM_USE.getName());
+        LEAF_DECAY  = GriefDefenderPlugin.getGlobalConfig().getConfig().modules.isProtectionModuleEnabled(Flags.LEAF_DECAY.getName());
+        LIQUID_FLOW  = GriefDefenderPlugin.getGlobalConfig().getConfig().modules.isProtectionModuleEnabled(Flags.LIQUID_FLOW.getName());
+        PORTAL_USE  = GriefDefenderPlugin.getGlobalConfig().getConfig().modules.isProtectionModuleEnabled(Flags.PORTAL_USE.getName());
+        PROJECTILE_IMPACT_BLOCK  = GriefDefenderPlugin.getGlobalConfig().getConfig().modules.isProtectionModuleEnabled(Flags.PROJECTILE_IMPACT_BLOCK.getName());
+        PROJECTILE_IMPACT_ENTITY  = GriefDefenderPlugin.getGlobalConfig().getConfig().modules.isProtectionModuleEnabled(Flags.PROJECTILE_IMPACT_ENTITY.getName());
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/permission/option/GDOption.java b/sponge/src/main/java/com/griefdefender/permission/option/GDOption.java
new file mode 100644
index 0000000..5f97884
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/permission/option/GDOption.java
@@ -0,0 +1,243 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.permission.option;
+
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.Tristate;
+import com.griefdefender.api.permission.option.Option;
+import com.griefdefender.api.permission.option.type.CreateModeType;
+import com.griefdefender.api.permission.option.type.CreateModeTypes;
+import com.griefdefender.api.permission.option.type.GameModeType;
+import com.griefdefender.api.permission.option.type.GameModeTypes;
+import com.griefdefender.api.permission.option.type.WeatherType;
+import com.griefdefender.api.permission.option.type.WeatherTypes;
+
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+public class GDOption<T> implements Option<T> {
+
+    private static final List<String> GLOBAL_OPTIONS = Arrays.asList(
+            "abandon-return-ratio", "blocks-accrued-per-hour", "chest-expiration", "economy-block-cost", 
+            "economy-block-sell-return", "expiration", "initial-blocks", "max-accrued-blocks", "radius-list", 
+            "radius-list");
+    private static final List<String> ADMIN_OPTIONS = Arrays.asList(
+            "player-command", "player-deny-godmode", "player-deny-hunger", "player-gamemode",
+            "player-health-regen", "player-keep-inventory", "player-keep-level", "player-walk-speed",
+            "radius-inspect", "radius-list");
+
+    private final String id;
+    private final String name;
+    private final Class<T> allowed;
+    private Component description;
+    private Boolean isGlobal;
+    private Boolean isAdmin;
+
+    GDOption(OptionBuilder<T> builder) {
+        this(builder.id, builder.name, builder.typeClass);
+    }
+
+    public GDOption(String id, String name, Class<T> allowed) {
+        this.id = id;
+        this.name = name;
+        this.allowed = allowed;
+        this.isAdmin = ADMIN_OPTIONS.contains(name);
+        this.isGlobal = GLOBAL_OPTIONS.contains(name);
+    }
+
+    @Override
+    public String getId() {
+        return this.id;
+    }
+
+    public String getPermission() {
+        return "griefdefender." + this.name.toLowerCase();
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+    public boolean isGlobal() {
+        return this.isGlobal;
+    }
+
+    public boolean isAdmin() {
+        return this.isAdmin;
+    }
+
+    @Override
+    public Class<T> getAllowedType() {
+        return this.allowed;
+    }
+
+    @Override
+    public Component getDescription() {
+        if (this.description == null) {
+            this.description = this.createDescription();
+        }
+        return this.description;
+    }
+
+    @Override
+    public String toString() {
+        return this.name;
+    }
+
+    private Component createDescription() {
+        final Component description = GriefDefenderPlugin.getInstance().messageData.getMessage("option-description-" + this.name.toLowerCase());
+        if (description != null) {
+            return description;
+        }
+        return TextComponent.of("Not defined.");
+    }
+
+    public void reloadDescription() {
+        this.description = null;
+    }
+
+    @Override
+    public T getDefaultValue() {
+        if (this.allowed.isAssignableFrom(Tristate.class)) {
+            return (T) Tristate.UNDEFINED;
+        }
+        if (this.allowed.isAssignableFrom(String.class)) {
+            return (T) "undefined";
+        }
+        if (this.allowed.isAssignableFrom(Integer.class)) {
+            return (T) Integer.valueOf(-1);
+        }
+        if (this.allowed.isAssignableFrom(Double.class)) {
+            return (T) Double.valueOf(0);
+        }
+        if (this.allowed.isAssignableFrom(Boolean.class)) {
+            return (T) Boolean.FALSE;
+        }
+        if (this.allowed.isAssignableFrom(CreateModeType.class)) {
+            return (T) CreateModeTypes.AREA;
+        }
+        if (this.allowed.isAssignableFrom(GameModeType.class)) {
+            return (T) GameModeTypes.UNDEFINED;
+        }
+        if (this.allowed.isAssignableFrom(WeatherType.class)) {
+            return (T) WeatherTypes.UNDEFINED;
+        }
+        return null;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+        if (!(o instanceof Option)) {
+            return false;
+        }
+        return this.id.equals(((Option<?>) o).getId());
+    }
+
+    @Override
+    public int hashCode() {
+        return this.id.hashCode();
+    }
+
+    @Nullable
+    public boolean validateStringValue(String value, boolean log) {
+        if (value.equalsIgnoreCase("undefined")) {
+            return false;
+        } else if (this.allowed == Integer.class) {
+            try {
+                Integer.parseInt(value);
+            } catch (NumberFormatException e) {
+                if (log) {
+                    GriefDefenderPlugin.getInstance().getLogger().warn("Invalid Integer value '" + value + "', entered for option " + this.getName() 
+                        + ".\nYou must use a valid number. Skipping...");
+                }
+                return false;
+            }
+            return true;
+        } else if (this.allowed == Boolean.class) {
+            if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")) {
+                return true;
+            }
+
+            if (log) {
+                GriefDefenderPlugin.getInstance().getLogger().warn("Invalid Boolean value '" + value + "', entered for option " + this.getName() 
+                    + ".\nAcceptable values are : true, or false. Skipping...");
+            }
+            return false;
+        } else if (this.allowed == Double.class) {
+            try {
+                Double.parseDouble(value);
+            } catch (NumberFormatException e) {
+                if (log) {
+                    GriefDefenderPlugin.getInstance().getLogger().warn("Invalid Double value '" + value + "', entered for option " + this.getName() 
+                        + ".\nYou must use a valid number. Skipping...");
+                }
+                return false;
+            }
+            return true;
+        } else if (this.allowed == Tristate.class) {
+            if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")) {
+                return true;
+            }
+            if (log) {
+                GriefDefenderPlugin.getInstance().getLogger().warn("Invalid Tristate value '" + value + "', entered for option " + this.getName() 
+                    + ".\nAcceptable values are : true, false, or undefined. Skipping...");
+            }
+        } else if (this.allowed == CreateModeType.class) {
+            if (value.equalsIgnoreCase("area") || value.equalsIgnoreCase("volume")) {
+                return true;
+            }
+            if (log) {
+                GriefDefenderPlugin.getInstance().getLogger().warn("Invalid CreateModeType value '" + value + "', entered for option " + this.getName() 
+                    + ".\nAcceptable values are : area, volume, or undefined. Skipping...");
+            }
+        } else if (this.allowed == GameModeType.class) {
+            if (value.equalsIgnoreCase("survival") || value.equalsIgnoreCase("adventure") || value.equalsIgnoreCase("creative") || value.equalsIgnoreCase("spectator")) {
+                return true;
+            }
+            if (log) {
+                GriefDefenderPlugin.getInstance().getLogger().warn("Invalid GameModeType value '" + value + "', entered for option " + this.getName() 
+                    + ".\nAcceptable values are : adventure, creative, survival, spectator, or undefined. Skipping...");
+            }
+        } else if (this.allowed == WeatherType.class) {
+            if (value.equalsIgnoreCase("clear") || value.equalsIgnoreCase("rain")) {
+                return true;
+            }
+            if (log) {
+                GriefDefenderPlugin.getInstance().getLogger().warn("Invalid WeatherType value '" + value + "', entered for option " + this.getName() 
+                    + ".\nAcceptable values are : clear, rain, or undefined. Skipping...");
+            }
+        }
+ 
+        return false;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/permission/option/OptionBuilder.java b/sponge/src/main/java/com/griefdefender/permission/option/OptionBuilder.java
new file mode 100644
index 0000000..1c5993a
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/permission/option/OptionBuilder.java
@@ -0,0 +1,69 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.permission.option;
+
+import com.griefdefender.api.permission.option.Option;
+import com.griefdefender.api.permission.option.Option.Builder;
+import com.griefdefender.registry.OptionRegistryModule;
+
+public final class OptionBuilder<T> implements Option.Builder<T> {
+
+    Class<T> typeClass;
+    String id;
+    String name;
+
+    @Override
+    public Builder<T> type(Class<T> tClass) {
+        this.typeClass = tClass;
+        return this;
+    }
+
+    @Override
+    public Builder<T> id(String id) {
+        this.id = id;
+        return this;
+    }
+
+    @Override
+    public Builder<T> name(String name) {
+        this.name = name;
+        return this;
+    }
+
+    @Override
+    public Option<T> build() {
+        final GDOption<T> key = new GDOption<>(this);
+        OptionRegistryModule.getInstance().registerCustomType(key);
+        return key;
+    }
+
+    @Override
+    public Builder<T> reset() {
+        this.typeClass = null;
+        this.id = null;
+        this.name = null;
+        return this;
+    }
+}
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/permission/option/type/GDCreateModeType.java b/sponge/src/main/java/com/griefdefender/permission/option/type/GDCreateModeType.java
new file mode 100644
index 0000000..c73cf31
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/permission/option/type/GDCreateModeType.java
@@ -0,0 +1,52 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) SpongePowered <https://www.spongepowered.org>
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.permission.option.type;
+
+import com.griefdefender.api.permission.option.type.CreateModeType;
+
+public class GDCreateModeType implements CreateModeType {
+
+    private final String id;
+    private final String name;
+
+    public GDCreateModeType(String id, String name) {
+        this.id = id;
+        this.name = name;
+    }
+
+    @Override
+    public String getId() {
+        return this.id;
+    }
+
+    @Override
+    public String getName() {
+        return this.name;
+    }
+
+    public String toString() {
+        return this.name;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/permission/option/type/GDGameModeType.java b/sponge/src/main/java/com/griefdefender/permission/option/type/GDGameModeType.java
new file mode 100644
index 0000000..2c7d532
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/permission/option/type/GDGameModeType.java
@@ -0,0 +1,52 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) SpongePowered <https://www.spongepowered.org>
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.permission.option.type;
+
+import com.griefdefender.api.permission.option.type.GameModeType;
+
+public class GDGameModeType implements GameModeType {
+
+    private final String id;
+    private final String name;
+
+    public GDGameModeType(String id, String name) {
+        this.id = id;
+        this.name = name;
+    }
+
+    @Override
+    public String getId() {
+        return this.id;
+    }
+
+    @Override
+    public String getName() {
+        return this.name;
+    }
+
+    public String toString() {
+        return this.name;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/permission/option/type/GDWeatherType.java b/sponge/src/main/java/com/griefdefender/permission/option/type/GDWeatherType.java
new file mode 100644
index 0000000..4eb1ab8
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/permission/option/type/GDWeatherType.java
@@ -0,0 +1,52 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) SpongePowered <https://www.spongepowered.org>
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.permission.option.type;
+
+import com.griefdefender.api.permission.option.type.WeatherType;
+
+public class GDWeatherType implements WeatherType {
+
+    private final String id;
+    private final String name;
+
+    public GDWeatherType(String id, String name) {
+        this.id = id;
+        this.name = name;
+    }
+
+    @Override
+    public String getId() {
+        return this.id;
+    }
+
+    @Override
+    public String getName() {
+        return this.name;
+    }
+
+    public String toString() {
+        return this.name;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/permission/ui/ClaimClickData.java b/sponge/src/main/java/com/griefdefender/permission/ui/ClaimClickData.java
new file mode 100644
index 0000000..37edaa1
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/permission/ui/ClaimClickData.java
@@ -0,0 +1,37 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.permission.ui;
+
+import com.griefdefender.claim.GDClaim;
+
+public class ClaimClickData {
+    public final GDClaim claim;
+    public final Object value;
+
+    public ClaimClickData(GDClaim claim, Object value) {
+        this.claim = claim;
+        this.value = value;
+    }
+}
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/permission/ui/FlagData.java b/sponge/src/main/java/com/griefdefender/permission/ui/FlagData.java
new file mode 100644
index 0000000..dd79915
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/permission/ui/FlagData.java
@@ -0,0 +1,143 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.permission.ui;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+import com.griefdefender.api.claim.ClaimContexts;
+import com.griefdefender.api.permission.Context;
+import com.griefdefender.api.permission.flag.Flag;
+import com.griefdefender.api.permission.flag.Flags;
+
+import net.kyori.text.format.TextColor;
+
+public class FlagData {
+
+    public Flag flag;
+    public Map<Integer, FlagContextHolder> flagContextMap = new HashMap<>();
+
+    public FlagData(Flag flag, Boolean value, MenuType type, Set<Context> contexts) {
+        this.flag = flag;
+        this.addContexts(flag, value, type, contexts);
+    }
+
+    public boolean addContexts(Flag flag, Boolean value, MenuType type, Set<Context> contexts) {
+        final Set<Context> filteredContexts = UIHelper.getFilteredContexts(contexts);
+        final int hashCode = Objects.hash(filteredContexts);
+        final FlagContextHolder flagHolder = this.flagContextMap.get(hashCode);
+        if (flagHolder != null) {
+            if (flagHolder.getType() == MenuType.CLAIM && type == MenuType.DEFAULT) {
+                // ignore
+                return false;
+            }
+            // Context Default Types have higher priority than global
+            if (contexts.contains(ClaimContexts.GLOBAL_DEFAULT_CONTEXT)) {
+                for (Context context : flagHolder.getAllContexts()) {
+                    if (context.getKey().equalsIgnoreCase("gd_claim_default")) {
+                        if (!context.getValue().equalsIgnoreCase("global")) {
+                            return false;
+                        }
+                    }
+                }
+            }
+            // Context Override Types have higher priority than global
+            if (contexts.contains(ClaimContexts.GLOBAL_OVERRIDE_CONTEXT)) {
+                for (Context context : flagHolder.getAllContexts()) {
+                    if (context.getKey().equalsIgnoreCase("gd_claim_override")) {
+                        if (!context.getValue().equalsIgnoreCase("global")) {
+                            return false;
+                        }
+                    }
+                }
+            }
+        }
+        this.flagContextMap.put(Objects.hash(filteredContexts), new FlagContextHolder(flag, value, type, contexts));
+        return true;
+    }
+
+    public class FlagContextHolder {
+        private Set<Context> contexts;
+        private Flag flag;
+        private Boolean value;
+        private MenuType type;
+        private TextColor color;
+        private Set<Context> removedContexts = new HashSet<>();
+        
+        public FlagContextHolder(Flag flag, Boolean value, MenuType type, Set<Context> contexts) {
+            this.flag = flag;
+            this.value = value;
+            this.contexts = this.getFilteredContexts(contexts);
+            this.type = type;
+            this.color = UIHelper.getPermissionMenuTypeColor(type);
+        }
+
+        public Flag getFlag() {
+            return this.flag;
+        }
+
+        public Boolean getValue() {
+            return this.value;
+        }
+
+        public MenuType getType() {
+            return this.type;
+        }
+
+        public TextColor getColor() {
+            return this.color;
+        }
+
+        public Set<Context> getRemovedContexts() {
+            return this.removedContexts;
+        }
+
+        public Set<Context> getContexts() {
+            return this.contexts;
+        }
+
+        public Set<Context> getAllContexts() {
+            Set<Context> allContexts = new HashSet<>();
+            allContexts.addAll(this.removedContexts);
+            allContexts.addAll(this.contexts);
+            return allContexts;
+        }
+
+        private Set<Context> getFilteredContexts(Set<Context> contexts) {
+            Set<Context> filteredContexts = new HashSet<>(contexts);
+            for (Context context : contexts) {
+                if (context.getKey().contains("gd_claim") || context.getKey().equals("server")) {
+                    this.removedContexts.add(context);
+                    filteredContexts.remove(context);
+                }
+            }
+
+            return filteredContexts;
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/permission/ui/MenuType.java b/sponge/src/main/java/com/griefdefender/permission/ui/MenuType.java
new file mode 100644
index 0000000..abae357
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/permission/ui/MenuType.java
@@ -0,0 +1,35 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.permission.ui;
+
+public enum MenuType {
+
+    DEFAULT,
+    CLAIM,
+    OVERRIDE,
+    INHERIT,
+    GROUP,
+    PLAYER
+}
diff --git a/sponge/src/main/java/com/griefdefender/permission/ui/OptionData.java b/sponge/src/main/java/com/griefdefender/permission/ui/OptionData.java
new file mode 100644
index 0000000..45cd00e
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/permission/ui/OptionData.java
@@ -0,0 +1,122 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.permission.ui;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+import com.griefdefender.api.permission.Context;
+import com.griefdefender.api.permission.option.Option;
+
+import net.kyori.text.format.TextColor;
+
+@SuppressWarnings("rawtypes")
+public class OptionData {
+
+    public Option option;
+    public Map<Integer, OptionContextHolder> optionContextMap = new HashMap<>();
+
+    public OptionData(Option option, String value, MenuType type, Set<Context> contexts) {
+        this.option = option;
+        this.addContexts(option, value, type, contexts);
+    }
+
+    public boolean addContexts(Option option, String value, MenuType type, Set<Context> contexts) {
+        final Set<Context> filteredContexts = UIHelper.getFilteredContexts(contexts);
+        final int hashCode = Objects.hash(filteredContexts);
+        final OptionContextHolder flagHolder = this.optionContextMap.get(hashCode);
+        if (flagHolder != null) {
+            if (flagHolder.getType() == MenuType.CLAIM && type == MenuType.DEFAULT) {
+                // ignore
+                return false;
+            }
+        }
+        this.optionContextMap.put(Objects.hash(filteredContexts), new OptionContextHolder(option, value, type, contexts));
+        return true;
+    }
+
+    public class OptionContextHolder {
+        private Set<Context> contexts;
+        private Option option;
+        private String value;
+        private MenuType type;
+        private TextColor color;
+        private Set<Context> removedContexts = new HashSet<>();
+        
+        public OptionContextHolder(Option option, String value, MenuType type, Set<Context> contexts) {
+            this.option = option;
+            this.value = value;
+            this.contexts = this.getFilteredContexts(contexts);
+            this.type = type;
+            this.color = UIHelper.getPermissionMenuTypeColor(type);
+        }
+
+        public Option getOption() {
+            return this.option;
+        }
+
+        public String getValue() {
+            return this.value;
+        }
+
+        public MenuType getType() {
+            return this.type;
+        }
+
+        public TextColor getColor() {
+            return this.color;
+        }
+
+        public Set<Context> getRemovedContexts() {
+            return this.removedContexts;
+        }
+
+        public Set<Context> getContexts() {
+            return this.contexts;
+        }
+
+        public Set<Context> getAllContexts() {
+            Set<Context> allContexts = new HashSet<>();
+            allContexts.addAll(this.removedContexts);
+            allContexts.addAll(this.contexts);
+            return allContexts;
+        }
+
+        private Set<Context> getFilteredContexts(Set<Context> contexts) {
+            Set<Context> filteredContexts = new HashSet<>(contexts);
+            for (Context context : contexts) {
+                if (context.getKey().contains("gd_claim") || context.getKey().equals("server")) {
+                    this.removedContexts.add(context);
+                    filteredContexts.remove(context);
+                }
+            }
+
+            return filteredContexts;
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/permission/ui/UIHelper.java b/sponge/src/main/java/com/griefdefender/permission/ui/UIHelper.java
new file mode 100644
index 0000000..3b7ac66
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/permission/ui/UIHelper.java
@@ -0,0 +1,187 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.permission.ui;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.griefdefender.api.GriefDefender;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.permission.Context;
+import com.griefdefender.api.permission.option.Option;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.permission.GDPermissions;
+import com.griefdefender.permission.ui.FlagData.FlagContextHolder;
+
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.format.TextColor;
+import net.kyori.text.serializer.plain.PlainComponentSerializer;
+
+public class UIHelper {
+
+    public static Comparator<Component> PLAIN_COMPARATOR = (text1, text2) -> PlainComponentSerializer.INSTANCE.serialize(text1).compareTo(PlainComponentSerializer.INSTANCE.serialize(text2));
+
+    public static List<Component> stripeText(List<Component> texts) {
+        Collections.sort(texts, PLAIN_COMPARATOR);
+
+        ImmutableList.Builder<Component> finalTexts = ImmutableList.builder();
+        for (int i = 0; i < texts.size(); i++) {
+            Component text = texts.get(i);
+            if (i % 2 == 0) { 
+                text = text.color(TextColor.GREEN);
+            } else {
+                text = text.color(TextColor.AQUA);
+            }
+
+            finalTexts.add(text);
+        }
+        return finalTexts.build();
+    }
+
+    public static Component getPermissionMenuTypeHoverText(FlagContextHolder flagHolder, MenuType menuType) {
+        if (flagHolder.getType() == MenuType.DEFAULT && menuType == MenuType.CLAIM) {
+            return TextComponent.builder()
+                    .append(MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.FLAG_NOT_SET, 
+                            ImmutableMap.of("flag", TextComponent.of(flagHolder.getFlag().getName(), TextColor.GREEN),
+                                            "value", TextComponent.of(flagHolder.getValue(), TextColor.LIGHT_PURPLE)))).build();
+        }
+
+        return getPermissionMenuTypeHoverText(menuType);
+    }
+
+    public static Component getPermissionMenuTypeHoverText(MenuType type) {
+        Component hoverText = TextComponent.empty();
+        if (type == MenuType.DEFAULT) {
+            hoverText = TextComponent.builder("")
+                    .append(MessageCache.getInstance().TITLE_DEFAULT.color(TextColor.LIGHT_PURPLE))
+                    .append(" : ")
+                    .append(MessageCache.getInstance().FLAG_UI_INFO_DEFAULT)
+                    .build();
+        } else if (type == MenuType.CLAIM) {
+            hoverText = TextComponent.builder("")
+                    .append(MessageCache.getInstance().TITLE_CLAIM.color(TextColor.GOLD))
+                    .append(" : ")
+                    .append(MessageCache.getInstance().FLAG_UI_INFO_CLAIM)
+                    .build();
+        } else if (type == MenuType.OVERRIDE) {
+            hoverText = TextComponent.builder("")
+                    .append(MessageCache.getInstance().TITLE_OVERRIDE.color(TextColor.RED))
+                    .append(" : ")
+                    .append(MessageCache.getInstance().FLAG_UI_INFO_OVERRIDE)
+                    .build();
+        } else if (type == MenuType.INHERIT) {
+            hoverText = TextComponent.builder("")
+                    .append(MessageCache.getInstance().TITLE_INHERIT.color(TextColor.AQUA))
+                    .append(" : ")
+                    .append(MessageCache.getInstance().FLAG_UI_INFO_INHERIT)
+                    .build();
+        }
+        return hoverText;
+    }
+
+    public static Component getBaseOptionOverlayText(String option) {
+        String baseFlag = option.replace(GDPermissions.OPTION_BASE + ".", "");
+        int endIndex = baseFlag.indexOf(".");
+        if (endIndex != -1) {
+            baseFlag = baseFlag.substring(0, endIndex);
+        }
+
+        final Option<?> flag = GriefDefender.getRegistry().getType(Option.class, baseFlag).orElse(null);
+        if (flag == null) {
+            return MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.OPTION_NOT_FOUND, ImmutableMap.of(
+                    "option", baseFlag));
+        }
+
+        return flag.getDescription();
+    }
+
+    public static Component getFriendlyContextString(Claim claim, Set<Context> contexts) {
+        if (contexts.isEmpty()) {
+            return TextComponent.of("[]", TextColor.WHITE);
+        }
+
+        TextComponent.Builder builder = TextComponent.builder();
+        final Iterator<Context> iterator = contexts.iterator();
+        while (iterator.hasNext()) {
+            final Context context = iterator.next();
+            builder.append("\n[", TextColor.WHITE)
+                .append(context.getKey(), TextColor.GREEN)
+                .append("=", TextColor.GRAY)
+                .append(context.getValue(), TextColor.WHITE);
+
+            if (iterator.hasNext()) {
+                builder.append("], ");
+            } else {
+                builder.append("]");
+            }
+        }
+        return builder.build();
+    }
+
+    public static TextColor getPermissionMenuTypeColor(MenuType type) {
+        TextColor color = TextColor.LIGHT_PURPLE;
+        if (type == MenuType.DEFAULT) {
+            color = TextColor.LIGHT_PURPLE;
+        } else if (type == MenuType.CLAIM) {
+            color = TextColor.GOLD;
+        } else if (type == MenuType.INHERIT) {
+            color = TextColor.AQUA;
+        } else {
+            color = TextColor.RED;
+        }
+
+        return color;
+    }
+
+    public static boolean containsCustomContext(Set<Context> contexts) {
+        for (Context context : contexts) {
+            if (context.getKey().equals("gd_claim_default") || context.getKey().equals("server") || context.getKey().equals("gd_claim")) {
+                continue;
+            }
+
+            return true;
+        }
+        return false;
+    }
+
+    public static Set<Context> getFilteredContexts(Set<Context> contexts) {
+        Set<Context> filteredContexts = new HashSet<>(contexts);
+        for (Context context : contexts) {
+            if (context.getKey().contains("gd_claim") || context.getKey().equals("server")) {
+                filteredContexts.remove(context);
+            }
+        }
+
+        return filteredContexts;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/provider/LuckPermsProvider.java b/sponge/src/main/java/com/griefdefender/provider/LuckPermsProvider.java
new file mode 100644
index 0000000..e2d9b7e
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/provider/LuckPermsProvider.java
@@ -0,0 +1,885 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.provider;
+
+import com.github.benmanes.caffeine.cache.Cache;
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableSet;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.Tristate;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.permission.Context;
+import com.griefdefender.api.permission.ContextKeys;
+import com.griefdefender.api.permission.PermissionResult;
+import com.griefdefender.api.permission.ResultTypes;
+import com.griefdefender.api.permission.flag.Flag;
+import com.griefdefender.api.permission.option.Option;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.listener.LuckPermsEventHandler;
+import com.griefdefender.permission.GDPermissionHolder;
+import com.griefdefender.permission.GDPermissionResult;
+import com.griefdefender.permission.GDPermissionUser;
+import me.lucko.luckperms.LuckPerms;
+import me.lucko.luckperms.api.Contexts;
+import me.lucko.luckperms.api.DataMutateResult;
+import me.lucko.luckperms.api.Group;
+import me.lucko.luckperms.api.LuckPermsApi;
+import me.lucko.luckperms.api.Node;
+import me.lucko.luckperms.api.PermissionHolder;
+import me.lucko.luckperms.api.User;
+import me.lucko.luckperms.api.caching.MetaData;
+import me.lucko.luckperms.api.caching.PermissionData;
+import me.lucko.luckperms.api.context.ContextSet;
+import me.lucko.luckperms.api.context.ImmutableContextSet;
+import me.lucko.luckperms.api.context.MutableContextSet;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.UUID;
+import java.util.Map.Entry;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.io.FilenameUtils;
+
+public class LuckPermsProvider implements PermissionProvider {
+
+    private final Cache<String, Group> groupCache = Caffeine.newBuilder().expireAfterAccess(30, TimeUnit.MINUTES)
+            .build();
+    private final Cache<String, User> userCache = Caffeine.newBuilder().expireAfterAccess(30, TimeUnit.MINUTES)
+            .build();
+
+    public static Comparator<Set<Context>> CONTEXT_COMPARATOR = new Comparator<Set<Context>>() {
+        @Override
+        public int compare(Set<Context> s1, Set<Context> s2) {
+            if (s1.size() > s2.size()) {
+                return -1;
+            }
+            if (s1.size() < s2.size()) {
+                return 1;
+            }
+            return s1.equals(s2) ? 0 : -1;
+        }
+    };
+
+    private final LuckPermsApi luckPermsApi;
+
+    public LuckPermsProvider() {
+        this.luckPermsApi = LuckPerms.getApi();
+        //new LuckPermsEventHandler(this.luckPermsApi);
+    }
+
+    public LuckPermsApi getApi() {
+        return this.luckPermsApi;
+    }
+
+    @Override
+    public boolean hasGroupSubject(String identifier) {
+        return this.getGroupSubject(identifier) != null;
+    }
+
+    public PermissionHolder getLuckPermsHolder(GDPermissionHolder holder) {
+        if (holder.getIdentifier().equalsIgnoreCase("default")) {
+            return this.luckPermsApi.getGroup("default");
+        }
+        if (holder instanceof GDPermissionUser) {
+            return this.getLuckPermsUser(holder.getIdentifier());
+        }
+
+        return this.getLuckPermsGroup(holder.getIdentifier());
+    }
+
+    public User getLuckPermsUser(String identifier) {
+        User user = this.userCache.getIfPresent(identifier);
+        if (user != null) {
+            return user;
+        }
+
+        UUID uuid = null;
+        if (identifier.length() == 36) {
+            try {
+                uuid = UUID.fromString(identifier);
+            } catch (IllegalArgumentException e) {
+                e.printStackTrace();
+            }
+        }
+        if (uuid != null) {
+            user = this.getUserSubject(uuid);
+        }
+
+        if (user == null) {
+            user = this.luckPermsApi.getUser(identifier);
+        }
+        if (user != null) {
+            this.userCache.put(identifier, user);
+        }
+
+        return user;
+    }
+
+    public Group getLuckPermsGroup(String identifier) {
+        if (identifier.equalsIgnoreCase("default")) {
+            return this.luckPermsApi.getGroup("default");
+        }
+        Group group = this.groupCache.getIfPresent(identifier);
+        if (group != null) {
+            return group;
+        }
+
+        group = this.luckPermsApi.getGroup(identifier);
+        if (group != null) {
+            this.groupCache.put(identifier, group);
+        }
+        return group;
+    }
+
+    public Group getGroupSubject(String identifier) {
+        final Group group = this.luckPermsApi.getGroupManager().getGroup(identifier);
+        if (group != null) {
+            return group;
+        }
+
+        try {
+            return this.luckPermsApi.getGroupManager().loadGroup(identifier).get().orElse(null);
+        } catch (InterruptedException e) {
+            return null;
+        } catch (ExecutionException e) {
+            return null;
+        }
+    }
+
+   /* public GDPermissionUser getUserSubject(String name) {
+        final User user = this.luckPermsApi.getUserManager().getUser(name);
+        if (user != null) {
+            return new GDPermissionUser(user);
+        }
+
+        try {
+            final UUID uuid = this.luckPermsApi.getUserManager().lookupUuid(name).get();
+            if (uuid != null) {
+                return this.luckPermsApi.getUserManager().loadUser(uuid).get();
+            }
+            return null;
+        } catch (InterruptedException e) {
+            return null;
+        } catch (ExecutionException e) {
+            return null;
+        }
+    }*/
+
+    public UUID lookupUserUniqueId(String name) {
+        final User user = this.getLuckPermsUser(name);
+        if (user != null) {
+            return user.getUuid();
+        }
+        return null;
+    }
+
+    public User getUserSubject(UUID uuid) {
+        User user = this.luckPermsApi.getUserManager().getUser(uuid);
+        if (user != null) {
+            return user;
+        }
+
+        try {
+            user = this.luckPermsApi.getUserManager().loadUser(uuid).get();
+            if (user != null) {
+                return user;
+            }
+        } catch (InterruptedException e) {
+            return null;
+        } catch (ExecutionException e) {
+            return null;
+        }
+        return null;
+    }
+
+    public List<String> getAllLoadedPlayerNames() {
+        List<String> subjectList = new ArrayList<>();
+        for (User user : this.luckPermsApi.getUserManager().getLoadedUsers()) {
+            final String name = user.getName();
+            if (name != null) {
+                subjectList.add(name);
+            }
+        }
+        if (!subjectList.contains("public")) {
+            subjectList.add("public");
+        }
+        return subjectList;
+    } 
+
+    public List<String> getAllLoadedGroupNames() {
+        List<String> subjectList = new ArrayList<>();
+        for (Group group : this.luckPermsApi.getGroupManager().getLoadedGroups()) {
+            final String name = group.getName();
+            if (name != null) {
+                subjectList.add(name);
+            }
+        }
+        if (!subjectList.contains("public")) {
+            subjectList.add("public");
+        }
+        return subjectList;
+    } 
+
+    public void addActiveContexts(Set<Context> contexts, GDPermissionHolder permissionHolder) {
+        addActiveContexts(contexts, permissionHolder, null, null);
+    }
+
+    public void addActiveContexts(Set<Context> contexts, GDPermissionHolder permissionHolder, GDPlayerData playerData, Claim claim) {
+        if (playerData != null) {
+            playerData.ignoreActiveContexts = true;
+        }
+        final PermissionHolder luckPermsHolder = this.getLuckPermsHolder(permissionHolder);
+        if (luckPermsHolder instanceof Group) {
+            contexts.addAll(this.getGDContexts(this.luckPermsApi.getContextManager().getStaticContext().mutableCopy()));
+            return;
+        }
+
+        ImmutableContextSet contextSet = this.luckPermsApi.getContextManager().lookupApplicableContext((User) luckPermsHolder).orElse(null);
+        if (contextSet == null) {
+            contextSet = this.luckPermsApi.getContextManager().getStaticContext();
+        }
+        if (contextSet == null) {
+            return;
+        }
+        MutableContextSet activeContexts = contextSet.mutableCopy();
+        if (playerData != null && claim != null) {
+            final Claim parent = claim.getParent().orElse(null);
+            if (parent != null && claim.getData() != null && claim.getData().doesInheritParent()) {
+                activeContexts.remove(parent.getContext().getKey(), parent.getContext().getValue());
+            } else {
+                activeContexts.remove(claim.getContext().getKey(), claim.getContext().getValue());
+            }
+        }
+        contexts.addAll(this.getGDContexts(activeContexts));
+    }
+
+    public void clearPermissions(GDClaim claim) {
+        Map<Set<Context>, Map<String, Boolean>> permissionMap = this.getPermanentPermissions(GriefDefenderPlugin.DEFAULT_HOLDER);
+        for (Entry<Set<Context>, Map<String, Boolean>> mapEntry : permissionMap.entrySet()) {
+            for (Context context : mapEntry.getKey()) {
+                if (context.getKey().equalsIgnoreCase("gd_claim") && context.getValue().equalsIgnoreCase(claim.getUniqueId().toString())) {
+                    this.clearPermissions(GriefDefenderPlugin.DEFAULT_HOLDER, mapEntry.getKey());
+                    break;
+                }
+            }
+        }
+    }
+
+    public void clearPermissions(GDPermissionHolder holder, Context context) {
+        clearPermissions(holder, ImmutableSet.of(context));
+    }
+
+    public void clearPermissions(GDPermissionHolder holder, Set<Context> contexts) {
+        final PermissionHolder permissionHolder = this.getLuckPermsHolder(holder);
+        if (permissionHolder == null) {
+            return;
+        }
+
+        ImmutableContextSet set = ImmutableContextSet.fromEntries(contexts);
+        permissionHolder.clearNodes(set);
+        this.savePermissionHolder(permissionHolder);
+    }
+
+    public boolean holderHasPermission(GDPermissionHolder holder, String permission) {
+        final PermissionHolder permissionHolder = this.getLuckPermsHolder(holder);
+        if (permissionHolder == null) {
+            return false;
+        }
+
+        return permissionHolder.getCachedData().getPermissionData(Contexts.allowAll()).getPermissionValue(permission) == me.lucko.luckperms.api.Tristate.TRUE;
+    }
+
+    public Map<String, Boolean> getPermissions(GDPermissionHolder holder, Set<Context> contexts) {
+        ImmutableContextSet set = ImmutableContextSet.fromEntries(contexts);
+        Contexts context = Contexts.global().setContexts(set);
+        final PermissionHolder permissionHolder = this.getLuckPermsHolder(holder);
+        if (permissionHolder == null) {
+            return new HashMap<>();
+        }
+        PermissionData cachedData = permissionHolder.getCachedData().getPermissionData(context);
+        return cachedData.getImmutableBacking();
+    }
+
+    public Map<String, String> getOptions(GDPermissionHolder holder, Set<Context> contexts) {
+        ImmutableContextSet set = ImmutableContextSet.fromEntries(contexts);
+        Contexts context = Contexts.global().setContexts(set);
+        final PermissionHolder permissionHolder = this.getLuckPermsHolder(holder);
+        if (permissionHolder == null) {
+            return new HashMap<>();
+        }
+        MetaData cachedData = permissionHolder.getCachedData().getMetaData(context);
+        return cachedData.getMeta();
+    }
+
+    public Map<Set<Context>, Map<String, Boolean>> getPermanentPermissions(GDPermissionHolder holder) {
+        final PermissionHolder permissionHolder = this.getLuckPermsHolder(holder);
+        if (permissionHolder == null) {
+            return new HashMap<>();
+        }
+
+        final ImmutableCollection<Node> nodes = permissionHolder.getNodes().values();
+        Map<Set<Context>, Map<String, Boolean>> permanentPermissionMap = new TreeMap<Set<Context>, Map<String, Boolean>>(CONTEXT_COMPARATOR);
+        Map<ContextSet, Set<Context>> contextMap = new HashMap<>();
+        for (Node node : nodes) {
+            if (node.isMeta()) {
+                continue;
+            }
+
+            String serverName = node.getServer().orElse(null);
+            if (serverName != null && serverName.equalsIgnoreCase("global")) {
+                serverName = null;
+            }
+            Set<Context> contexts = null;
+            if (contextMap.get(node.getContexts()) == null) {
+                contexts = getGPContexts(node.getContexts());
+                if (serverName != null && !serverName.equalsIgnoreCase("undefined")) {
+                    contexts.add(new Context("server", serverName));
+                }
+                contextMap.put(node.getContexts(), contexts);
+            } else {
+                contexts = contextMap.get(node.getContexts());
+                if (serverName != null && !serverName.equalsIgnoreCase("undefined")) {
+                    contexts.add(new Context("server", serverName));
+                }
+            }
+            Map<String, Boolean> permissionEntry = permanentPermissionMap.get(contexts);
+            if (permissionEntry == null) {
+                permissionEntry = new HashMap<>();
+                permissionEntry.put(node.getPermission(), node.getValue());
+                permanentPermissionMap.put(contexts, permissionEntry);
+            } else {
+                permissionEntry.put(node.getPermission(), node.getValue());
+            }
+        }
+
+        return permanentPermissionMap;
+    }
+
+    public Map<Set<Context>, Map<String, Boolean>> getTransientPermissions(GDPermissionHolder holder) {
+        final PermissionHolder permissionHolder = this.getLuckPermsHolder(holder);
+        if (permissionHolder == null) {
+            return new HashMap<>();
+        }
+
+        final Set<? extends Node> nodes = permissionHolder.getTransientPermissions();
+        Map<Set<Context>, Map<String, Boolean>> transientPermissionMap = new TreeMap<Set<Context>, Map<String, Boolean>>(CONTEXT_COMPARATOR);
+        Map<ContextSet, Set<Context>> contextMap = new HashMap<>();
+        for (Node node : nodes) {
+            if (node.isMeta()) {
+                continue;
+            }
+
+            String serverName = node.getServer().orElse(null);
+            if (serverName != null && serverName.equalsIgnoreCase("global")) {
+                serverName = null;
+            }
+            Set<Context> contexts = null;
+            if (contextMap.get(node.getContexts()) == null) {
+                contexts = getGPContexts(node.getContexts());
+                if (serverName != null && !serverName.equalsIgnoreCase("undefined")) {
+                    contexts.add(new Context("server", serverName));
+                }
+                contextMap.put(node.getContexts(), contexts);
+            } else {
+                contexts = contextMap.get(node.getContexts());
+                if (serverName != null && !serverName.equalsIgnoreCase("undefined")) {
+                    contexts.add(new Context("server", serverName));
+                }
+            }
+            Map<String, Boolean> permissionEntry = transientPermissionMap.get(contexts);
+            if (permissionEntry == null) {
+                permissionEntry = new HashMap<>();
+                permissionEntry.put(node.getPermission(), node.getValue());
+                transientPermissionMap.put(contexts, permissionEntry);
+            } else {
+                permissionEntry.put(node.getPermission(), node.getValue());
+            }
+        }
+        return transientPermissionMap;
+    }
+
+    public Map<Set<Context>, Map<String, String>> getPermanentOptions(GDPermissionHolder holder) {
+        final PermissionHolder permissionHolder = this.getLuckPermsHolder(holder);
+        if (permissionHolder == null) {
+            return new HashMap<>();
+        }
+
+        final ImmutableCollection<Node> nodes = permissionHolder.getNodes().values();
+        Map<Set<Context>, Map<String, String>> permanentPermissionMap = new TreeMap<Set<Context>, Map<String, String>>(CONTEXT_COMPARATOR);
+        Map<ContextSet, Set<Context>> contextMap = new HashMap<>();
+        for (Node node : nodes) {
+            if (!node.isMeta()) {
+                continue;
+            }
+            String serverName = node.getServer().orElse(null);
+            if (serverName != null && serverName.equalsIgnoreCase("global")) {
+                serverName = null;
+            }
+            Set<Context> contexts = null;
+            if (contextMap.get(node.getContexts()) == null) {
+                contexts = getGPContexts(node.getContexts());
+                if (serverName != null && !serverName.equalsIgnoreCase("undefined")) {
+                    contexts.add(new Context("server", serverName));
+                }
+                contextMap.put(node.getContexts(), contexts);
+            } else {
+                contexts = contextMap.get(node.getContexts());
+                if (serverName != null && !serverName.equalsIgnoreCase("undefined")) {
+                    contexts.add(new Context("server", serverName));
+                }
+            }
+            Map<String, String> metaEntry = permanentPermissionMap.get(contexts);
+            if (metaEntry == null) {
+                metaEntry = new HashMap<>();
+                metaEntry.put(node.getMeta().getKey(), node.getMeta().getValue());
+                permanentPermissionMap.put(contexts, metaEntry);
+            } else {
+                metaEntry.put(node.getMeta().getKey(), node.getMeta().getValue());
+            }
+        }
+        return permanentPermissionMap;
+    }
+
+    public Map<Set<Context>, Map<String, String>> getTransientOptions(GDPermissionHolder holder) {
+        final PermissionHolder permissionHolder = this.getLuckPermsHolder(holder);
+        if (permissionHolder == null) {
+            return new HashMap<>();
+        }
+
+        final Set<? extends Node> nodes = permissionHolder.getTransientPermissions();
+        Map<Set<Context>, Map<String, String>> permanentPermissionMap = new TreeMap<Set<Context>, Map<String, String>>(CONTEXT_COMPARATOR);
+        Map<ContextSet, Set<Context>> contextMap = new HashMap<>();
+        for (Node node : nodes) {
+            if (!node.isMeta()) {
+                continue;
+            }
+            String serverName = node.getServer().orElse(null);
+            if (serverName != null && serverName.equalsIgnoreCase("global")) {
+                serverName = null;
+            }
+            Set<Context> contexts = null;
+            if (contextMap.get(node.getContexts()) == null) {
+                contexts = getGPContexts(node.getContexts());
+                if (serverName != null && !serverName.equalsIgnoreCase("undefined")) {
+                    contexts.add(new Context("server", serverName));
+                }
+                contextMap.put(node.getContexts(), contexts);
+            } else {
+                contexts = contextMap.get(node.getContexts());
+                if (serverName != null && !serverName.equalsIgnoreCase("undefined")) {
+                    contexts.add(new Context("server", serverName));
+                }
+            }
+            Map<String, String> metaEntry = permanentPermissionMap.get(contexts);
+            if (metaEntry == null) {
+                metaEntry = new HashMap<>();
+                metaEntry.put(node.getMeta().getKey(), node.getMeta().getValue());
+                permanentPermissionMap.put(contexts, metaEntry);
+            } else {
+                metaEntry.put(node.getMeta().getKey(), node.getMeta().getValue());
+            }
+        }
+        return permanentPermissionMap;
+    }
+
+    public Map<String, String> getPermanentOptions(GDPermissionHolder holder, Set<Context> contexts) {
+        final PermissionHolder permissionHolder = this.getLuckPermsHolder(holder);
+        if (permissionHolder == null) {
+            return new HashMap<>();
+        }
+
+        final Set<? extends Node> nodes = permissionHolder.getPermissions();
+        final Map<String, String> options = new HashMap<>();
+        for (Node node : nodes) {
+            if (!node.isMeta()) {
+                continue;
+            }
+
+            if (contexts == null) {
+                options.put(node.getMeta().getKey(), node.getMeta().getValue());
+            } else if (getGPContexts(node.getContexts()).containsAll(contexts)) {
+                options.put(node.getMeta().getKey(), node.getMeta().getValue());
+            }
+        }
+        return options;
+    }
+
+    public Map<String, String> getTransientOptions(GDPermissionHolder holder, Set<Context> contexts) {
+        final PermissionHolder permissionHolder = this.getLuckPermsHolder(holder);
+        if (permissionHolder == null) {
+            return new HashMap<>();
+        }
+
+        final Set<? extends Node> nodes = permissionHolder.getTransientPermissions();
+        final Map<String, String> options = new HashMap<>();
+        for (Node node : nodes) {
+            if (!node.isMeta()) {
+                continue;
+            }
+
+            if (contexts == null) {
+                options.put(node.getMeta().getKey(), node.getMeta().getValue());
+            } else if (getGPContexts(node.getContexts()).containsAll(contexts)) {
+                options.put(node.getMeta().getKey(), node.getMeta().getValue());
+            }
+        }
+        return options;
+    }
+
+    public Map<Set<Context>, Map<String, Boolean>> getAllPermissions(GDPermissionHolder holder) {
+        final PermissionHolder permissionHolder = this.getLuckPermsHolder(holder);
+        if (permissionHolder == null) {
+            return new HashMap<>();
+        }
+
+        final Set<? extends Node> nodes = permissionHolder.getAllNodes();
+        Map<Set<Context>, Map<String, Boolean>> permissionMap = new HashMap<>();
+        Map<ContextSet, Set<Context>> contextMap = new HashMap<>();
+        for (Node node : nodes) {
+            Set<Context> contexts = null;
+            if (contextMap.get(node.getContexts()) == null) {
+                contexts = getGPContexts(node.getContexts());
+                contextMap.put(node.getContexts(), contexts);
+            } else {
+                contexts = contextMap.get(node.getContexts());
+            }
+            Map<String, Boolean> permissionEntry = permissionMap.get(contexts);
+            if (permissionEntry == null) {
+                permissionEntry = new HashMap<>();
+                permissionEntry.put(node.getPermission(), node.getValue());
+                permissionMap.put(contexts, permissionEntry);
+            } else {
+                permissionEntry.put(node.getPermission(), node.getValue());
+            }
+        }
+        return permissionMap;
+    }
+
+    public Set<Context> getGPContexts(ContextSet contextSet) {
+        final Set<Context> gpContexts = new HashSet<>();
+        for (Map.Entry<String, String> mapEntry : contextSet.toSet()) {
+            if (mapEntry.getKey().startsWith("gd_") || mapEntry.getKey().equals("used_item") 
+                    || mapEntry.getKey().equals("source") || mapEntry.getKey().equals("target")
+                    || mapEntry.getKey().equals("world") || mapEntry.getKey().equals("server")
+                    || mapEntry.getKey().equals("state")) {
+                if (contextSet.containsKey(ContextKeys.CLAIM) && mapEntry.getKey().equals("server")) {
+                    continue;
+                }
+
+                gpContexts.add(new Context(mapEntry.getKey(), mapEntry.getValue()));
+            }
+        }
+        return gpContexts;
+    }
+
+    public Tristate getPermissionValue(GDPermissionHolder holder, String permission) {
+        ImmutableContextSet set = ImmutableContextSet.empty();
+        return this.getPermissionValue(holder, permission, set);
+    }
+
+    
+    public Tristate getPermissionValue(GDClaim claim, GDPermissionHolder holder, String permission, MutableContextSet contexts) {
+        return getPermissionValue(claim, holder, permission, this.getGDContexts(contexts));
+    }
+
+    public Tristate getPermissionValue(GDClaim claim, GDPermissionHolder holder, String permission, Set<Context> contexts) {
+        return getPermissionValue(claim, holder, permission, contexts, true);
+    }
+
+    public Tristate getPermissionValue(GDClaim claim, GDPermissionHolder holder, String permission, Set<Context> contexts, boolean checkTransient) {
+        final Set<Context> activeContexts = new HashSet<>();
+        this.addActiveContexts(activeContexts, holder, null, claim);
+        contexts.addAll(activeContexts);
+        final int contextHash =  Objects.hash(claim, holder, permission, contexts);
+        final Cache<Integer, Tristate> cache = PermissionHolderCache.getInstance().getOrCreatePermissionCache(holder);
+        Tristate result = cache.getIfPresent(contextHash);
+        if (result != null) {
+            return result;
+        }
+        // check persistent permissions first
+        Map<Set<Context>, Map<String, Boolean>> permanentPermissions = getPermanentPermissions(holder);
+        for (Entry<Set<Context>, Map<String, Boolean>> entry : permanentPermissions.entrySet()) {
+            if (entry.getKey().isEmpty()) {
+                continue;
+            }
+            boolean match = true;
+            for (Context context : entry.getKey()) {
+                if (!contexts.contains(context)) {
+                    match = false;
+                    break;
+                }
+            }
+            if (match) {
+                for (Map.Entry<String, Boolean> permEntry : entry.getValue().entrySet()) {
+                    if (FilenameUtils.wildcardMatch(permission, permEntry.getKey())) {
+                        final Tristate value = Tristate.fromBoolean(permEntry.getValue());
+                        cache.put(contextHash, value);
+                        return value;
+                    }
+                }
+                // If we get here, continue on normally
+                continue;
+            }
+        }
+
+        if (!checkTransient) {
+            return Tristate.UNDEFINED;
+        }
+
+        // check transient permissions last
+        Map<Set<Context>, Map<String, Boolean>> transientPermissions = getTransientPermissions(holder);
+        for (Entry<Set<Context>, Map<String, Boolean>> entry : transientPermissions.entrySet()) {
+            if (entry.getKey().isEmpty()) {
+                continue;
+            }
+            boolean match = true;
+            for (Context context : entry.getKey()) {
+                if (!contexts.contains(context)) {
+                    match = false;
+                    break;
+                }
+            }
+            if (match) {
+                for (Map.Entry<String, Boolean> permEntry : entry.getValue().entrySet()) {
+                    if (FilenameUtils.wildcardMatch(permission, permEntry.getKey())) {
+                        final Tristate value = Tristate.fromBoolean(permEntry.getValue());
+                        cache.put(contextHash, value);
+                        return value;
+                    }
+                }
+                // If we get here, continue on normally
+                continue;
+            }
+        }
+
+        cache.put(contextHash, Tristate.UNDEFINED);
+        return Tristate.UNDEFINED;
+    }
+
+    public Tristate getPermissionValueWithRequiredContexts(GDClaim claim, GDPermissionHolder holder, String permission, Set<Context> contexts, String contextFilter) {
+        Map<Set<Context>, Map<String, Boolean>> permanentPermissions = getPermanentPermissions(holder);
+        for (Entry<Set<Context>, Map<String, Boolean>> entry : permanentPermissions.entrySet()) {
+            if (entry.getKey().isEmpty()) {
+                continue;
+            }
+            boolean match = true;
+            for (Context context : entry.getKey()) {
+                if (!contexts.contains(context)) {
+                    match = false;
+                    break;
+                }
+            }
+
+            // Check for required contexts
+            for (Context context : contexts) {
+                if (!context.getKey().contains(contextFilter)) {
+                    if (!entry.getKey().contains(context)) {
+                        match = false;
+                        break;
+                    }
+                }
+            }
+            if (match) {
+                for (Map.Entry<String, Boolean> permEntry : entry.getValue().entrySet()) {
+                    if (FilenameUtils.wildcardMatch(permission, permEntry.getKey())) {
+                        final Tristate value = Tristate.fromBoolean(permEntry.getValue());
+                        return value;
+                    }
+                }
+            }
+        }
+        return Tristate.UNDEFINED;
+    }
+
+    public Tristate getPermissionValue(GDPermissionHolder holder, String permission, Set<Context> contexts) {
+        ImmutableContextSet contextSet = ImmutableContextSet.fromEntries(contexts);
+        return this.getPermissionValue(holder, permission, contextSet);
+    }
+
+    public Tristate getPermissionValue(GDPermissionHolder holder, String permission, ContextSet contexts) {
+        Contexts context = Contexts.global().setContexts(contexts);
+        final PermissionHolder permissionHolder = this.getLuckPermsHolder(holder);
+        if (permissionHolder == null) {
+            return Tristate.UNDEFINED;
+        }
+
+        PermissionData cachedData = permissionHolder.getCachedData().getPermissionData(context);
+        return getGDTristate(cachedData.getPermissionValue(permission));
+    }
+
+    // To set options, pass "meta.option".
+    @Override
+    public String getOptionValue(GDPermissionHolder holder, Option option, Set<Context> contexts) {
+        ImmutableContextSet set = ImmutableContextSet.fromEntries(contexts);
+        Contexts context = Contexts.global().setContexts(set);
+        final PermissionHolder permissionHolder = this.getLuckPermsHolder(holder);
+        if (permissionHolder == null) {
+            return null;
+        }
+
+        MetaData metaData = permissionHolder.getCachedData().getMetaData(context);
+        return metaData.getMeta().get(option.getPermission());
+    }
+
+    public PermissionResult setOptionValue(GDPermissionHolder holder, String permission, String value, Set<Context> contexts) {
+        DataMutateResult result = null;
+        ImmutableContextSet set = ImmutableContextSet.fromEntries(contexts);
+        Contexts context = Contexts.global().setContexts(set);
+        final PermissionHolder permissionHolder = this.getLuckPermsHolder(holder);
+        if (permissionHolder == null) {
+            return new GDPermissionResult(ResultTypes.FAILURE);
+        }
+
+        MetaData metaData = permissionHolder.getCachedData().getMetaData(context);
+        for (Map.Entry<String, String> mapEntry : metaData.getMeta().entrySet()) {
+            if (mapEntry.getKey().equalsIgnoreCase(permission)) {
+                // Always unset existing meta first
+                final Node node = this.luckPermsApi.getNodeFactory().makeMetaNode(permission, mapEntry.getValue()).withExtraContext(set).build();
+                result = permissionHolder.unsetPermission(node);
+            }
+        }
+
+        final Node node = this.luckPermsApi.getNodeFactory().makeMetaNode(permission, value).withExtraContext(set).build();
+        if (!value.equalsIgnoreCase("undefined")) {
+            result = permissionHolder.setPermission(node);
+        }
+        if (result != null && result.wasSuccess()) {
+            this.savePermissionHolder(permissionHolder);
+            return new GDPermissionResult(ResultTypes.SUCCESS);
+        }
+        return new GDPermissionResult(ResultTypes.FAILURE);
+    }
+
+    public PermissionResult setPermissionValue(GDPermissionHolder holder, Flag flag, Tristate value, Set<Context> contexts) {
+        final boolean result = setPermissionValue(holder, flag.getPermission(), value, contexts);
+        if (result) {
+            return new GDPermissionResult(ResultTypes.SUCCESS);
+        }
+        return new GDPermissionResult(ResultTypes.FAILURE);
+    }
+
+    public boolean setPermissionValue(GDPermissionHolder holder, String permission, Tristate value, Set<Context> contexts) {
+        DataMutateResult result = null;
+        ImmutableContextSet set = ImmutableContextSet.fromEntries(contexts);
+        final Node node = this.luckPermsApi.getNodeFactory().newBuilder(permission).setValue(value.asBoolean()).withExtraContext(set).build();
+        final PermissionHolder permissionHolder = this.getLuckPermsHolder(holder);
+        if (permissionHolder == null) {
+            return false;
+        }
+
+        if (value == Tristate.UNDEFINED) {
+            result = permissionHolder.unsetPermission(node);
+        } else {
+            result = permissionHolder.setPermission(node);
+        }
+
+        if (result.wasSuccess()) {
+            if (permissionHolder instanceof Group) {
+                final Group group = (Group) permissionHolder;
+                group.refreshCachedData();
+                for (User user :this.luckPermsApi.getUserManager().getLoadedUsers()) {
+                    user.refreshCachedData();
+                }
+                // If a group is changed, we invalidate all cache
+                PermissionHolderCache.getInstance().invalidateAllPermissionCache();
+            } else {
+                // We need to invalidate cache outside of LP listener so we can guarantee proper result returns
+                PermissionHolderCache.getInstance().getOrCreatePermissionCache(holder).invalidateAll();
+            }
+
+            this.savePermissionHolder(permissionHolder);
+        }
+        return result.wasSuccess();
+    }
+
+    public void setTransientOption(GDPermissionHolder holder, String permission, String value, Set<Context> contexts) {
+        MutableContextSet contextSet = MutableContextSet.fromEntries(contexts);
+        final PermissionHolder permissionHolder = this.getLuckPermsHolder(holder);
+        if (permissionHolder == null) {
+            return;
+        }
+
+        final Node node = this.luckPermsApi.getNodeFactory().makeMetaNode(permission, value).withExtraContext(contextSet).build();
+        permissionHolder.setTransientPermission(node);
+    }
+
+    public void setTransientPermission(GDPermissionHolder holder, String permission, Boolean value, Set<Context> contexts) {
+        MutableContextSet contextSet = MutableContextSet.fromEntries(contexts);
+        final PermissionHolder permissionHolder = this.getLuckPermsHolder(holder);
+        if (permissionHolder == null) {
+            return;
+        }
+
+        final Node node = this.luckPermsApi.getNodeFactory().newBuilder(permission).setValue(value).withExtraContext(contextSet).build();
+        permissionHolder.setTransientPermission(node);
+    }
+
+    public void savePermissionHolder(PermissionHolder holder) {
+        if (holder instanceof User) {
+            this.luckPermsApi.getUserManager().saveUser((User) holder);
+        } else {
+            this.luckPermsApi.getGroupManager().saveGroup((Group) holder);
+        }
+    }
+
+    public void refreshCachedData(GDPermissionHolder holder) {
+        final PermissionHolder permissionHolder = this.getLuckPermsHolder(holder);
+        if (permissionHolder == null) {
+            return;
+        }
+        permissionHolder.refreshCachedData();
+    }
+
+    public Set<Context> getGDContexts(ContextSet contextSet) {
+        final Set<Context> gdContexts = new HashSet<>();
+        contextSet.forEach(entry -> {
+            gdContexts.add(new Context(entry.getKey(), entry.getValue()));
+        });
+
+        return gdContexts;
+    }
+
+    public Tristate getGDTristate(me.lucko.luckperms.api.Tristate state) {
+        if (state == me.lucko.luckperms.api.Tristate.TRUE) {
+            return Tristate.TRUE;
+        }
+        if (state == me.lucko.luckperms.api.Tristate.FALSE) {
+            return Tristate.FALSE;
+        }
+        return Tristate.UNDEFINED;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/provider/MCClansProvider.java b/sponge/src/main/java/com/griefdefender/provider/MCClansProvider.java
new file mode 100644
index 0000000..88db648
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/provider/MCClansProvider.java
@@ -0,0 +1,41 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.provider;
+
+import nl.riebie.mcclans.api.ClanService;
+import org.spongepowered.api.Sponge;
+
+public class MCClansProvider {
+
+    private final ClanService clanService;
+
+    public MCClansProvider() {
+        this.clanService = Sponge.getServiceManager().provide(ClanService.class).orElse(null);
+    }
+
+    public ClanService getClanService() {
+        return this.clanService;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/provider/NucleusProvider.java b/sponge/src/main/java/com/griefdefender/provider/NucleusProvider.java
new file mode 100644
index 0000000..f53740f
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/provider/NucleusProvider.java
@@ -0,0 +1,84 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.provider;
+
+import com.griefdefender.GDBootstrap;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.storage.BaseStorage;
+import com.griefdefender.util.SpongeUtil;
+import io.github.nucleuspowered.nucleus.api.NucleusAPI;
+import io.github.nucleuspowered.nucleus.api.exceptions.PluginAlreadyRegisteredException;
+import io.github.nucleuspowered.nucleus.api.service.NucleusMessageTokenService;
+import io.github.nucleuspowered.nucleus.api.service.NucleusPrivateMessagingService;
+import net.kyori.text.Component;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.plugin.PluginContainer;
+
+import java.util.Optional;
+
+public class NucleusProvider {
+
+
+    public NucleusProvider() {
+    }
+
+    public static Optional<NucleusPrivateMessagingService> getPrivateMessagingService() {
+        return NucleusAPI.getPrivateMessagingService();
+    }
+
+    public void registerTokens() {
+        NucleusMessageTokenService messageTokenService = NucleusAPI.getMessageTokenService();
+        PluginContainer pc = GDBootstrap.getInstance().pluginContainer;
+        final BaseStorage dataStore = GriefDefenderPlugin.getInstance().dataStore;
+        try {
+            messageTokenService.register(GDBootstrap.getInstance().pluginContainer,
+                    (tokenInput, commandSource, variables) -> {
+                        // Each token will require something like this.
+
+                        // This token, town, will give the name of the town the player is currently in.
+                        // Will be registered in Nucleus as "{{pl:griefdefender:town}}", with the shortened version of "{{town}}"
+                        // This will return the name of the town the player is currently in.
+                        if (tokenInput.equalsIgnoreCase("town") && commandSource instanceof Player) {
+                            Player player = (Player) commandSource;
+                            final GDPlayerData data = dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+
+                            // Shamelessly stolen from PlayerEventHandler
+                            if (data.inTown) {
+                                final Component component = dataStore.getClaimAtPlayer(data, player.getLocation()).getTownClaim().getTownData().getTownTag().orElse(null);
+                                return Optional.ofNullable(SpongeUtil.getSpongeText(component));
+                            }
+                        }
+
+                        return Optional.empty();
+                    });
+        } catch (PluginAlreadyRegisteredException ignored) {
+            // already been done.
+        }
+
+        // register {{town}} from {{pl:griefdefender:town}}
+        messageTokenService.registerPrimaryToken("town", pc, "town");
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/provider/PermissionProvider.java b/sponge/src/main/java/com/griefdefender/provider/PermissionProvider.java
new file mode 100644
index 0000000..abdb767
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/provider/PermissionProvider.java
@@ -0,0 +1,337 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.provider;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.api.Tristate;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.permission.Context;
+import com.griefdefender.api.permission.PermissionResult;
+import com.griefdefender.api.permission.flag.Flag;
+import com.griefdefender.api.permission.option.Option;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.permission.GDPermissionHolder;
+
+/**
+ * Represents a provider of permission data.
+ * 
+ * <p>This is the interface that a permissions plugin must implement and
+ * to provide permissions lookups for GriefDefender.</p>
+ */
+public interface PermissionProvider {
+
+    /**
+     * Checks if the group identifier exists.
+     * 
+     * @param identifier The group identifier
+     * @return whether the group exists
+     */
+    boolean hasGroupSubject(String identifier);
+
+    /**
+     * Performs a lookup for a UUID with matching 
+     * username.
+     * 
+     * @param name The username to search with
+     * @return The user uuid if available
+     */
+    @Nullable UUID lookupUserUniqueId(String name);
+
+    /**
+     * Retrieves an immutable list of all loaded player
+     * names that exist in permissions.
+     * 
+     * @return An immutable list of player names or empty if none.
+     */
+    List<String> getAllLoadedPlayerNames();
+
+    /**
+     * Retrieves an immutable list of all loaded group
+     * names that exist in permissions.
+     * 
+     * @return An immutable list of group names or empty if none.
+     */
+    List<String> getAllLoadedGroupNames();
+
+    /**
+     * Appends all active contexts to passed context set currently
+     * active on permission holder.
+     * 
+     * @param contexts The set to append to
+     * @param permissionHolder The holder to check active contexts
+     */
+    void addActiveContexts(Set<Context> contexts, GDPermissionHolder permissionHolder);
+
+    /**
+     * Appends all active contexts to passed context set currently
+     * active on permission holder.
+     * 
+     * @param contexts The set to append to
+     * @param permissionHolder The holder to check active contexts
+     * @param playerData The player data
+     * @param claim The claim
+     */
+    void addActiveContexts(Set<Context> contexts, GDPermissionHolder permissionHolder, GDPlayerData playerData, Claim claim);
+
+    /**
+     * Clears all permissions that contain {@link Claim#getContext()}
+     * from passed {@link Claim}.
+     * 
+     * @param claim The claim
+     */
+    void clearPermissions(GDClaim claim);
+
+    /**
+     * Clears all permissions that contain {@link Context}
+     * from passed holder.
+     * 
+     * @param claim The claim
+     */
+    void clearPermissions(GDPermissionHolder holder, Context context);
+
+    /**
+     * Clears all permissions that contain {@link Context}'s
+     * from passed player.
+     * 
+     * @param claim The claim
+     */
+    void clearPermissions(GDPermissionHolder holder, Set<Context> contexts);
+
+    /**
+     * Checks if holder has permission.
+     * 
+     * @param permission The permission
+     * @return whether the holder has permission
+     */
+    boolean holderHasPermission(GDPermissionHolder holder, String permission);
+
+    /**
+     * Gets all cached permissions of holder that contain specific {@link Context}'s.
+     * 
+     * @param holder The holder
+     * @param contexts The contexts required
+     * @return An immutable map of cached permissions or empty if none.
+     */
+    Map<String, Boolean> getPermissions(GDPermissionHolder holder, Set<Context> contexts);
+
+    /**
+     * Gets all cached options of holder that contain specific {@link Context}'s.
+     * 
+     * @param holder The holder
+     * @param contexts The contexts required
+     * @return An immutable map of cached options or empty if none.
+     */
+    Map<String, String> getOptions(GDPermissionHolder holder, Set<Context> contexts);
+
+    /**
+     * Gets all persisted permissions with associated contexts of holder.
+     * 
+     * @param holder The holder
+     * @return An immutable map of persisted permissions or empty if none.
+     */
+    Map<Set<Context>, Map<String, Boolean>> getPermanentPermissions(GDPermissionHolder holder);
+
+    /**
+     * Gets all transient permissions with associated contexts of holder.
+     * 
+     * @param holder The holder
+     * @return An immutable map of transient permissions or empty if none.
+     */
+    Map<Set<Context>, Map<String, Boolean>> getTransientPermissions(GDPermissionHolder holder);
+
+    /**
+     * Gets all persisted options with associated contexts of holder.
+     * 
+     * @param holder The holder
+     * @return An immutable map of persisted options or empty if none.
+     */
+    Map<Set<Context>, Map<String, String>> getPermanentOptions(GDPermissionHolder holder);
+
+    /**
+     * Gets all transient options with associated contexts of holder.
+     * 
+     * @param holder The holder
+     * @return An immutable map of transient options or empty if none.
+     */
+    Map<Set<Context>, Map<String, String>> getTransientOptions(GDPermissionHolder holder);
+
+    /**
+     * Gets all persisted options and associated values of holder.
+     * 
+     * @param holder The holder
+     * @return An immutable map of persisted options or empty if none.
+     */
+    Map<String, String> getPermanentOptions(GDPermissionHolder holder, Set<Context> contexts);
+
+    /**
+     * Gets all transient options and associated values of holder.
+     * 
+     * @param holder The holder
+     * @return An immutable map of transient options or empty if none.
+     */
+    Map<String, String> getTransientOptions(GDPermissionHolder holder, Set<Context> contexts);
+
+    /**
+     * Gets all persisted permissions, including inherited nodes, with associated contexts of holder.
+     * 
+     * @param holder The holder
+     * @return An immutable map of persisted permissions or empty if none.
+     */
+    Map<Set<Context>, Map<String, Boolean>> getAllPermissions(GDPermissionHolder holder);
+
+    /**
+     * Gets the current value of a permission assigned to a holder.
+     * 
+     * @param holder The holder
+     * @param permission The permission to check
+     * @return The permission value
+     */
+    Tristate getPermissionValue(GDPermissionHolder holder, String permission);
+
+    /**
+     * Gets the current value of a permission assigned to a holder.
+     * 
+     * @param claim The current claim
+     * @param holder The holder
+     * @param permission The permission to check
+     * @param contexts The contexts
+     * @return The permission value
+     */
+    Tristate getPermissionValue(GDClaim claim, GDPermissionHolder holder, String permission, Set<Context> contexts);
+
+    /**
+     * Gets the current value of a permission assigned to a holder.
+     * 
+     * @param claim The current claim
+     * @param holder The holder
+     * @param permission The permission to check
+     * @param contexts The contexts
+     * @param checkTransient Whether to check transient permissions
+     * @return The permission value
+     */
+    Tristate getPermissionValue(GDClaim claim, GDPermissionHolder holder, String permission, Set<Context> contexts, boolean checkTransient);
+
+    /**
+     * Gets the current value of a permission assigned to a holder.
+     * 
+     * @param holder The holder
+     * @param permission The permission to check
+     * @param contexts The contexts
+     * @return The permission value
+     */
+    Tristate getPermissionValue(GDPermissionHolder holder, String permission, Set<Context> contexts);
+
+    /**
+     * Gets the current value of a permission that contains all passed contexts
+     * assigned to a holder.
+     * 
+     * @param claim The current claim
+     * @param holder The holder
+     * @param permission The permission to check
+     * @param contexts The contexts required
+     * @param contextFilter The context key to ignore for required contexts
+     * @return The permission value
+     */
+    Tristate getPermissionValueWithRequiredContexts(GDClaim claim, GDPermissionHolder holder, String permission, Set<Context> contexts, String contextFilter);
+
+    /**
+     * Gets the current value of an option assigned to a holder.
+     * 
+     * @param holder The holder
+     * @param permission The permission to check
+     * @param contexts The contexts
+     * @return The option value
+     */
+    String getOptionValue(GDPermissionHolder holder, Option option, Set<Context> contexts);
+
+    /**
+     * Sets an option and value with contexts to a holder.
+     * 
+     * @param holder The holder
+     * @param permission The permission
+     * @param value The value
+     * @param contexts The contexts
+     * @return The permission result
+     */
+    PermissionResult setOptionValue(GDPermissionHolder holder, String permission, String value, Set<Context> contexts);
+
+    /**
+     * Sets a permission and value with contexts to a holder.
+     * 
+     * @param holder The holder
+     * @param flag The flag to use for permission
+     * @param value The value
+     * @param contexts The contexts
+     * @return The permission result
+     */
+    PermissionResult setPermissionValue(GDPermissionHolder holder, Flag flag, Tristate value, Set<Context> contexts);
+
+    /**
+     * Sets a permission and value with contexts to a holder.
+     * 
+     * @param holder The holder
+     * @param permission The permission
+     * @param value The value
+     * @param contexts The contexts
+     * @return Whether the set permission operation was successful
+     */
+    boolean setPermissionValue(GDPermissionHolder holder, String permission, Tristate value, Set<Context> contexts);
+
+    /**
+     * Sets a transient option and value with contexts to a holder.
+     * 
+     * @param holder The holder
+     * @param permission The permission
+     * @param value The value
+     * @param contexts The contexts
+     * @return Whether the set permission operation was successful
+     */
+    void setTransientOption(GDPermissionHolder holder, String permission, String value, Set<Context> contexts);
+
+    /**
+     * Sets a transient permission and value with contexts to a holder.
+     * 
+     * @param holder The holder
+     * @param permission The permission
+     * @param value The value
+     * @param contexts The contexts
+     * @return Whether the set permission operation was successful
+     */
+    void setTransientPermission(GDPermissionHolder holder, String permission, Boolean value, Set<Context> contexts);
+
+    /**
+     * Refreshes all cached permission data of holder.
+     * 
+     * @param holder The holder
+     */
+    void refreshCachedData(GDPermissionHolder holder);
+}
diff --git a/sponge/src/main/java/com/griefdefender/registry/ChatTypeRegistryModule.java b/sponge/src/main/java/com/griefdefender/registry/ChatTypeRegistryModule.java
new file mode 100644
index 0000000..b52f082
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/registry/ChatTypeRegistryModule.java
@@ -0,0 +1,91 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.registry;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.griefdefender.GDChatType;
+import com.griefdefender.api.ChatType;
+import com.griefdefender.api.ChatTypes;
+import com.griefdefender.api.registry.CatalogRegistryModule;
+import com.griefdefender.util.RegistryHelper;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Optional;
+
+public class ChatTypeRegistryModule implements CatalogRegistryModule<ChatType> {
+
+    private static ChatTypeRegistryModule instance;
+
+    public static ChatTypeRegistryModule getInstance() {
+        return instance;
+    }
+
+    private final Map<String, ChatType> registryMap = new HashMap<>();
+
+    @Override
+    public Optional<ChatType> getById(String id) {
+        if (id == null) {
+            return Optional.empty();
+        }
+
+        if (id.contains("griefdefender.")) {
+            id = id.replace("griefdefender.", "griefdefender:");
+        }
+        if (!id.contains(":")) {
+            id = "griefdefender:" + id;
+        }
+
+        return Optional.ofNullable(this.registryMap.get(checkNotNull(id)));
+    }
+
+    @Override
+    public Collection<ChatType> getAll() {
+        return this.registryMap.values();
+    }
+
+    @Override
+    public void registerDefaults() {
+        RegistryHelper.mapFields(ChatTypes.class, input -> {
+            final String name = input.toLowerCase().replace("_", "-");
+            final String id = "griefdefender:" + name;
+            final ChatType type = new GDChatType(id, name);
+            this.registryMap.put(id, type);
+            return type;
+        });
+    }
+
+    @Override
+    public void registerCustomType(ChatType type) {
+        this.registryMap.put(type.getId().toLowerCase(Locale.ENGLISH), type);
+    }
+
+    static {
+        instance = new ChatTypeRegistryModule();
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/registry/ClaimTypeRegistryModule.java b/sponge/src/main/java/com/griefdefender/registry/ClaimTypeRegistryModule.java
new file mode 100644
index 0000000..899754b
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/registry/ClaimTypeRegistryModule.java
@@ -0,0 +1,91 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.registry;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.griefdefender.api.claim.ClaimType;
+import com.griefdefender.api.claim.ClaimTypes;
+import com.griefdefender.api.registry.CatalogRegistryModule;
+import com.griefdefender.claim.GDClaimType;
+import com.griefdefender.util.RegistryHelper;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Optional;
+
+public class ClaimTypeRegistryModule implements CatalogRegistryModule<ClaimType> {
+
+    private static ClaimTypeRegistryModule instance;
+
+    public static ClaimTypeRegistryModule getInstance() {
+        return instance;
+    }
+
+    private final Map<String, ClaimType> registryMap = new HashMap<>();
+
+    @Override
+    public Optional<ClaimType> getById(String id) {
+        if (id == null) {
+            return Optional.empty();
+        }
+
+        if (id.contains("griefdefender.")) {
+            id = id.replace("griefdefender.", "griefdefender:");
+        }
+        if (!id.contains(":")) {
+            id = "griefdefender:" + id;
+        }
+
+        return Optional.ofNullable(this.registryMap.get(checkNotNull(id)));
+    }
+
+    @Override
+    public Collection<ClaimType> getAll() {
+        return this.registryMap.values();
+    }
+
+    @Override
+    public void registerDefaults() {
+        RegistryHelper.mapFields(ClaimTypes.class, input -> {
+            final String name = input.toLowerCase().replace("_", "-");
+            final String id = "griefdefender:" + name;
+            final ClaimType type = new GDClaimType(id, name);
+            this.registryMap.put(id, type);
+            return type;
+        });
+    }
+
+    @Override
+    public void registerCustomType(ClaimType type) {
+        this.registryMap.put(type.getId().toLowerCase(Locale.ENGLISH), type);
+    }
+
+    static {
+        instance = new ClaimTypeRegistryModule();
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/registry/CreateModeTypeRegistryModule.java b/sponge/src/main/java/com/griefdefender/registry/CreateModeTypeRegistryModule.java
new file mode 100644
index 0000000..83a338c
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/registry/CreateModeTypeRegistryModule.java
@@ -0,0 +1,91 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.registry;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.griefdefender.api.permission.option.type.CreateModeType;
+import com.griefdefender.api.permission.option.type.CreateModeTypes;
+import com.griefdefender.api.registry.CatalogRegistryModule;
+import com.griefdefender.permission.option.type.GDCreateModeType;
+import com.griefdefender.util.RegistryHelper;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+public class CreateModeTypeRegistryModule implements CatalogRegistryModule<CreateModeType> {
+
+    private static CreateModeTypeRegistryModule instance;
+
+    public static CreateModeTypeRegistryModule getInstance() {
+        return instance;
+    }
+
+    private final Map<String, CreateModeType> registryMap = new HashMap<>();
+
+    @Override
+    public Optional<CreateModeType> getById(String id) {
+        if (id == null) {
+            return Optional.empty();
+        }
+
+        if (id.contains("griefdefender.")) {
+            id = id.replace("griefdefender.", "griefdefender:");
+        }
+        if (!id.contains(":")) {
+            id = "griefdefender:" + id;
+        }
+
+        return Optional.ofNullable(this.registryMap.get(checkNotNull(id)));
+    }
+
+    @Override
+    public Collection<CreateModeType> getAll() {
+        return this.registryMap.values();
+    }
+
+    @Override
+    public void registerDefaults() {
+        RegistryHelper.mapFields(CreateModeTypes.class, input -> {
+            final String name = input.toLowerCase().replace("_", "-");
+            final String id = "griefdefender:" + name;
+            final CreateModeType type = new GDCreateModeType(id, name);
+            this.registryMap.put(id, type);
+            return type;
+        });
+    }
+
+    @Override
+    public void registerCustomType(CreateModeType type) {
+        // TODO Auto-generated method stub
+        
+    }
+
+    static {
+        instance = new CreateModeTypeRegistryModule();
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/registry/FlagRegistryModule.java b/sponge/src/main/java/com/griefdefender/registry/FlagRegistryModule.java
new file mode 100644
index 0000000..372b218
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/registry/FlagRegistryModule.java
@@ -0,0 +1,92 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.registry;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.permission.flag.Flag;
+import com.griefdefender.api.permission.flag.Flags;
+import com.griefdefender.api.registry.CatalogRegistryModule;
+import com.griefdefender.permission.flag.GDFlag;
+import com.griefdefender.util.RegistryHelper;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Optional;
+
+public class FlagRegistryModule implements CatalogRegistryModule<Flag> {
+
+    private static FlagRegistryModule instance;
+
+    public static FlagRegistryModule getInstance() {
+        return instance;
+    }
+
+    private final Map<String, Flag> registryMap = new HashMap<>();
+
+    @Override
+    public Optional<Flag> getById(String id) {
+        if (id == null) {
+            return Optional.empty();
+        }
+
+        id = id.replace("griefdefender.flag.", "");
+        if (!id.contains(":")) {
+            id = "griefdefender:" + id;
+        }
+
+        return Optional.ofNullable(this.registryMap.get(checkNotNull(id)));
+    }
+
+    @Override
+    public Collection<Flag> getAll() {
+        return this.registryMap.values();
+    }
+
+    @Override
+    public void registerDefaults() {
+        RegistryHelper.mapFields(Flags.class, input -> {
+            final String name = input.toLowerCase().replace("_", "-");
+            final String id = "griefdefender:" + name;
+            final Flag flag = new GDFlag(id, name);
+            this.registryMap.put(id, flag);
+            return flag;
+        });
+    }
+
+    @Override
+    public void registerCustomType(Flag type) {
+        this.registryMap.put(type.getId().toLowerCase(Locale.ENGLISH), type);
+        GriefDefenderPlugin.getGlobalConfig().getConfig().permissionCategory.refreshFlags();
+        GriefDefenderPlugin.getInstance().dataStore.setDefaultGlobalPermissions();
+    }
+
+    static {
+        instance = new FlagRegistryModule();
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/registry/GDRegistry.java b/sponge/src/main/java/com/griefdefender/registry/GDRegistry.java
new file mode 100644
index 0000000..8aee539
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/registry/GDRegistry.java
@@ -0,0 +1,116 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.registry;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableList;
+import com.google.inject.Singleton;
+import com.griefdefender.api.CatalogType;
+import com.griefdefender.api.Registry;
+import com.griefdefender.api.claim.ClaimType;
+import com.griefdefender.api.claim.ShovelType;
+import com.griefdefender.api.claim.TrustType;
+import com.griefdefender.api.permission.ResultType;
+import com.griefdefender.api.permission.flag.Flag;
+import com.griefdefender.api.permission.option.Option;
+import com.griefdefender.api.registry.CatalogRegistryModule;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.IdentityHashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Optional;
+
+@Singleton
+public class GDRegistry implements Registry {
+
+    protected final static Map<Class<? extends CatalogType>, CatalogRegistryModule<?>> catalogRegistryMap = new IdentityHashMap<>();
+    private static final Map<Class<?>, Supplier<?>> BUILDER_SUPPLIERS = new IdentityHashMap<>();
+
+    static {
+        catalogRegistryMap.put(ClaimType.class, (CatalogRegistryModule<ClaimType>) ClaimTypeRegistryModule.getInstance());
+        catalogRegistryMap.put(Flag.class, (CatalogRegistryModule<Flag>) FlagRegistryModule.getInstance());
+        catalogRegistryMap.put(ResultType.class, (CatalogRegistryModule<ResultType>) ResultTypeRegistryModule.getInstance());
+        catalogRegistryMap.put(Option.class, (CatalogRegistryModule<Option>) OptionRegistryModule.getInstance());
+        catalogRegistryMap.put(ShovelType.class, (CatalogRegistryModule<ShovelType>)ShovelTypeRegistryModule.getInstance());
+        catalogRegistryMap.put(TrustType.class, (CatalogRegistryModule<TrustType>)TrustTypeRegistryModule.getInstance());
+    }
+
+    @Override
+    public <T> Registry registerBuilderSupplier(Class<T> builderClass, Supplier<? extends T> supplier) {
+        checkArgument(!BUILDER_SUPPLIERS.containsKey(builderClass), "Already registered a builder supplier!");
+        BUILDER_SUPPLIERS.put(builderClass, supplier);
+        return this;
+    }
+
+    @Override
+    public <T> T createBuilder(Class<T> builderClass) throws IllegalArgumentException {
+        final Supplier<?> supplier = BUILDER_SUPPLIERS.get(builderClass);
+        checkArgument(supplier != null, "Could not find a Supplier for the provided builder class: " + builderClass.getCanonicalName());
+        return (T) supplier.get();
+    }
+
+    @Override
+    public <T extends CatalogType> Optional<T> getType(Class<T> typeClass, String id) {
+        CatalogRegistryModule<T> registryModule = getRegistryModuleFor(typeClass).orElse(null);
+        if (registryModule == null) {
+            return Optional.empty();
+        }
+        return registryModule.getById(id.toLowerCase(Locale.ENGLISH));
+    }
+
+    @Override
+    public <T extends CatalogType> Collection<T> getAllOf(Class<T> typeClass) {
+        CatalogRegistryModule<T> registryModule = getRegistryModuleFor(typeClass).orElse(null);
+        if (registryModule == null) {
+            return Collections.emptyList();
+        }
+        return registryModule.getAll();
+    }
+
+    @Override
+    public <T extends CatalogType> Collection<T> getAllFor(String pluginId, Class<T> typeClass) {
+        final CatalogRegistryModule<T> registryModule = getRegistryModuleFor(typeClass).orElse(null);
+        if (registryModule == null) {
+            return Collections.emptyList();
+        }
+        ImmutableList.Builder<T> builder = ImmutableList.builder();
+        registryModule.getAll()
+                .stream()
+                .filter(type -> pluginId.equals(type.getId().split(":")[0]))
+                .forEach(builder::add);
+
+        return builder.build();
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public <T extends CatalogType> Optional<CatalogRegistryModule<T>> getRegistryModuleFor(Class<T> catalogClass) {
+        return Optional.ofNullable((CatalogRegistryModule<T>) catalogRegistryMap.get(catalogClass));
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/registry/GameModeTypeRegistryModule.java b/sponge/src/main/java/com/griefdefender/registry/GameModeTypeRegistryModule.java
new file mode 100644
index 0000000..76327c3
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/registry/GameModeTypeRegistryModule.java
@@ -0,0 +1,91 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.registry;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.griefdefender.api.permission.option.type.GameModeType;
+import com.griefdefender.api.permission.option.type.GameModeTypes;
+import com.griefdefender.api.registry.CatalogRegistryModule;
+import com.griefdefender.permission.option.type.GDGameModeType;
+import com.griefdefender.util.RegistryHelper;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+public class GameModeTypeRegistryModule implements CatalogRegistryModule<GameModeType> {
+
+    private static GameModeTypeRegistryModule instance;
+
+    public static GameModeTypeRegistryModule getInstance() {
+        return instance;
+    }
+
+    private final Map<String, GameModeType> registryMap = new HashMap<>();
+
+    @Override
+    public Optional<GameModeType> getById(String id) {
+        if (id == null) {
+            return Optional.empty();
+        }
+
+        if (id.contains("griefdefender.")) {
+            id = id.replace("griefdefender.", "griefdefender:");
+        }
+        if (!id.contains(":")) {
+            id = "griefdefender:" + id;
+        }
+
+        return Optional.ofNullable(this.registryMap.get(checkNotNull(id)));
+    }
+
+    @Override
+    public Collection<GameModeType> getAll() {
+        return this.registryMap.values();
+    }
+
+    @Override
+    public void registerDefaults() {
+        RegistryHelper.mapFields(GameModeTypes.class, input -> {
+            final String name = input.toLowerCase().replace("_", "-");
+            final String id = "griefdefender:" + name;
+            final GameModeType type = new GDGameModeType(id, name);
+            this.registryMap.put(id, type);
+            return type;
+        });
+    }
+
+    @Override
+    public void registerCustomType(GameModeType type) {
+        // TODO Auto-generated method stub
+        
+    }
+
+    static {
+        instance = new GameModeTypeRegistryModule();
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/registry/OptionRegistryModule.java b/sponge/src/main/java/com/griefdefender/registry/OptionRegistryModule.java
new file mode 100644
index 0000000..12257d4
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/registry/OptionRegistryModule.java
@@ -0,0 +1,141 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.registry;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.griefdefender.api.Tristate;
+import com.griefdefender.api.permission.option.Option;
+import com.griefdefender.api.permission.option.Options;
+import com.griefdefender.api.permission.option.type.CreateModeType;
+import com.griefdefender.api.permission.option.type.GameModeType;
+import com.griefdefender.api.permission.option.type.WeatherType;
+import com.griefdefender.api.registry.CatalogRegistryModule;
+import com.griefdefender.permission.option.GDOption;
+import com.griefdefender.util.RegistryHelper;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Optional;
+
+@SuppressWarnings("rawtypes")
+public class OptionRegistryModule implements CatalogRegistryModule<Option> {
+
+    private static OptionRegistryModule instance;
+
+    public static OptionRegistryModule getInstance() {
+        return instance;
+    }
+
+    static {
+        instance = new OptionRegistryModule();
+    }
+
+    protected final Map<String, Option> registryMap = new HashMap<>();
+    protected final Map<String, Option> customMap = new HashMap<>();
+
+    @Override
+    public Optional<Option> getById(String id) {
+        if (id == null) {
+            return Optional.empty();
+        }
+
+        if (id.contains("griefdefender.")) {
+            id = id.replace("griefdefender.", "griefdefender:");
+        }
+        if (!id.contains(":")) {
+            id = "griefdefender:" + id;
+        }
+ 
+        return Optional.ofNullable(this.registryMap.get(checkNotNull(id)));
+    }
+
+    @Override
+    public Collection<Option> getAll() {
+        return this.registryMap.values();
+    }
+
+    public Map<String, Option> getCustomAdditions() {
+        return this.customMap;
+    }
+
+    @Override
+    public void registerDefaults() {
+        this.createKey("griefdefender:abandon-delay", "abandon-delay", Integer.class);
+        this.createKey("griefdefender:abandon-return-ratio", "abandon-return-ratio", Double.class);
+        this.createKey("griefdefender:create-limit", "create-limit", Integer.class);
+        this.createKey("griefdefender:expiration", "expiration", Integer.class);
+        this.createKey("griefdefender:max-size-x", "max-size-x", Integer.class);
+        this.createKey("griefdefender:max-size-y", "max-size-y", Integer.class);
+        this.createKey("griefdefender:max-size-z", "max-size-z", Integer.class);
+        this.createKey("griefdefender:min-size-x", "min-size-x", Integer.class);
+        this.createKey("griefdefender:min-size-y", "min-size-y", Integer.class);
+        this.createKey("griefdefender:min-size-z", "min-size-z", Integer.class);
+        this.createKey("griefdefender:tax-expiration", "tax-expiration", Integer.class);
+        this.createKey("griefdefender:tax-expiration-days-keep", "tax-expiration-days-keep", Integer.class);
+        this.createKey("griefdefender:tax-rate", "tax-rate", Double.class);
+        this.createKey("griefdefender:blocks-accrued-per-hour", "blocks-accrued-per-hour", Integer.class);
+        this.createKey("griefdefender:chest-expiration", "chest-expiration", Integer.class);
+        this.createKey("griefdefender:create-mode", "create-mode", CreateModeType.class);
+        this.createKey("griefdefender:economy-block-cost", "economy-block-cost", Double.class);
+        this.createKey("griefdefender:economy-block-sell-return", "economy-block-sell-return", Double.class);
+        this.createKey("griefdefender:initial-blocks", "initial-blocks", Integer.class);
+        this.createKey("griefdefender:max-accrued-blocks", "max-accrued-blocks", Integer.class);
+        this.createKey("griefdefender:max-level", "max-level", Integer.class);
+        this.createKey("griefdefender:min-level", "min-level", Integer.class);
+        this.createKey("griefdefender:radius-list", "radius-list", Integer.class);
+        this.createKey("griefdefender:radius-inspect", "radius-inspect", Integer.class);
+        this.createKey("griefdefender:raid", "raid", Boolean.class);
+        this.createKey("griefdefender:spawn-limit", "spawn-limit", Integer.class);
+        this.createKey("griefdefender:player-command", "player-command", String.class);
+        this.createKey("griefdefender:player-deny-flight", "player-deny-flight", Boolean.class);
+        this.createKey("griefdefender:player-deny-godmode", "player-deny-godmode", Boolean.class);
+        this.createKey("griefdefender:player-deny-hunger", "player-deny-hunger", Boolean.class);
+        this.createKey("griefdefender:player-gamemode", "player-gamemode", GameModeType.class);
+        this.createKey("griefdefender:player-health-regen", "player-health-regen", Double.class);
+        this.createKey("griefdefender:player-keep-inventory", "player-keep-inventory", Tristate.class);
+        this.createKey("griefdefender:player-keep-level", "player-keep-level", Tristate.class);
+        this.createKey("griefdefender:player-teleport-delay", "player-teleport-delay", Integer.class);
+        this.createKey("griefdefender:player-walk-speed", "player-walk-speed", Integer.class);
+        this.createKey("griefdefender:player-weather", "player-weather", WeatherType.class);
+        this.createKey("griefdefender:pvp", "pvp", Tristate.class);
+
+        RegistryHelper.mapFields(Options.class, input -> {
+            final String name = input.replace("_", "-");
+            return this.registryMap.get("griefdefender:" + name.toLowerCase(Locale.ENGLISH));
+        });
+    }
+
+    private void createKey(String id, String name, Class<?> clazz) {
+        this.registryMap.put(id, new GDOption<>(id, name, clazz));
+    }
+
+    @Override
+    public void registerCustomType(Option type) {
+        this.customMap.put(type.getId(), type);
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/registry/ResultTypeRegistryModule.java b/sponge/src/main/java/com/griefdefender/registry/ResultTypeRegistryModule.java
new file mode 100644
index 0000000..421daf6
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/registry/ResultTypeRegistryModule.java
@@ -0,0 +1,91 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.registry;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.griefdefender.api.permission.ResultType;
+import com.griefdefender.api.permission.ResultTypes;
+import com.griefdefender.api.registry.CatalogRegistryModule;
+import com.griefdefender.permission.GDResultType;
+import com.griefdefender.util.RegistryHelper;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Optional;
+
+public class ResultTypeRegistryModule implements CatalogRegistryModule<ResultType> {
+
+    private static ResultTypeRegistryModule instance;
+
+    public static ResultTypeRegistryModule getInstance() {
+        return instance;
+    }
+
+    private final Map<String, ResultType> registryMap = new HashMap<>();
+
+    @Override
+    public Optional<ResultType> getById(String id) {
+        if (id == null) {
+            return Optional.empty();
+        }
+
+        if (id.contains("griefdefender.")) {
+            id = id.replace("griefdefender.", "griefdefender:");
+        }
+        if (!id.contains(":")) {
+            id = "griefdefender:" + id;
+        }
+
+        return Optional.ofNullable(this.registryMap.get(checkNotNull(id)));
+    }
+
+    @Override
+    public Collection<ResultType> getAll() {
+        return this.registryMap.values();
+    }
+
+    @Override
+    public void registerDefaults() {
+        RegistryHelper.mapFields(ResultTypes.class, input -> {
+            final String name = input.toLowerCase().replace("_", "-");
+            final String id = "griefdefender:" + name;
+            final ResultType type = new GDResultType(id, name);
+            this.registryMap.put(id, type);
+            return type;
+        });
+    }
+
+    @Override
+    public void registerCustomType(ResultType type) {
+        this.registryMap.put(type.getId().toLowerCase(Locale.ENGLISH), type);
+    }
+
+    static {
+        instance = new ResultTypeRegistryModule();
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/registry/ShovelTypeRegistryModule.java b/sponge/src/main/java/com/griefdefender/registry/ShovelTypeRegistryModule.java
new file mode 100644
index 0000000..5c35b06
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/registry/ShovelTypeRegistryModule.java
@@ -0,0 +1,91 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.registry;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.griefdefender.api.claim.ShovelType;
+import com.griefdefender.api.claim.ShovelTypes;
+import com.griefdefender.api.registry.CatalogRegistryModule;
+import com.griefdefender.claim.GDShovelType;
+import com.griefdefender.util.RegistryHelper;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+public class ShovelTypeRegistryModule implements CatalogRegistryModule<ShovelType> {
+
+    private static ShovelTypeRegistryModule instance;
+
+    public static ShovelTypeRegistryModule getInstance() {
+        return instance;
+    }
+
+    private final Map<String, ShovelType> registryMap = new HashMap<>();
+
+    @Override
+    public Optional<ShovelType> getById(String id) {
+        if (id == null) {
+            return Optional.empty();
+        }
+
+        if (id.contains("griefdefender.")) {
+            id = id.replace("griefdefender.", "griefdefender:");
+        }
+        if (!id.contains(":")) {
+            id = "griefdefender:" + id;
+        }
+
+        return Optional.ofNullable(this.registryMap.get(checkNotNull(id)));
+    }
+
+    @Override
+    public Collection<ShovelType> getAll() {
+        return this.registryMap.values();
+    }
+
+    @Override
+    public void registerDefaults() {
+        RegistryHelper.mapFields(ShovelTypes.class, input -> {
+            final String name = input.toLowerCase().replace("_", "-");
+            final String id = "griefdefender:" + name;
+            final ShovelType type = new GDShovelType(id, name);
+            this.registryMap.put(id, type);
+            return type;
+        });
+    }
+
+    @Override
+    public void registerCustomType(ShovelType type) {
+        // TODO Auto-generated method stub
+        
+    }
+
+    static {
+        instance = new ShovelTypeRegistryModule();
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/registry/TrustTypeRegistryModule.java b/sponge/src/main/java/com/griefdefender/registry/TrustTypeRegistryModule.java
new file mode 100644
index 0000000..d7a99de
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/registry/TrustTypeRegistryModule.java
@@ -0,0 +1,91 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.registry;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.griefdefender.api.claim.TrustType;
+import com.griefdefender.api.claim.TrustTypes;
+import com.griefdefender.api.registry.CatalogRegistryModule;
+import com.griefdefender.claim.GDTrustType;
+import com.griefdefender.util.RegistryHelper;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+public class TrustTypeRegistryModule implements CatalogRegistryModule<TrustType> {
+
+    private static TrustTypeRegistryModule instance;
+
+    public static TrustTypeRegistryModule getInstance() {
+        return instance;
+    }
+
+    private final Map<String, TrustType> registryMap = new HashMap<>();
+
+    @Override
+    public Optional<TrustType> getById(String id) {
+        if (id == null) {
+            return Optional.empty();
+        }
+
+        if (id.contains("griefdefender.")) {
+            id = id.replace("griefdefender.", "griefdefender:");
+        }
+        if (!id.contains(":")) {
+            id = "griefdefender:" + id;
+        }
+
+        return Optional.ofNullable(this.registryMap.get(checkNotNull(id)));
+    }
+
+    @Override
+    public Collection<TrustType> getAll() {
+        return this.registryMap.values();
+    }
+
+    @Override
+    public void registerDefaults() {
+        RegistryHelper.mapFields(TrustTypes.class, input -> {
+            final String name = input.toLowerCase().replace("_", "-");
+            final String id = "griefdefender:" + name;
+            final TrustType type = new GDTrustType(id, name);
+            this.registryMap.put(id, type);
+            return type;
+        });
+    }
+
+    @Override
+    public void registerCustomType(TrustType type) {
+        // TODO Auto-generated method stub
+        
+    }
+
+    static {
+        instance = new TrustTypeRegistryModule();
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/registry/WeatherTypeRegistryModule.java b/sponge/src/main/java/com/griefdefender/registry/WeatherTypeRegistryModule.java
new file mode 100644
index 0000000..2f742c1
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/registry/WeatherTypeRegistryModule.java
@@ -0,0 +1,91 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.registry;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.griefdefender.api.permission.option.type.WeatherType;
+import com.griefdefender.api.permission.option.type.WeatherTypes;
+import com.griefdefender.api.registry.CatalogRegistryModule;
+import com.griefdefender.permission.option.type.GDWeatherType;
+import com.griefdefender.util.RegistryHelper;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+public class WeatherTypeRegistryModule implements CatalogRegistryModule<WeatherType> {
+
+    private static WeatherTypeRegistryModule instance;
+
+    public static WeatherTypeRegistryModule getInstance() {
+        return instance;
+    }
+
+    private final Map<String, WeatherType> registryMap = new HashMap<>();
+
+    @Override
+    public Optional<WeatherType> getById(String id) {
+        if (id == null) {
+            return Optional.empty();
+        }
+
+        if (id.contains("griefdefender.")) {
+            id = id.replace("griefdefender.", "griefdefender:");
+        }
+        if (!id.contains(":")) {
+            id = "griefdefender:" + id;
+        }
+
+        return Optional.ofNullable(this.registryMap.get(checkNotNull(id)));
+    }
+
+    @Override
+    public Collection<WeatherType> getAll() {
+        return this.registryMap.values();
+    }
+
+    @Override
+    public void registerDefaults() {
+        RegistryHelper.mapFields(WeatherTypes.class, input -> {
+            final String name = input.toLowerCase().replace("_", "-");
+            final String id = "griefdefender:" + name;
+            final WeatherType type = new GDWeatherType(id, name);
+            this.registryMap.put(id, type);
+            return type;
+        });
+    }
+
+    @Override
+    public void registerCustomType(WeatherType type) {
+        // TODO Auto-generated method stub
+        
+    }
+
+    static {
+        instance = new WeatherTypeRegistryModule();
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/storage/BaseStorage.java b/sponge/src/main/java/com/griefdefender/storage/BaseStorage.java
new file mode 100644
index 0000000..0e37af5
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/storage/BaseStorage.java
@@ -0,0 +1,443 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.storage;
+
+import com.flowpowered.math.vector.Vector3i;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.GriefDefender;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.ClaimContexts;
+import com.griefdefender.api.claim.ClaimResult;
+import com.griefdefender.api.claim.ClaimResultType;
+import com.griefdefender.api.claim.ClaimType;
+import com.griefdefender.api.claim.ClaimTypes;
+import com.griefdefender.api.permission.Context;
+import com.griefdefender.api.permission.flag.Flag;
+import com.griefdefender.api.permission.flag.Flags;
+import com.griefdefender.api.permission.option.Option;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.claim.GDClaimManager;
+import com.griefdefender.claim.GDClaimResult;
+import com.griefdefender.claim.GDClaimType;
+import com.griefdefender.configuration.ClaimTemplateStorage;
+import com.griefdefender.configuration.GriefDefenderConfig;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.configuration.type.ConfigBase;
+import com.griefdefender.configuration.type.GlobalConfig;
+import com.griefdefender.event.GDCauseStackManager;
+import com.griefdefender.event.GDRemoveClaimEvent;
+import com.griefdefender.permission.GDPermissionUser;
+import com.griefdefender.permission.GDPermissions;
+import com.griefdefender.permission.flag.FlagContexts;
+import com.griefdefender.permission.option.GDOption;
+import com.griefdefender.registry.FlagRegistryModule;
+import com.griefdefender.registry.OptionRegistryModule;
+import com.griefdefender.util.PermissionUtil;
+import com.griefdefender.util.SpongeContexts;
+import net.kyori.text.TextComponent;
+import net.kyori.text.format.TextColor;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.command.CommandSource;
+import org.spongepowered.api.service.permission.SubjectData;
+import org.spongepowered.api.world.Location;
+import org.spongepowered.api.world.World;
+import org.spongepowered.api.world.storage.WorldProperties;
+
+import java.io.File;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+
+public abstract class BaseStorage {
+
+    protected final Map<UUID, GDClaimManager> claimWorldManagers = new ConcurrentHashMap<>();
+
+    public static Map<UUID, GriefDefenderConfig<ConfigBase>> dimensionConfigMap = new HashMap<>();
+    public static Map<UUID, GriefDefenderConfig<ConfigBase>> worldConfigMap = new HashMap<>();
+    public static Map<String, ClaimTemplateStorage> globalTemplates = new HashMap<>();
+    public static GriefDefenderConfig<GlobalConfig> globalConfig;
+    public static Map<UUID, GDPlayerData> GLOBAL_PLAYER_DATA = new ConcurrentHashMap<>();
+    public static boolean USE_GLOBAL_PLAYER_STORAGE = true;
+    public static Map<String, Double> GLOBAL_OPTION_DEFAULTS = new HashMap<>();
+
+    public final static Path dataLayerFolderPath = GriefDefenderPlugin.getInstance().getConfigPath();
+    public final static Path globalPlayerDataPath = dataLayerFolderPath.resolve("GlobalPlayerData");
+
+    public void initialize() throws Exception {
+        USE_GLOBAL_PLAYER_STORAGE = GriefDefenderPlugin.getGlobalConfig().getConfig().playerdata.useGlobalPlayerDataStorage;
+        if (USE_GLOBAL_PLAYER_STORAGE) {
+            File globalPlayerDataFolder = globalPlayerDataPath.toFile();
+            if (!globalPlayerDataFolder.exists()) {
+                globalPlayerDataFolder.mkdirs();
+            }
+        }
+
+        // handle default flag/option permissions
+        this.setDefaultGlobalPermissions();
+    }
+
+    public void clearCachedPlayerData(UUID worldUniqueId, UUID playerUniqueId) {
+        this.getClaimWorldManager(worldUniqueId).removePlayer(playerUniqueId);
+    }
+
+    public abstract ClaimResult writeClaimToStorage(GDClaim claim);
+
+    public abstract ClaimResult deleteClaimFromStorage(GDClaim claim);
+
+    public Claim getClaim(UUID worldUniqueId, UUID id) {
+        return this.getClaimWorldManager(worldUniqueId).getClaimByUUID(id).orElse(null);
+    }
+
+    public void asyncSaveGlobalPlayerData(UUID playerID, GDPlayerData playerData) {
+        // save everything except the ignore list
+        this.overrideSavePlayerData(playerID, playerData);
+    }
+
+    abstract void overrideSavePlayerData(UUID playerID, GDPlayerData playerData);
+
+    public ClaimResult createClaim(World world, Vector3i point1, Vector3i point2, ClaimType claimType, UUID ownerUniqueId, boolean cuboid) {
+        return createClaim(world, point1, point2, claimType, ownerUniqueId, cuboid, null);
+    }
+
+    public ClaimResult createClaim(World world, Vector3i point1, Vector3i point2, ClaimType claimType, UUID ownerUniqueId, boolean cuboid, Claim parent) {
+        ClaimResult claimResult = Claim.builder()
+                .bounds(point1, point2)
+                .cuboid(cuboid)
+                .world(world.getUniqueId())
+                .type(claimType)
+                .owner(ownerUniqueId)
+                .parent(parent)
+                .build();
+
+        if (claimResult.successful()) {
+            final Claim claim = claimResult.getClaim().get();
+            final GDClaimManager claimManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(world.getUniqueId());
+            claimManager.addClaim(claim, true);
+    
+            /*if (claimResult.getClaims().size() > 1) {
+                claim.migrateClaims(new ArrayList<>(claimResult.getClaims()));
+            }*/
+        }
+
+        return claimResult;
+    }
+
+    public ClaimResult deleteAllAdminClaims(CommandSource src, World world) {
+        GDClaimManager claimWorldManager = this.claimWorldManagers.get(world.getProperties().getUniqueId());
+        if (claimWorldManager == null) {
+            return new GDClaimResult(ClaimResultType.CLAIMS_DISABLED);
+        }
+
+        List<Claim> claimsToDelete = new ArrayList<Claim>();
+        boolean adminClaimFound = false;
+        for (Claim claim : claimWorldManager.getWorldClaims()) {
+            if (claim.isAdminClaim()) {
+                claimsToDelete.add(claim);
+                adminClaimFound = true;
+            }
+        }
+
+        if (!adminClaimFound) {
+            return new GDClaimResult(ClaimResultType.CLAIM_NOT_FOUND);
+        }
+
+        GDCauseStackManager.getInstance().pushCause(src);
+        GDRemoveClaimEvent event = new GDRemoveClaimEvent(ImmutableList.copyOf(claimsToDelete));
+        GriefDefender.getEventManager().post(event);
+        GDCauseStackManager.getInstance().popCause();
+        if (event.cancelled()) {
+            return new GDClaimResult(ClaimResultType.CLAIM_EVENT_CANCELLED,
+                event.getMessage().orElse(GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.DELETE_ALL_TYPE_DENY,
+                        ImmutableMap.of("type", TextComponent.of("ADMIN").color(TextColor.RED)))));
+        }
+
+        for (Claim claim : claimsToDelete) {
+            PermissionUtil.getInstance().clearPermissions((GDClaim) claim);
+            claimWorldManager.deleteClaimInternal(claim, true);
+        }
+
+        return new GDClaimResult(claimsToDelete, ClaimResultType.SUCCESS);
+    }
+
+    public ClaimResult deleteClaim(Claim claim, boolean deleteChildren) {
+        GDClaimManager claimManager = this.getClaimWorldManager(claim.getWorldUniqueId());
+        return claimManager.deleteClaim(claim, deleteChildren);
+    }
+
+    public void abandonClaimsForPlayer(GDPermissionUser user, Set<Claim> claimsToDelete) {
+        for (Claim claim : claimsToDelete) {
+            PermissionUtil.getInstance().clearPermissions((GDClaim) claim);
+            GDClaimManager claimWorldManager = this.claimWorldManagers.get(claim.getWorldUniqueId());
+            claimWorldManager.deleteClaimInternal(claim, true);
+        }
+
+        return;
+    }
+
+    public void deleteClaimsForPlayer(UUID playerID) {
+        if (BaseStorage.USE_GLOBAL_PLAYER_STORAGE && playerID != null) {
+            final GDPlayerData playerData = BaseStorage.GLOBAL_PLAYER_DATA.get(playerID);
+            List<Claim> claimsToDelete = new ArrayList<>(playerData.getInternalClaims());
+            for (Claim claim : claimsToDelete) {
+                PermissionUtil.getInstance().clearPermissions((GDClaim) claim);
+                GDClaimManager claimWorldManager = this.claimWorldManagers.get(claim.getWorldUniqueId());
+                claimWorldManager.deleteClaimInternal(claim, true);
+            }
+
+            playerData.getInternalClaims().clear();
+            return;
+        }
+
+        for (GDClaimManager claimWorldManager : this.claimWorldManagers.values()) {
+            Set<Claim> claims = claimWorldManager.getInternalPlayerClaims(playerID);
+            if (playerID == null) {
+                claims = claimWorldManager.getWorldClaims();
+            }
+            if (claims == null) {
+                continue;
+            }
+
+            List<Claim> claimsToDelete = new ArrayList<Claim>();
+            for (Claim claim : claims) {
+                if (!claim.isAdminClaim()) {
+                    claimsToDelete.add(claim);
+                }
+            }
+ 
+            for (Claim claim : claimsToDelete) {
+                PermissionUtil.getInstance().clearPermissions(GriefDefenderPlugin.DEFAULT_HOLDER, ImmutableSet.of(claim.getContext()));
+                claimWorldManager.deleteClaimInternal(claim, true);
+                claims.remove(claim);
+            }
+        }
+    }
+
+    public GDClaim getClaimAt(Location<World> location) {
+        GDClaimManager claimManager = this.getClaimWorldManager(location.getExtent().getUniqueId());
+        return (GDClaim) claimManager.getClaimAt(location);
+    }
+
+    public GDClaim getClaimAtPlayer(GDPlayerData playerData, Location<World> location) {
+        GDClaimManager claimManager = this.getClaimWorldManager(location.getExtent().getUniqueId());
+        return (GDClaim) claimManager.getClaimAtPlayer(location, playerData);
+    }
+
+    public GDClaim getClaimAtPlayer(Location<World> location, GDPlayerData playerData, boolean useBorderBlockRadius) {
+        GDClaimManager claimManager = this.getClaimWorldManager(location.getExtent().getUniqueId());
+        return (GDClaim) claimManager.getClaimAt(location, null, playerData, useBorderBlockRadius);
+    }
+
+    public GDClaim getClaimAtPlayer(Location<World> location, GDClaim cachedClaim, GDPlayerData playerData, boolean useBorderBlockRadius) {
+        GDClaimManager claimManager = this.getClaimWorldManager(location.getExtent().getUniqueId());
+        return (GDClaim) claimManager.getClaimAt(location, cachedClaim, playerData, useBorderBlockRadius);
+    }
+
+    public GDClaim getClaimAt(Location<World> location, GDClaim cachedClaim) {
+        GDClaimManager claimManager = this.getClaimWorldManager(location.getExtent().getUniqueId());
+        return (GDClaim) claimManager.getClaimAt(location, cachedClaim, null, false);
+    }
+
+    public GDPlayerData getPlayerData(World world, UUID playerUniqueId) {
+        return this.getPlayerData(world.getUniqueId(), playerUniqueId);
+    }
+
+    public GDPlayerData getPlayerData(UUID worldUniqueId, UUID playerUniqueId) {
+        GDPlayerData playerData = null;
+        GDClaimManager claimWorldManager = this.getClaimWorldManager(worldUniqueId);
+        playerData = claimWorldManager.getPlayerDataMap().get(playerUniqueId);
+        return playerData;
+    }
+
+    public GDPlayerData getOrCreateGlobalPlayerData(UUID playerUniqueId) {
+        GDClaimManager claimWorldManager = this.getClaimWorldManager(null);
+        return claimWorldManager.getOrCreatePlayerData(playerUniqueId);
+    }
+
+    public GDPlayerData getOrCreatePlayerData(World world, UUID playerUniqueId) {
+        return getOrCreatePlayerData(world.getUniqueId(), playerUniqueId);
+    }
+
+    public GDPlayerData getOrCreatePlayerData(UUID worldUniqueId, UUID playerUniqueId) {
+        GDClaimManager claimWorldManager = this.getClaimWorldManager(worldUniqueId);
+        return claimWorldManager.getOrCreatePlayerData(playerUniqueId);
+    }
+
+    public void removePlayerData(UUID worldUniqueId, UUID playerUniqueId) {
+        GDClaimManager claimWorldManager = this.getClaimWorldManager(worldUniqueId);
+        claimWorldManager.removePlayer(playerUniqueId);
+    }
+
+    public GDClaimManager getClaimWorldManager(UUID worldUniqueId) {
+        GDClaimManager claimWorldManager = null;
+        if (worldUniqueId == null) {
+            worldUniqueId = Sponge.getServer().getDefaultWorld().get().getUniqueId();
+        }
+        claimWorldManager = this.claimWorldManagers.get(worldUniqueId);
+
+        if (claimWorldManager == null) {
+            final WorldProperties defaultWorldProperties = Sponge.getServer().getDefaultWorld().get();
+            final World world = Sponge.getServer().getWorld(worldUniqueId).orElse(Sponge.getServer().getWorld(defaultWorldProperties.getUniqueId()).get());
+            registerWorld(world);
+            claimWorldManager = this.claimWorldManagers.get(world.getUniqueId());
+        }
+        return claimWorldManager;
+    }
+
+    public void removeClaimWorldManager(WorldProperties worldProperties) {
+        if (BaseStorage.USE_GLOBAL_PLAYER_STORAGE) {
+            return;
+        }
+        this.claimWorldManagers.remove(worldProperties.getUniqueId());
+    }
+
+    public void setDefaultGlobalPermissions() {
+        // Admin defaults
+        Set<Context> contexts = new HashSet<>();
+        contexts.add(ClaimContexts.ADMIN_DEFAULT_CONTEXT);
+        final GriefDefenderConfig<GlobalConfig> activeConfig = GriefDefenderPlugin.getGlobalConfig();
+        final Map<String, Boolean> adminDefaultFlags = activeConfig.getConfig().permissionCategory.getFlagDefaults(ClaimTypes.ADMIN.getName().toLowerCase());
+        if (adminDefaultFlags != null && !adminDefaultFlags.isEmpty()) {
+            this.setDefaultFlags(contexts, adminDefaultFlags);
+        }
+
+        // Basic defaults
+        contexts = new HashSet<>();
+        contexts.add(ClaimContexts.BASIC_DEFAULT_CONTEXT);
+        final Map<String, Boolean> basicDefaultFlags = activeConfig.getConfig().permissionCategory.getFlagDefaults(ClaimTypes.BASIC.getName().toLowerCase());
+        if (basicDefaultFlags != null && !basicDefaultFlags.isEmpty()) {
+            this.setDefaultFlags(contexts, basicDefaultFlags);
+        }
+        final Map<String, String> basicDefaultOptions = activeConfig.getConfig().permissionCategory.getBasicOptionDefaults();
+        contexts = new HashSet<>();
+        contexts.add(ClaimTypes.BASIC.getDefaultContext());
+        this.setDefaultOptions(ClaimTypes.BASIC.toString(), contexts, new HashMap<>(basicDefaultOptions));
+
+        // Town defaults
+        contexts = new HashSet<>();
+        contexts.add(ClaimContexts.TOWN_DEFAULT_CONTEXT);
+        final Map<String, Boolean> townDefaultFlags = activeConfig.getConfig().permissionCategory.getFlagDefaults(ClaimTypes.TOWN.getName().toLowerCase());
+        final Map<String, String> townDefaultOptions = activeConfig.getConfig().permissionCategory.getTownOptionDefaults();
+        if (townDefaultFlags != null && !townDefaultFlags.isEmpty()) {
+            this.setDefaultFlags(contexts, townDefaultFlags);
+        }
+        contexts = new HashSet<>();
+        contexts.add(ClaimTypes.TOWN.getDefaultContext());
+        this.setDefaultOptions(ClaimTypes.TOWN.toString(), contexts, new HashMap<>(townDefaultOptions));
+
+        // Subdivision defaults
+        contexts = new HashSet<>();
+        contexts.add(ClaimTypes.SUBDIVISION.getDefaultContext());
+        final Map<String, String> subdivisionDefaultOptions = activeConfig.getConfig().permissionCategory.getSubdivisionOptionDefaults();
+        this.setDefaultOptions(ClaimTypes.SUBDIVISION.toString(), contexts, new HashMap<>(subdivisionDefaultOptions));
+
+        // Wilderness defaults
+        contexts = new HashSet<>();
+        contexts.add(ClaimContexts.WILDERNESS_DEFAULT_CONTEXT);
+        final Map<String, Boolean> wildernessDefaultFlags = activeConfig.getConfig().permissionCategory.getFlagDefaults(ClaimTypes.WILDERNESS.getName().toLowerCase());
+        this.setDefaultFlags(contexts, wildernessDefaultFlags);
+
+        // Global default options
+        contexts = new HashSet<>();
+        contexts.add(ClaimContexts.GLOBAL_DEFAULT_CONTEXT);
+        final Map<String, Boolean> globalDefaultFlags = activeConfig.getConfig().permissionCategory.getFlagDefaults("global");
+        this.setDefaultFlags(contexts, globalDefaultFlags);
+        final Map<String, String> globalDefaultOptions = activeConfig.getConfig().permissionCategory.getUserOptionDefaults();
+        this.setDefaultOptions(ClaimContexts.GLOBAL_DEFAULT_CONTEXT.getName(), contexts, new HashMap<>(globalDefaultOptions));
+        GriefDefenderPlugin.getInstance().getPermissionProvider().setTransientPermission(GriefDefenderPlugin.DEFAULT_HOLDER, "griefdefender", false, new HashSet<>());
+        activeConfig.save();
+    }
+
+    private void setDefaultFlags(Set<Context> contexts, Map<String, Boolean> defaultFlags) {
+        GriefDefenderPlugin.getInstance().executor.execute(() -> {
+            for (Map.Entry<String, Boolean> mapEntry : defaultFlags.entrySet()) {
+                final Flag flag = FlagRegistryModule.getInstance().getById(mapEntry.getKey()).orElse(null);
+                if (flag == null) {
+                    continue;
+                }
+                PermissionUtil.getInstance().setTransientPermission(GriefDefenderPlugin.DEFAULT_HOLDER, GDPermissions.FLAG_BASE + "." + mapEntry.getKey(), mapEntry.getValue(), contexts);
+                if (flag == Flags.ENTITY_DAMAGE) {
+                    // allow monsters to be attacked by default
+                    contexts.add(FlagContexts.TARGET_TYPE_MONSTER);
+                    PermissionUtil.getInstance().setTransientPermission(GriefDefenderPlugin.DEFAULT_HOLDER, GDPermissions.FLAG_BASE + "." + mapEntry.getKey(), true, contexts);
+                    contexts.remove(FlagContexts.TARGET_TYPE_MONSTER);
+                }
+            }
+            PermissionUtil.getInstance().refreshCachedData(GriefDefenderPlugin.DEFAULT_HOLDER);
+        });
+    }
+
+    private void setDefaultOptions(String type, Set<Context> contexts, Map<String, String> defaultOptions) {
+        final Map<Set<Context>, Map<String, String>> permanentOptions = PermissionUtil.getInstance().getPermanentOptions(GriefDefenderPlugin.DEFAULT_HOLDER);
+        final Map<String, String> options = permanentOptions.get(contexts);
+        GriefDefenderPlugin.getInstance().executor.execute(() -> {
+            for (Map.Entry<String, String> optionEntry : defaultOptions.entrySet()) {
+                final Option option = OptionRegistryModule.getInstance().getById(optionEntry.getKey()).orElse(null);
+                if (option == null) {
+                    continue;
+                }
+
+                if (!((GDOption) option).validateStringValue(optionEntry.getValue(), true)) {
+                    continue;
+                }
+                // Transient options are checked first so we must ignore setting if a persisted option exists
+                boolean foundPersisted = false;
+                if (options != null) {
+                    for (Entry<String, String> mapEntry : options.entrySet()) {
+                        if (mapEntry.getKey().equalsIgnoreCase(option.getPermission())) {
+                            foundPersisted = true;
+                            break;
+                        }
+                    }
+                    if (foundPersisted) {
+                        continue;
+                    }
+                }
+                PermissionUtil.getInstance().setTransientOption(GriefDefenderPlugin.DEFAULT_HOLDER, option.getPermission(), optionEntry.getValue(), contexts);
+            }
+            PermissionUtil.getInstance().refreshCachedData(GriefDefenderPlugin.DEFAULT_HOLDER);
+        });
+    }
+
+    abstract GDPlayerData getPlayerDataFromStorage(UUID playerID);
+
+    public abstract void registerWorld(World world);
+
+    public abstract void loadWorldData(World world);
+
+    public abstract void unloadWorldData(World world);
+
+    abstract void loadClaimTemplates();
+}
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/storage/FileStorage.java b/sponge/src/main/java/com/griefdefender/storage/FileStorage.java
new file mode 100644
index 0000000..07f0f93
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/storage/FileStorage.java
@@ -0,0 +1,512 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.storage;
+
+import com.flowpowered.math.vector.Vector3i;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.GriefDefender;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.ClaimBlockSystem;
+import com.griefdefender.api.claim.ClaimResult;
+import com.griefdefender.api.claim.ClaimResultType;
+import com.griefdefender.api.claim.ClaimSchematic;
+import com.griefdefender.api.claim.ClaimType;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.claim.GDClaimManager;
+import com.griefdefender.claim.GDClaimResult;
+import com.griefdefender.claim.GDClaimSchematic;
+import com.griefdefender.configuration.ClaimStorageData;
+import com.griefdefender.configuration.ClaimTemplateStorage;
+import com.griefdefender.configuration.GriefDefenderConfig;
+import com.griefdefender.configuration.TownStorageData;
+import com.griefdefender.configuration.type.ConfigBase;
+import com.griefdefender.event.GDLoadClaimEvent;
+import com.griefdefender.migrator.GPBukkitMigrator;
+import com.griefdefender.migrator.WorldGuardMigrator;
+import org.apache.commons.io.FileUtils;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.data.DataContainer;
+import org.spongepowered.api.data.persistence.DataFormats;
+import org.spongepowered.api.data.persistence.DataTranslators;
+import org.spongepowered.api.scheduler.Task;
+import org.spongepowered.api.world.DimensionType;
+import org.spongepowered.api.world.World;
+import org.spongepowered.api.world.schematic.Schematic;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import java.util.zip.GZIPInputStream;
+
+public class FileStorage extends BaseStorage {
+
+    private final static Path migrationVersionFilePath = dataLayerFolderPath.resolve("_migrationVersion");
+    private final static Path worldsConfigFolderPath = dataLayerFolderPath.resolve("worlds");
+    public final static Path claimDataPath = Paths.get("GriefDefenderData", "ClaimData");
+    public final static Path claimTemplatePath = claimDataPath.resolve("Templates");
+    private final static Map<UUID, Task> cleanupClaimTasks = new HashMap<>();
+    private final Path rootConfigPath = GriefDefenderPlugin.getInstance().getConfigPath().resolve("worlds");
+    public static Path rootWorldSavePath;
+    private int claimLoadCount = 0;
+
+    @Override
+    public void initialize() throws Exception {
+        // ensure data folders exist
+        File worldsDataFolder = worldsConfigFolderPath.toFile();
+
+        if (!worldsDataFolder.exists()) {
+            worldsDataFolder.mkdirs();
+        }
+
+        rootWorldSavePath = Sponge.getGame().getSavesDirectory().resolve(Sponge.getServer().getDefaultWorldName());
+
+        super.initialize();
+    }
+
+    @Override
+    public void loadClaimTemplates() {
+        try {
+            if (Files.exists(rootWorldSavePath.resolve(claimTemplatePath))) {
+                File[] files = rootWorldSavePath.resolve(claimTemplatePath).toFile().listFiles();
+                int count = 0;
+                for (File file : files) {
+                    ClaimTemplateStorage templateStorage = new ClaimTemplateStorage(file.toPath());
+                    String templateName = templateStorage.getConfig().getTemplateName();
+                    if (!templateName.isEmpty()) {
+                        globalTemplates.put(templateName, templateStorage);
+                        count++;
+                    }
+                }
+                GriefDefenderPlugin.getInstance().getLogger().info(count + " total claim templates loaded.");
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    public void registerWorld(World world) {
+        final UUID worldUniqueId = world.getUniqueId();
+        DimensionType dimType = world.getProperties().getDimensionType();
+        String[] parts = dimType.getId().split(":");
+        Path dimPath = rootConfigPath.resolve(parts[0]).resolve(dimType.getName());
+        if (!Files.exists(dimPath.resolve(world.getName()))) {
+            try {
+                Files.createDirectories(dimPath.resolve(world.getName()));
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+
+        GDClaimManager claimWorldManager = new GDClaimManager(world);
+        this.claimWorldManagers.put(world.getUniqueId(), claimWorldManager);
+        // create/load configs
+        GriefDefenderConfig<ConfigBase> dimConfig = new GriefDefenderConfig<>(ConfigBase.class, dimPath.resolve("dimension.conf"), BaseStorage.globalConfig);
+        GriefDefenderConfig<ConfigBase> worldConfig = new GriefDefenderConfig<>(ConfigBase.class, dimPath.resolve(world.getName()).resolve("world.conf"), dimConfig);
+        BaseStorage.dimensionConfigMap.put(worldUniqueId, dimConfig);
+        BaseStorage.worldConfigMap.put(worldUniqueId, worldConfig);
+
+        if (GriefDefenderPlugin.getGlobalConfig().getConfig().migrator.gpBukkitMigrator) {
+            final File migrateFile = dimPath.resolve(world.getName()).resolve("_bukkitMigrated").toFile();
+            if (!migrateFile.exists()) {
+                try {
+                    final Path path = Paths.get("plugins", "GriefPreventionData", "ClaimData");
+                    if (path.toFile().exists()) {
+                        GPBukkitMigrator.migrate(world, path);
+                        Files.createFile(dimPath.resolve(world.getName()).resolve("_bukkitMigrated"));
+                    }
+                } catch (FileNotFoundException e) {
+                    e.printStackTrace();
+                } catch (ClassNotFoundException e) {
+                    e.printStackTrace();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        if (GriefDefenderPlugin.getGlobalConfig().getConfig().migrator.worldGuardMigrator) {
+            final File migrateFile = dimPath.resolve(world.getName()).resolve("_wgMigrated").toFile();
+            if (!migrateFile.exists()) {
+                try {
+                    final Path path = Paths.get("plugins", "WorldGuard", "worlds", world.getName());
+                    if (path.toFile().exists()) {
+                        WorldGuardMigrator.migrate(world);
+                        Files.createFile(dimPath.resolve(world.getName()).resolve("_wgMigrated"));
+                    }
+                } catch (FileNotFoundException e) {
+                    e.printStackTrace();
+                } catch (ClassNotFoundException e) {
+                    e.printStackTrace();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+
+        Path newWorldDataPath = dimPath.resolve(world.getName());
+
+        try {
+            // Create data folders if they do not exist
+            if (Files.notExists(newWorldDataPath.resolve("ClaimData"))) {
+                Files.createDirectories(newWorldDataPath.resolve("ClaimData"));
+            }
+            if (Files.notExists(newWorldDataPath.resolve("ClaimData").resolve("wilderness"))) {
+                Files.createDirectories(newWorldDataPath.resolve("ClaimData").resolve("wilderness"));
+            }
+            if (Files.notExists(newWorldDataPath.resolve("SchematicData"))) {
+                Files.createDirectories(newWorldDataPath.resolve("SchematicData"));
+            }
+
+            if (GriefDefenderPlugin.getInstance().getWorldEditProvider() != null) {
+                GriefDefenderPlugin.getInstance().getWorldEditProvider().getSchematicWorldMap().put(world.getUniqueId(), newWorldDataPath.resolve("SchematicData"));
+            }
+
+            if (BaseStorage.USE_GLOBAL_PLAYER_STORAGE) {
+                if (Files.notExists(globalPlayerDataPath)) {
+                    Files.createDirectories(globalPlayerDataPath);
+                }
+            } else if (Files.notExists(newWorldDataPath.resolve("PlayerData"))) {
+                Files.createDirectories(newWorldDataPath.resolve("PlayerData"));
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public void loadWorldData(World world) {
+        final UUID worldUniqueId = world.getUniqueId();
+        final DimensionType dimType = world.getProperties().getDimensionType();
+        final String[] parts = dimType.getId().split(":");
+        final Path dimPath = rootConfigPath.resolve(parts[0]).resolve(dimType.getName());
+        final Path newWorldDataPath = dimPath.resolve(world.getName());
+        GDClaimManager claimWorldManager = this.claimWorldManagers.get(worldUniqueId);
+        if (claimWorldManager == null) {
+            this.registerWorld(world);
+            claimWorldManager = this.claimWorldManagers.get(worldUniqueId);
+        }
+
+        // Load wilderness claim first
+        final Path wildernessFilePath = newWorldDataPath.resolve("ClaimData").resolve("wilderness").resolve(worldUniqueId.toString());
+        if (Files.exists(wildernessFilePath)) {
+            try {
+                this.loadClaim(wildernessFilePath.toFile(), world, world.getUniqueId());
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        } else {
+            claimWorldManager.createWildernessClaim(world);
+        }
+
+        // Load Claim Data
+        try {
+            File[] files = newWorldDataPath.resolve("ClaimData").toFile().listFiles();
+            if (files != null && files.length > 0) {
+                this.loadClaimData(files, world);
+                GriefDefenderPlugin.getInstance().getLogger().info("[" + world.getName() + "] " + this.claimLoadCount + " total claims loaded.");
+            }
+
+            if (GriefDefenderPlugin.getGlobalConfig().getConfig().playerdata.useGlobalPlayerDataStorage) {
+                files = globalPlayerDataPath.toFile().listFiles();
+            } else {
+                files = newWorldDataPath.resolve("PlayerData").toFile().listFiles();
+            }
+            if (files != null && files.length > 0) {
+                this.loadPlayerData(world, files);
+            }
+
+            // If a wilderness claim was not loaded, create a new one
+            if (claimWorldManager.getWildernessClaim() == null) {
+                claimWorldManager.createWildernessClaim(world);
+            }
+
+            // Load schematics
+            if (GriefDefenderPlugin.getInstance().getWorldEditProvider() != null) {
+                GriefDefenderPlugin.getInstance().getLogger().info("Loading schematics for world " + world.getName() + "...");
+                GriefDefenderPlugin.getInstance().getWorldEditProvider().loadSchematics(world);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        this.claimLoadCount = 0;
+    }
+
+    public void unloadWorldData(World world) {
+        final UUID worldUniqueId = world.getUniqueId();
+        GDClaimManager claimWorldManager = this.getClaimWorldManager(worldUniqueId);
+        for (Claim claim : claimWorldManager.getWorldClaims()) {
+            ((GDClaim) claim).unload();
+        }
+        // Task must be cancelled before removing the claimWorldManager reference to avoid a memory leak
+        Task cleanupTask = cleanupClaimTasks.get(worldUniqueId);
+        if (cleanupTask != null) {
+           cleanupTask.cancel();
+           cleanupClaimTasks.remove(worldUniqueId);
+        }
+
+        claimWorldManager.unload();
+        this.claimWorldManagers.remove(worldUniqueId);
+        BaseStorage.dimensionConfigMap.remove(worldUniqueId);
+        BaseStorage.worldConfigMap.remove(worldUniqueId);
+    }
+
+    void loadClaimData(File[] files, World world) throws Exception {
+        for (int i = 0; i < files.length; i++) {
+            File file = files[i];
+            if (file.isFile()) {
+                this.loadClaimFile(file, world);
+            }
+        }
+        for (int i = 0; i < files.length; i++) {
+            File file = files[i];
+            if (file.isDirectory()) {
+                this.loadClaimData(file.listFiles(), world);
+            }
+        }
+    }
+
+    void loadClaimFile(File file, World world) {
+        if (file.isFile()) // avoids folders
+        {
+            // the filename is the claim ID. try to parse it
+            UUID claimId;
+
+            try {
+                final String fileName = file.getName();
+                // UUID's should always be 36 in length
+                if (fileName.length() != 36) {
+                    return;
+                }
+
+                claimId = UUID.fromString(fileName);
+            } catch (Exception e) {
+                GriefDefenderPlugin.getInstance().getLogger().error("Could not read claim file " + file.getAbsolutePath());
+                return;
+            }
+
+            try {
+               this.loadClaim(file, world, claimId);
+            }
+
+            catch (Exception e) {
+                if (e.getMessage() != null && e.getMessage().contains("World not found")) {
+                    file.delete();
+                } else {
+                    GriefDefenderPlugin.getInstance().getLogger().error(file.getName() + " is corrupted.");
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+    void loadPlayerData(World world, File[] files) throws Exception {
+        final boolean resetMigration = GriefDefenderPlugin.getGlobalConfig().getConfig().playerdata.resetMigrations;
+        final boolean resetClaimData = GriefDefenderPlugin.getGlobalConfig().getConfig().playerdata.resetAccruedClaimBlocks;
+        final int migration2dRate = GriefDefenderPlugin.getGlobalConfig().getConfig().playerdata.migrateAreaRate;
+        final int migration3dRate = GriefDefenderPlugin.getGlobalConfig().getConfig().playerdata.migrateVolumeRate;
+        boolean migrate = false;
+        if (resetMigration || resetClaimData || (migration2dRate > -1 && GriefDefenderPlugin.CLAIM_BLOCK_SYSTEM == ClaimBlockSystem.AREA) 
+                || (migration3dRate > -1 && GriefDefenderPlugin.CLAIM_BLOCK_SYSTEM == ClaimBlockSystem.VOLUME)) {
+            // load all player data if migrating
+            migrate = true;
+        }
+        for (int i = 0; i < files.length; i++) {
+            if (files[i].isFile())
+            {
+                UUID playerUUID;
+
+                try {
+                    final String fileName = files[i].getName();
+                    // UUID's should always be 36 in length
+                    if (fileName.length() != 36) {
+                        return;
+                    }
+
+                    playerUUID = UUID.fromString(fileName);
+                } catch (Exception e) {
+                    GriefDefenderPlugin.getInstance().getLogger().error("Could not read player file " + files[i].getAbsolutePath());
+                    continue;
+                }
+
+                if (!migrate && !Sponge.getServer().getPlayer(playerUUID).isPresent()) {
+                    continue;
+                }
+
+                try {
+                    this.getOrCreatePlayerData(world, playerUUID);
+                } catch (Exception e) {
+                    if (e.getMessage() != null && e.getMessage().contains("World not found")) {
+                        files[i].delete();
+                    } else {
+                        GriefDefenderPlugin.getInstance().getLogger().error(files[i].getName() + " is corrupted.");
+                        e.printStackTrace();
+                    }
+                }
+            }
+        }
+    }
+
+    public GDClaim loadClaim(File claimFile, World world, UUID claimId)
+            throws Exception {
+        GDClaim claim;
+
+        final GDClaimManager claimManager = this.getClaimWorldManager(world.getUniqueId());
+        if (claimManager.getWildernessClaim() != null && claimManager.getWildernessClaim().getUniqueId().equals(claimId)) {
+            return null;
+        }
+        boolean isTown = claimFile.toPath().getParent().endsWith("town");
+        boolean writeToStorage = false;
+        ClaimStorageData claimStorage = null;
+        if (isTown) {
+            claimStorage = new TownStorageData(claimFile.toPath(), world.getUniqueId());
+        } else {
+            claimStorage = new ClaimStorageData(claimFile.toPath(), world.getUniqueId());
+        }
+
+        final ClaimType type = claimStorage.getConfig().getType();
+        final UUID parent = claimStorage.getConfig().getParent().orElse(null);
+        final String fileName = claimFile.getName();
+        //final World world = Sponge.getServer().loadWorld(worldProperties).orElse(null);
+        //if (world == null) {
+        //    throw new Exception("World [Name: " + worldProperties.getWorldName() + "][UUID: " + worldUniqueId.toString() + "] is not loaded.");
+        //}
+
+        if (claimFile.getParentFile().getName().equalsIgnoreCase("claimdata")) {
+            final Path newPath = claimStorage.filePath.getParent().resolve(type.getName().toLowerCase());
+            if (Files.notExists(newPath)) {
+                Files.createDirectories(newPath);
+            }
+            Files.move(claimStorage.filePath, newPath.resolve(fileName));
+            claimStorage.filePath = newPath.resolve(fileName);
+            claimStorage = new ClaimStorageData(claimStorage.filePath, world.getUniqueId());
+        }
+
+        // identify world the claim is in
+        UUID claimWorldUniqueId = claimStorage.getConfig().getWorldUniqueId();
+        if (!claimWorldUniqueId.equals(world.getUniqueId())) {
+            GriefDefenderPlugin.getInstance().getLogger().info("Found mismatch world UUID in " + type.getName().toLowerCase() + " claim file " + claimFile + ". Expected " + world.getUniqueId() + ", found " + claimWorldUniqueId + ". Updating file with correct UUID...");
+            claimStorage.getConfig().setWorldUniqueId(world.getUniqueId());
+            writeToStorage = true;
+        }
+
+        // boundaries
+        final boolean cuboid = claimStorage.getConfig().isCuboid();
+        Vector3i lesserCorner = claimStorage.getConfig().getLesserBoundaryCornerPos();
+        Vector3i greaterCorner = claimStorage.getConfig().getGreaterBoundaryCornerPos();
+        if (lesserCorner == null || greaterCorner == null) {
+            throw new Exception("Claim file '" + claimFile.getName() + "' has corrupted data and cannot be loaded. Skipping...");
+        }
+
+        UUID ownerID = claimStorage.getConfig().getOwnerUniqueId();
+        claim = new GDClaim(world, lesserCorner, greaterCorner, claimId, claimStorage.getConfig().getType(), ownerID, cuboid);
+        claim.setClaimStorage(claimStorage);
+        claim.setClaimData(claimStorage.getConfig());
+        GDLoadClaimEvent.Pre preEvent = new GDLoadClaimEvent.Pre(claim);
+        GriefDefender.getEventManager().post(preEvent);
+
+        // add parent claim first
+        if (parent != null) {
+            GDClaim parentClaim = null;
+            try {
+                parentClaim = (GDClaim) claimManager.getClaimByUUID(parent).orElse(null);
+            } catch (Throwable t) {
+                t.printStackTrace();
+            }
+            if (parentClaim == null) {
+                throw new Exception("Required parent claim '" + parent + " no longer exists. Skipping...");
+            }
+            claim.parent = parentClaim;
+        }
+
+        claimManager.addClaim(claim, writeToStorage);
+        this.claimLoadCount++;
+        GDLoadClaimEvent.Post postEvent = new GDLoadClaimEvent.Post(claim);
+        GriefDefender.getEventManager().post(postEvent);
+        return claim;
+    }
+
+    @Override
+    public ClaimResult writeClaimToStorage(GDClaim claim) {
+        try {
+            ClaimStorageData claimStorage = claim.getClaimStorage();
+            claim.updateClaimStorageData();
+            claimStorage.save();
+            return new GDClaimResult(claim, ClaimResultType.SUCCESS);
+        } catch (Exception e) {
+            GriefDefenderPlugin.getInstance().getLogger().error(claim.getUniqueId() + " could not save properly.");
+            e.printStackTrace();
+        }
+
+        return new GDClaimResult(claim, ClaimResultType.FAILURE);
+    }
+
+    @Override
+    public ClaimResult deleteClaimFromStorage(GDClaim claim) {
+        final GDPlayerData ownerData = claim.getOwnerPlayerData();
+        try {
+            Files.delete(claim.getClaimStorage().filePath);
+            if (GriefDefenderPlugin.getInstance().getWorldEditProvider() != null) {
+                final Path schematicPath = GriefDefenderPlugin.getInstance().getWorldEditProvider().getSchematicWorldMap().get(claim.getWorldUniqueId());
+                if (schematicPath != null && Files.exists(schematicPath.resolve(claim.getUniqueId().toString()))) {
+                    if (ownerData != null && ownerData.useRestoreSchematic) {
+                        final ConfigBase activeConfig = GriefDefenderPlugin.getActiveConfig(claim.getWorldUniqueId()).getConfig();
+                        if (GriefDefenderPlugin.getInstance().getWorldEditProvider() != null && activeConfig.claim.claimAutoSchematicRestore) {
+                            final ClaimSchematic schematic = claim.getSchematics().get("__restore__");
+                            if (schematic != null) {
+                                schematic.apply();
+                            }
+                        }
+                    }
+                    FileUtils.deleteDirectory(schematicPath.resolve(claim.getUniqueId().toString()).toFile());
+                }
+            }
+
+            return new GDClaimResult(claim, ClaimResultType.SUCCESS);
+        } catch (IOException e) {
+            e.printStackTrace();
+            GriefDefenderPlugin.getInstance().getLogger().error("Error: Unable to delete claim file \"" + claim.getClaimStorage().filePath + "\".");
+        }
+
+        return new GDClaimResult(claim, ClaimResultType.FAILURE);
+    }
+
+    @Override
+    GDPlayerData getPlayerDataFromStorage(UUID playerID) {
+        return null;
+    }
+
+    @Override
+    void overrideSavePlayerData(UUID playerID, GDPlayerData playerData) {
+    }
+
+}
diff --git a/sponge/src/main/java/com/griefdefender/task/ClaimBlockTask.java b/sponge/src/main/java/com/griefdefender/task/ClaimBlockTask.java
new file mode 100644
index 0000000..50fc4bc
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/task/ClaimBlockTask.java
@@ -0,0 +1,122 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.task;
+
+import com.google.common.reflect.TypeToken;
+import com.griefdefender.GDBootstrap;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.claim.ClaimResultType;
+import com.griefdefender.api.permission.option.Options;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.claim.GDClaimResult;
+import com.griefdefender.configuration.PlayerStorageData;
+import com.griefdefender.permission.GDPermissionManager;
+import com.griefdefender.storage.BaseStorage;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.data.manipulator.mutable.entity.VehicleData;
+import org.spongepowered.api.data.property.block.MatterProperty;
+import org.spongepowered.api.entity.Entity;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.service.economy.Currency;
+import org.spongepowered.api.service.economy.account.Account;
+import org.spongepowered.api.world.Location;
+import org.spongepowered.api.world.World;
+
+import java.math.BigDecimal;
+import java.util.Optional;
+
+public class ClaimBlockTask implements Runnable {
+
+    private Player player;
+
+    public ClaimBlockTask() {
+    }
+
+    public ClaimBlockTask(Player player) {
+        this.player = player;
+    }
+
+    @Override
+    public void run() {
+        if (this.player == null) {
+            for (World world : Sponge.getServer().getWorlds()) {
+                int i = 0;
+                for (Entity entity : world.getEntities()) {
+                    if (!(entity instanceof Player)) {
+                        continue;
+                    }
+
+                    final Player player = (Player) entity;
+                    final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+                    final GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(playerData, player.getLocation());
+                    final int accrualPerHour = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), player, Options.BLOCKS_ACCRUED_PER_HOUR, claim);
+                    if (accrualPerHour > 0) {
+                        ClaimBlockTask newTask = new ClaimBlockTask(player);
+                        Sponge.getGame().getScheduler().createTaskBuilder().delayTicks(i++).execute(newTask)
+                                .submit(GDBootstrap.getInstance());
+                    }
+                }
+            }
+            return;
+        }
+
+        final BaseStorage dataStore = GriefDefenderPlugin.getInstance().dataStore;
+        final GDPlayerData playerData = dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+        final Location<World> lastLocation = playerData.lastAfkCheckLocation;
+        final Optional<MatterProperty> matterProperty = player.getLocation().getBlock().getProperty(MatterProperty.class);
+        if (!player.get(VehicleData.class).isPresent() &&
+                (lastLocation == null || lastLocation.getPosition().distanceSquared(player.getLocation().getPosition()) >= 0) &&
+                matterProperty.isPresent() && matterProperty.get().getValue() != MatterProperty.Matter.LIQUID) {
+            int accruedBlocks = playerData.getBlocksAccruedPerHour() / 12;
+            if (accruedBlocks < 0) {
+                accruedBlocks = 1;
+            }
+
+            if (GriefDefenderPlugin.getInstance().isEconomyModeEnabled()) {
+                final Account playerAccount = GriefDefenderPlugin.getInstance().economyService.get().getOrCreateAccount(player.getUniqueId()).orElse(null);
+                if (playerAccount == null) {
+                    return;
+                }
+
+                final Currency defaultCurrency = GriefDefenderPlugin.getInstance().economyService.get().getDefaultCurrency();
+                playerAccount.deposit(defaultCurrency, BigDecimal.valueOf(accruedBlocks), Sponge.getCauseStackManager().getCurrentCause());
+            } else {
+                int currentTotal = playerData.getAccruedClaimBlocks();
+                if ((currentTotal + accruedBlocks) > playerData.getMaxAccruedClaimBlocks()) {
+                    PlayerStorageData playerStorage = playerData.getStorageData();
+                    playerStorage.getConfig().setAccruedClaimBlocks(playerData.getMaxAccruedClaimBlocks());
+                    playerData.lastAfkCheckLocation = player.getLocation();
+                    return;
+                }
+
+                PlayerStorageData playerStorage = playerData.getStorageData();
+                playerStorage.getConfig().setAccruedClaimBlocks(playerStorage.getConfig().getAccruedClaimBlocks() + accruedBlocks);
+            }
+        }
+
+        playerData.lastAfkCheckLocation = player.getLocation();
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/task/ClaimCleanupTask.java b/sponge/src/main/java/com/griefdefender/task/ClaimCleanupTask.java
new file mode 100644
index 0000000..c410677
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/task/ClaimCleanupTask.java
@@ -0,0 +1,152 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.task;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.reflect.TypeToken;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.ClaimSchematic;
+import com.griefdefender.api.permission.option.Options;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.claim.GDClaimManager;
+import com.griefdefender.configuration.GriefDefenderConfig;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.internal.util.BlockUtil;
+import com.griefdefender.permission.GDPermissionHolder;
+import com.griefdefender.permission.GDPermissionManager;
+import com.griefdefender.util.PermissionUtil;
+import net.kyori.text.Component;
+import net.kyori.text.serializer.plain.PlainComponentSerializer;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.event.CauseStackManager;
+import org.spongepowered.api.world.storage.WorldProperties;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+public class ClaimCleanupTask implements Runnable {
+
+    @Override
+    public void run() {
+        for (WorldProperties worldProperties : Sponge.getServer().getAllWorldProperties()) {
+            GDClaimManager claimManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(worldProperties.getUniqueId());
+            Set<Claim> claimList = claimManager.getWorldClaims();
+            if (claimList.size() == 0) {
+                continue;
+            }
+
+            final GriefDefenderConfig<?> activeConfig = GriefDefenderPlugin.getActiveConfig(worldProperties.getUniqueId());
+            final boolean schematicRestore = activeConfig.getConfig().claim.claimAutoSchematicRestore;
+            Iterator<Claim> iterator = new HashSet<>(claimList).iterator();
+            while (iterator.hasNext()) {
+                GDClaim claim = (GDClaim) iterator.next();
+                final GDPlayerData playerData = claim.getOwnerPlayerData();
+                if (claim.isAdminClaim() || !claim.getInternalClaimData().allowExpiration() || playerData == null) {
+                    continue;
+                }
+
+                if (!playerData.dataInitialized) {
+                    continue;
+                }
+
+                int areaOfDefaultClaim = 0;
+                if (activeConfig.getConfig().claim.autoChestClaimBlockRadius >= 0) {
+                    areaOfDefaultClaim = (int) Math.pow(activeConfig.getConfig().claim.autoChestClaimBlockRadius * 2 + 1, 2);
+                }
+
+                final GDPermissionHolder subject = playerData.getSubject();
+                Instant claimLastActive = claim.getInternalClaimData().getDateLastActive();
+
+                try (final CauseStackManager.StackFrame frame = Sponge.getCauseStackManager().pushCauseFrame()) {
+                    final int claimExpirationChest = playerData.getChestClaimExpiration();
+                    if (claim.getArea() <= areaOfDefaultClaim && claimExpirationChest > 0) {
+                        if (claimLastActive.plus(Duration.ofDays(claimExpirationChest))
+                            .isBefore(Instant.now())) {
+                            playerData.useRestoreSchematic = schematicRestore;
+                            claimManager.deleteClaim(claim);
+                            playerData.useRestoreSchematic = false;
+                            final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.CLAIM_EXPIRED_INACTIVITY,
+                                ImmutableMap.of(
+                                "player", subject.getFriendlyName(),
+                                "uuid", claim.getUniqueId().toString()));
+                            GriefDefenderPlugin.getInstance().getLogger().info(PlainComponentSerializer.INSTANCE.serialize(message));
+                            if (!schematicRestore && activeConfig.getConfig().claim.claimAutoNatureRestore) {
+                                BlockUtil.getInstance().restoreClaim(claim);
+                            }
+                            // remove all context permissions
+                            PermissionUtil.getInstance().clearPermissions(claim);
+                        }
+                        return;
+                    }
+
+                    if (!claim.isBasicClaim()) {
+                        continue;
+                    }
+                    final int optionValue = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), subject, Options.EXPIRATION, claim);
+                    final int optionClaimExpirationBasic = optionValue;
+                    if (optionClaimExpirationBasic > 0) {
+                        final Instant localNow = Instant.now();
+                        final boolean claimNotActive = claimLastActive.plus(Duration.ofDays(optionClaimExpirationBasic)).isBefore(localNow);
+                        if (!claimNotActive) {
+                            final boolean taxEnabled = activeConfig.getConfig().claim.bankTaxSystem;
+                            if (!taxEnabled || !claim.getData().isExpired()) {
+                                continue;
+                            }
+                            final Instant taxPastDueDate = claim.getEconomyData().getTaxPastDueDate().orElse(null);
+                            if (taxPastDueDate == null) {
+                                continue;
+                            }
+
+                            final int taxExpirationDays = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), subject, Options.TAX_EXPIRATION, claim).intValue();
+                            final int expireDaysToKeep = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), subject, Options.TAX_EXPIRATION_DAYS_KEEP, claim).intValue();
+                            if (!taxPastDueDate.plus(Duration.ofDays(taxExpirationDays + expireDaysToKeep)).isBefore(localNow)) {
+                                continue;
+                            }
+                        }
+                    }
+
+                    playerData.useRestoreSchematic = schematicRestore;
+                    claimManager.deleteClaim(claim);
+                    playerData.useRestoreSchematic = false;
+                    final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.CLAIM_EXPIRED_INACTIVITY,
+                            ImmutableMap.of(
+                            "player", subject.getFriendlyName(),
+                            "uuid", claim.getUniqueId().toString()));
+                    GriefDefenderPlugin.getInstance().getLogger().info(PlainComponentSerializer.INSTANCE.serialize(message));
+                    if (!schematicRestore && activeConfig.getConfig().claim.claimAutoNatureRestore) {
+                        BlockUtil.getInstance().restoreClaim(claim);
+                    }
+                    // remove all context permissions
+                    PermissionUtil.getInstance().clearPermissions(claim);
+                }
+            }
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/task/ClaimVisualApplyTask.java b/sponge/src/main/java/com/griefdefender/task/ClaimVisualApplyTask.java
new file mode 100644
index 0000000..79129d1
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/task/ClaimVisualApplyTask.java
@@ -0,0 +1,86 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.task;
+
+import com.griefdefender.GDBootstrap;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.internal.visual.ClaimVisual;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.block.BlockSnapshot;
+import org.spongepowered.api.entity.living.player.Player;
+
+import java.util.ArrayList;
+import java.util.concurrent.TimeUnit;
+
+public class ClaimVisualApplyTask implements Runnable {
+
+    private ClaimVisual visualization;
+    private Player player;
+    private GDPlayerData playerData;
+    private boolean resetActive;
+
+    public ClaimVisualApplyTask(Player player, GDPlayerData playerData, ClaimVisual visualization) {
+        this(player, playerData, visualization, true);
+    }
+
+    public ClaimVisualApplyTask(Player player, GDPlayerData playerData, ClaimVisual visualization, boolean resetActive) {
+        this.visualization = visualization;
+        this.playerData = playerData;
+        this.player = player;
+        this.resetActive = resetActive;
+    }
+
+    @Override
+    public void run() {
+        // Only revert active visual if we are not currently creating a claim
+        if (!this.playerData.visualBlocks.isEmpty() && this.playerData.lastShovelLocation == null) {
+            if (this.resetActive) {
+                this.playerData.revertActiveVisual(this.player);
+            }
+        }
+
+        for (int i = 0; i < this.visualization.getVisualTransactions().size(); i++) {
+            BlockSnapshot snapshot = this.visualization.getVisualTransactions().get(i).getFinal();
+            this.player.sendBlockChange(snapshot.getPosition(), snapshot.getState());
+        }
+
+        if (this.visualization.getClaim() != null) {
+            this.playerData.visualClaimId = this.visualization.getClaim().getUniqueId();
+            this.visualization.getClaim().playersWatching.add(this.player.getUniqueId());
+        }
+        // If we still have active visuals to revert, combine with new
+        if (!this.playerData.visualBlocks.isEmpty()) {
+            this.playerData.visualBlocks.addAll(this.visualization.getVisualTransactions());
+        } else {
+            this.playerData.visualBlocks = new ArrayList<>(this.visualization.getVisualTransactions());
+        }
+
+        if (playerData.lastShovelLocation == null) {
+            this.playerData.visualRevertTask = Sponge.getGame().getScheduler().createTaskBuilder().delay(1, TimeUnit.MINUTES)
+                    .execute(new ClaimVisualRevertTask(this.player, this.playerData)).submit(GDBootstrap.getInstance());
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/task/ClaimVisualRevertTask.java b/sponge/src/main/java/com/griefdefender/task/ClaimVisualRevertTask.java
new file mode 100644
index 0000000..77f8ce1
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/task/ClaimVisualRevertTask.java
@@ -0,0 +1,55 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.task;
+
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import org.spongepowered.api.entity.living.player.Player;
+
+class ClaimVisualRevertTask implements Runnable {
+
+    private Player player;
+    private GDPlayerData playerData;
+
+    public ClaimVisualRevertTask(Player player, GDPlayerData playerData) {
+        this.playerData = playerData;
+        this.player = player;
+    }
+
+    @Override
+    public void run() {
+        // don't do anything if the player's current visualization is different
+        // from the one scheduled to revert
+        if (this.playerData.visualBlocks.isEmpty()) {
+            return;
+        }
+
+        // check for any active WECUI visuals
+        if (GriefDefenderPlugin.getInstance().getWorldEditProvider() != null) {
+            GriefDefenderPlugin.getInstance().getWorldEditProvider().revertVisuals(this.player, this.playerData, this.playerData.visualClaimId);
+        }
+        this.playerData.revertActiveVisual(this.player);
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/task/PlayerTickTask.java b/sponge/src/main/java/com/griefdefender/task/PlayerTickTask.java
new file mode 100644
index 0000000..16a0fdf
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/task/PlayerTickTask.java
@@ -0,0 +1,101 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.task;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.reflect.TypeToken;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.permission.option.Options;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.permission.GDPermissionManager;
+
+import net.kyori.text.TextComponent;
+import net.kyori.text.adapter.spongeapi.TextAdapter;
+import net.kyori.text.format.TextColor;
+
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.data.key.Keys;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.entity.living.player.gamemode.GameMode;
+import org.spongepowered.api.entity.living.player.gamemode.GameModes;
+import org.spongepowered.api.world.World;
+
+public class PlayerTickTask implements Runnable {
+
+    public PlayerTickTask() {
+
+    }
+
+    @Override
+    public void run() {
+        for (World world : Sponge.getServer().getWorlds()) {
+            for (Player player : world.getPlayers()) {
+                if (player.isRemoved()) {
+                    continue;
+                }
+                final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId());
+                final GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(playerData, player.getLocation());
+                // health regen
+                if (world.getProperties().getTotalTime() % 100 == 0L) {
+                    final GameMode gameMode = player.get(Keys.GAME_MODE).get();
+                    // Handle player health regen
+                    if (gameMode != GameModes.CREATIVE && gameMode != GameModes.SPECTATOR) {
+                        final double maxHealth = player.get(Keys.MAX_HEALTH).get();
+                        final double currentHealth = player.get(Keys.HEALTH).get();
+                        if (currentHealth < maxHealth) {
+                            final double regenAmount = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Double.class), playerData.getSubject(), Options.PLAYER_HEALTH_REGEN, claim);
+                            if (regenAmount > 0) {
+                                final double newHealth = currentHealth + regenAmount;
+                                if (newHealth > maxHealth) {
+                                    player.offer(Keys.MAX_HEALTH, maxHealth);
+                                } else {
+                                    player.offer(Keys.HEALTH, newHealth);
+                                }
+                            }
+                        }
+                    }
+                }
+                // teleport delay
+                if (world.getProperties().getTotalTime() % 20 == 0L) {
+                    if (playerData.teleportDelay > 0) {
+                        final int delay = playerData.teleportDelay - 1;
+                        if (delay == 0) {
+                            player.setLocation(playerData.teleportLocation);
+                            playerData.teleportDelay = 0;
+                            playerData.teleportLocation = null;
+                            playerData.teleportSourceLocation = null;
+                            continue;
+                        }
+                        TextAdapter.sendComponent(player, MessageStorage.MESSAGE_DATA.getMessage(MessageStorage.TELEPORT_DELAY_NOTICE, 
+                                ImmutableMap.of("delay", TextComponent.of(delay, TextColor.GOLD))));
+                        playerData.teleportDelay = delay;
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/task/TaxApplyTask.java b/sponge/src/main/java/com/griefdefender/task/TaxApplyTask.java
new file mode 100644
index 0000000..3b2632f
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/task/TaxApplyTask.java
@@ -0,0 +1,193 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.task;
+
+import com.google.common.reflect.TypeToken;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.GriefDefender;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.economy.BankTransactionType;
+import com.griefdefender.api.permission.option.Options;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.claim.GDClaimManager;
+import com.griefdefender.configuration.GriefDefenderConfig;
+import com.griefdefender.economy.GDBankTransaction;
+import com.griefdefender.event.GDTaxClaimEvent;
+import com.griefdefender.permission.GDPermissionManager;
+import com.griefdefender.permission.GDPermissionUser;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.event.CauseStackManager;
+import org.spongepowered.api.service.economy.EconomyService;
+import org.spongepowered.api.service.economy.account.Account;
+import org.spongepowered.api.service.economy.transaction.ResultType;
+import org.spongepowered.api.service.economy.transaction.TransactionResult;
+import org.spongepowered.api.service.permission.Subject;
+import org.spongepowered.api.world.storage.WorldProperties;
+
+import java.math.BigDecimal;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Set;
+
+public class TaxApplyTask implements Runnable {
+
+    final WorldProperties worldProperties;
+    final EconomyService economyService;
+    final GriefDefenderConfig<?> activeConfig;
+    private int bankTransactionLogLimit = 60;
+
+    public TaxApplyTask(WorldProperties worldProperties) {
+        this.worldProperties = worldProperties;
+        this.economyService = GriefDefenderPlugin.getInstance().economyService.get();
+        this.activeConfig = GriefDefenderPlugin.getActiveConfig(this.worldProperties);
+    }
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    @Override
+    public void run() {
+        // don't do anything when there are no claims
+        GDClaimManager claimManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(this.worldProperties.getUniqueId());
+        ArrayList<Claim> claimList = (ArrayList<Claim>) new ArrayList<>(claimManager.getWorldClaims());
+        if (claimList.size() == 0) {
+            return;
+        }
+
+        this.bankTransactionLogLimit = this.activeConfig.getConfig().claim.bankTransactionLogLimit;
+        Iterator<GDClaim> iterator = ((ArrayList) claimList.clone()).iterator();
+        while (iterator.hasNext()) {
+            GDClaim claim = iterator.next();
+            final GDPlayerData playerData = claim.getOwnerPlayerData();
+            if (claim.isWilderness()) {
+                continue;
+            }
+            if (playerData == null) {
+                continue;
+            }
+
+            if (!playerData.dataInitialized) {
+                continue;
+            }
+
+            if (claim.isAdminClaim()) {
+                // search for town
+                final Set<Claim> children = claim.getChildren(false);
+                for (Claim child : children) {
+                    if (child.isTown()) {
+                        handleTownTax((GDClaim) child, playerData);
+                    } else if (child.isBasicClaim()) {
+                        handleClaimTax((GDClaim) child, playerData, false);
+                    }
+                }
+            } else {
+                if (claim.isTown()) {
+                    handleTownTax(claim, playerData);
+                } else if (claim.isBasicClaim()){
+                    handleClaimTax(claim, playerData, false);
+                }
+            }
+        }
+    }
+
+    private void handleClaimTax(GDClaim claim, GDPlayerData playerData, boolean inTown) {
+        final GDPermissionUser user = playerData.getSubject();
+        final Account claimAccount = null; //claim.getEconomyAccount().orElse(null);
+        if (claimAccount == null) {
+            return;
+        }
+        double taxRate = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Double.class), user, Options.TAX_RATE, claim);
+        double taxOwed = claim.getEconomyData().getTaxBalance() + (claim.getClaimBlocks() * taxRate);
+        try (final CauseStackManager.StackFrame frame = Sponge.getCauseStackManager().pushCauseFrame()) {
+            Sponge.getCauseStackManager().pushCause(GriefDefenderPlugin.getInstance());
+            GDTaxClaimEvent event = new GDTaxClaimEvent(claim, taxRate, taxOwed);
+            GriefDefender.getEventManager().post(event);
+            if (event.cancelled()) {
+                return;
+            }
+            final double taxBalance = claim.getEconomyData().getTaxBalance();
+            taxRate = event.getTaxRate();
+            taxOwed = taxBalance + (claim.getClaimBlocks() * taxRate);
+            TransactionResult result = claimAccount.withdraw(this.economyService.getDefaultCurrency(), BigDecimal.valueOf(taxOwed), Sponge.getCauseStackManager().getCurrentCause());
+            if (result.getResult() != ResultType.SUCCESS) {
+                final Instant localNow = Instant.now();
+                Instant taxPastDueDate = claim.getEconomyData().getTaxPastDueDate().orElse(null);
+                if (taxPastDueDate == null) {
+                     claim.getEconomyData().setTaxPastDueDate(Instant.now());
+                } else {
+                    final int taxExpirationDays = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), user, Options.TAX_EXPIRATION, claim);
+                    if (taxExpirationDays > 0) {
+                        claim.getInternalClaimData().setExpired(true);
+                        if (taxExpirationDays == 0) {
+                            claim.getInternalClaimData().setExpired(true);
+                            claim.getData().save();
+                        } else if (taxPastDueDate.plus(Duration.ofDays(taxExpirationDays)).isBefore(localNow)) {
+                            claim.getInternalClaimData().setExpired(true);
+                            claim.getData().save();
+                        }
+                    }
+                }
+                final double totalTaxOwed = taxBalance + taxOwed;
+                claim.getEconomyData().setTaxBalance(totalTaxOwed);
+                claim.getEconomyData().addBankTransaction(new GDBankTransaction(BankTransactionType.TAX_FAIL, Instant.now(), taxOwed));
+            } else {
+                claim.getEconomyData().addBankTransaction(new GDBankTransaction(BankTransactionType.TAX_SUCCESS, Instant.now(), taxOwed));
+                claim.getEconomyData().setTaxPastDueDate(null);
+                claim.getEconomyData().setTaxBalance(0);
+                claim.getInternalClaimData().setExpired(false);
+
+                if (inTown) {
+                    final GDClaim town = claim.getTownClaim();
+                    town.getData()
+                        .getEconomyData()
+                        .addBankTransaction(new GDBankTransaction(BankTransactionType.TAX_SUCCESS, Instant.now(), taxOwed));
+                    //town.getEconomyAccount()
+                     //   .get()
+                     //   .deposit(this.economyService.getDefaultCurrency(), BigDecimal.valueOf(taxOwed), Sponge.getCauseStackManager().getCurrentCause());
+                }
+                claim.getData().save();
+            }
+        }
+    }
+
+    private void handleTownTax(GDClaim town, GDPlayerData playerData) {
+        Account townAccount = null;//town.getEconomyAccount().orElse(null);
+        if (townAccount == null) {
+            // Virtual Accounts not supported by Economy Plugin so ignore
+            return;
+        }
+        Set<Claim> children = town.getChildren(true);
+        for (Claim child : children) {
+            // resident tax
+            if (child.isBasicClaim()) {
+                handleClaimTax((GDClaim) child, playerData, true);
+            }
+        }
+        if (town.getOwnerUniqueId().equals(playerData.playerID)) {
+            handleClaimTax(town, playerData, false);
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/text/action/GDCallbackHolder.java b/sponge/src/main/java/com/griefdefender/text/action/GDCallbackHolder.java
new file mode 100644
index 0000000..8e16d68
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/text/action/GDCallbackHolder.java
@@ -0,0 +1,83 @@
+/*
+ * This file is part of Sponge, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) SpongePowered <https://www.spongepowered.org>
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.text.action;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.cache.RemovalListener;
+import com.google.common.cache.RemovalNotification;
+import org.spongepowered.api.command.CommandSource;
+
+import java.util.Optional;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+public class GDCallbackHolder {
+    public static final String CALLBACK_COMMAND = "callback";
+    public static final String CALLBACK_COMMAND_QUALIFIED = "/gd:" + CALLBACK_COMMAND;
+    private static final GDCallbackHolder INSTANCE = new GDCallbackHolder();
+
+    static final ConcurrentMap<UUID, Consumer<CommandSource>> reverseMap = new ConcurrentHashMap<>();
+    private static final LoadingCache<Consumer<CommandSource>, UUID> callbackCache = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES)
+            .removalListener(new RemovalListener<Consumer<CommandSource>, UUID>() {
+                @Override
+                public void onRemoval(RemovalNotification<Consumer<CommandSource>, UUID> notification) {
+                    reverseMap.remove(notification.getValue(), notification.getKey());
+                }
+            })
+            .build(new CacheLoader<Consumer<CommandSource>, UUID>() {
+                @Override
+                public UUID load(Consumer<CommandSource> key) throws Exception {
+                    UUID ret = UUID.randomUUID();
+                    reverseMap.putIfAbsent(ret, key);
+                    return ret;
+                }
+            });
+
+
+    public static GDCallbackHolder getInstance() {
+        return INSTANCE;
+    }
+
+
+    public UUID getOrCreateIdForCallback(Consumer<CommandSource> callback) {
+        return callbackCache.getUnchecked(checkNotNull(callback, "callback"));
+    }
+
+    public Optional<Consumer<CommandSource>> getCallbackForUUID(UUID id) {
+        return Optional.of(reverseMap.get(id));
+    }
+
+    public String createCallbackRunCommand(Consumer<CommandSource> consumer) {
+        UUID callbackId = getOrCreateIdForCallback(consumer);
+        return CALLBACK_COMMAND_QUALIFIED + " " + callbackId;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/util/BlockPosCache.java b/sponge/src/main/java/com/griefdefender/util/BlockPosCache.java
new file mode 100644
index 0000000..eaae7d5
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/util/BlockPosCache.java
@@ -0,0 +1,61 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.util;
+
+import com.griefdefender.api.Tristate;
+import org.spongepowered.common.SpongeImpl;
+
+public class BlockPosCache {
+
+    private int lastTickCounter;
+    private short lastBlockPos;
+    private Tristate lastResult = Tristate.UNDEFINED;
+
+    public BlockPosCache(short pos) {
+        this.lastBlockPos = pos;
+        this.lastTickCounter = SpongeImpl.getServer().getTickCounter();
+    }
+
+    public void setLastResult(Tristate result) {
+        this.lastResult = result;
+    }
+
+    public Tristate getCacheResult(short pos) {
+        int currentTick = SpongeImpl.getServer().getTickCounter();
+        if (this.lastBlockPos != pos) {
+            this.lastBlockPos = pos;
+            this.lastTickCounter = currentTick;
+            return Tristate.UNDEFINED;
+        }
+
+        if ((currentTick - this.lastTickCounter) <= 2) {
+            this.lastTickCounter = currentTick;
+            return this.lastResult;
+        }
+
+        this.lastTickCounter = currentTick;
+        return Tristate.UNDEFINED;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/util/BootstrapUtil.java b/sponge/src/main/java/com/griefdefender/util/BootstrapUtil.java
new file mode 100644
index 0000000..298bc0e
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/util/BootstrapUtil.java
@@ -0,0 +1,65 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.util;
+
+import com.griefdefender.GDBootstrap;
+
+import java.io.File;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+
+public class BootstrapUtil {
+
+    private final static Method METHOD_ADD_URL;
+
+    static {
+        Method methodAddUrl = null;
+        try {
+            methodAddUrl = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
+            methodAddUrl.setAccessible(true);
+        } catch (NoSuchMethodException e) {
+            e.printStackTrace();
+        }
+        METHOD_ADD_URL = methodAddUrl;
+    }
+
+    public static void addUrlToClassLoader(String name, File input) {
+        try {
+            final ClassLoader classLoader = GDBootstrap.class.getClassLoader();
+            if (classLoader instanceof URLClassLoader) {
+                try {
+                    METHOD_ADD_URL.invoke(classLoader, new URL("jar:file:" + input.getPath() + "!/"));
+                } catch (Throwable t) {
+                    t.printStackTrace();
+                }
+            } else {
+                throw new RuntimeException("Unknown classloader: " + classLoader.getClass());
+            }
+        } catch (Exception ex) {
+            ex.printStackTrace();
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/util/CauseContextHelper.java b/sponge/src/main/java/com/griefdefender/util/CauseContextHelper.java
new file mode 100644
index 0000000..fabc10b
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/util/CauseContextHelper.java
@@ -0,0 +1,256 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.util;
+
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.ClaimContexts;
+import com.griefdefender.api.permission.Context;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.internal.util.NMSUtil;
+import com.griefdefender.permission.GDPermissions;
+import net.kyori.text.TextComponent;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.block.tileentity.TileEntity;
+import org.spongepowered.api.command.CommandSource;
+import org.spongepowered.api.entity.Entity;
+import org.spongepowered.api.entity.living.Living;
+import org.spongepowered.api.entity.living.player.User;
+import org.spongepowered.api.event.Event;
+import org.spongepowered.api.event.cause.Cause;
+import org.spongepowered.api.event.cause.EventContext;
+import org.spongepowered.api.event.cause.EventContextKeys;
+import org.spongepowered.api.event.world.ExplosionEvent;
+import org.spongepowered.api.item.ItemType;
+import org.spongepowered.api.world.storage.WorldProperties;
+import org.spongepowered.common.SpongeImplHooks;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class CauseContextHelper {
+
+    public static User getEventUser(Event event) {
+        final Cause cause = event.getCause();
+        final EventContext context = event.getContext();
+        // Don't attempt to set user for leaf decay
+        if (context.containsKey(EventContextKeys.LEAVES_DECAY)) {
+            return null;
+        }
+
+        User user = null;
+        User fakePlayer = null;
+        if (cause != null) {
+            user = cause.first(User.class).orElse(null);
+            if (user != null && user instanceof Entity && NMSUtil.getInstance().isFakePlayer((Entity) user)) {
+                fakePlayer = user;
+            }
+        }
+
+        // Only check notifier for fire spread
+        if (context.containsKey(EventContextKeys.FIRE_SPREAD)) {
+            return context.get(EventContextKeys.NOTIFIER).orElse(null);
+        }
+
+        if (user == null || fakePlayer != null) {
+            // Always use owner for ticking TE's
+            // See issue MinecraftPortCentral/GriefDefender#610 for more information
+            if (cause.containsType(TileEntity.class)) {
+                user = context.get(EventContextKeys.OWNER)
+                        .orElse(context.get(EventContextKeys.NOTIFIER)
+                                .orElse(context.get(EventContextKeys.CREATOR)
+                                        .orElse(null)));
+            } else {
+                user = context.get(EventContextKeys.NOTIFIER)
+                        .orElse(context.get(EventContextKeys.OWNER)
+                                .orElse(context.get(EventContextKeys.CREATOR)
+                                        .orElse(null)));
+            }
+        }
+
+        if (user == null) {
+            // fall back to fakeplayer if we still don't have a user
+            user = fakePlayer;
+            if (event instanceof ExplosionEvent) {
+                // Check igniter
+                final Living living = context.get(EventContextKeys.IGNITER).orElse(null);
+                if (living != null && living instanceof User) {
+                    user = (User) living;
+                }
+            }
+        }
+
+        return user;
+    }
+
+    // Credit to digitok of freenode for the regex assistance
+    //final String CONTEXT_PATTERN2 = "^contexts?\\[ *(?:[\\w.-]+:[\\w.-]+:[\\w\\/.-]+ *(?:, *(?!\\]$)|(?=\\]$)))+ *\\]$";
+    //private static final Pattern CONTEXT_PATTERN = Pattern.compile("^context?\\[ *(?:[\\w.-]+:[\\w.-]+:[\\w\\/.-]+ *(?:, *(?!\\]$)|(?=\\]$)))+ *\\]$"); 
+
+    // original = private static final Pattern CONTEXT_PATTERN = Pattern.compile("^context?\\[ *((?:[\\w.-]+:[\\w.-]+(?::[\\w\\/.-]+)? *(?:, *(?!\\]$)|(?=\\]$)))+) *\\]$");
+    private static final Pattern CONTEXT_PATTERN = Pattern.compile("^context?\\[ *((?:[\\w.-]+=[\\w.-]+(?::[\\w\\/.-]+)? *(?:, *(?!\\]$)|(?=\\]$)))+) *\\]$");
+    private static final Pattern CONTEXT_SPLIT = Pattern.compile("^context?\\[ *((?:[\\w.-]+:[\\w.-]+:[\\w\\/.-]+(?: *, *(?!\\]$)|(?= *\\]$)))+) *\\]$");
+    private static final List<String> VALID_CONTEXTS = Arrays.asList("world", "server", "mode", "player", "group", "source", "used_item", "type");
+    //  final String regex = "^context?\\[ *((?:[\\w.-]+:[\\w.-]+:[\\w\\/.-]+(?: *, *(?!\\]$)|(?= *\\]$)))+) *\\]$";
+    public static Set<Context> generateContexts(CommandSource src, Claim claim, String context) {
+        return generateContexts(src, claim, context, false);
+    }
+
+    public static Set<Context> generateContexts(CommandSource src, Claim claim, String context, boolean isOption) {
+        // verify context is valid
+        if (context == null) {
+            return new HashSet<>();
+        }
+        context = context.replace(" ", "");
+        Matcher matcher = CONTEXT_PATTERN.matcher(context);
+        if (!matcher.find()) {
+            GriefDefenderPlugin.sendMessage(src, TextComponent.of("Invalid context entered."));
+            return null;
+        }
+        /*if (context.contains("mode=") && !context.contains("type=")) {
+            GriefDefenderPlugin.sendMessage(src, Text.of("Context 'mode' requires 'type'."));
+            return null;
+        }*/
+
+        final boolean canManageDefaults = src.hasPermission(GDPermissions.MANAGE_FLAG_DEFAULTS);
+        final boolean canManageOverrides = src.hasPermission(GDPermissions.MANAGE_FLAG_OVERRIDES);
+        final Set<Context> contextSet = new HashSet<>();
+        final String contexts = matcher.group(1);
+        String[] split = contexts.split(",");
+        String reason = null;
+        for (int i = 0; i < split.length; i++) {
+            String[] parts = split[i].split("=");
+            //final String[] parts = split[i].split(":");
+            if (parts.length < 2) {
+                GriefDefenderPlugin.sendMessage(src, TextComponent.of("Invalid context entered."));
+                return null;
+            }
+            final String contextName = parts[0];
+            parts = parts[1].split(":");
+            if (parts.length < 1) {
+                GriefDefenderPlugin.sendMessage(src, TextComponent.of("Invalid context entered."));
+                return null;
+            }
+            final String arg1 = parts[0];
+            final String arg2 = parts.length > 1 ? parts[1] : null;
+            String id = "";
+            if (arg2 == null) {
+                id = "minecraft:" + arg1;
+            } else {
+                id = arg1 + ":" + arg2;
+            }
+            if (contextName.equals("world")) {
+                boolean found = false;
+                for (WorldProperties worldProperties : Sponge.getServer().getAllWorldProperties()) {
+                    if (arg1.equalsIgnoreCase(worldProperties.getWorldName())) {
+                        contextSet.add(new Context(contextName, arg1));
+                        found = true;
+                        break;
+                    }
+                }
+                if (!found) {
+                    GriefDefenderPlugin.sendMessage(src, TextComponent.of("No world found with name '" + arg1 + "'."));
+                    return null;
+                }
+            } else if (contextName.equals("server")) {
+                contextSet.add(new Context(contextName, arg1));
+            } else if (contextName.equals("default")) {
+                if (!canManageDefaults) {
+                    GriefDefenderPlugin.sendMessage(src, MessageCache.getInstance().PERMISSION_FLAG_DEFAULTS);
+                    return new HashSet<>();
+                }
+                if (arg1.equals("any") || arg1.equals("global")) {
+                    contextSet.add(ClaimContexts.GLOBAL_DEFAULT_CONTEXT);
+                } else if (arg1.equals("admin")) {
+                    contextSet.add(ClaimContexts.ADMIN_DEFAULT_CONTEXT);
+                } else if (arg1.equals("basic")) {
+                    contextSet.add(ClaimContexts.BASIC_DEFAULT_CONTEXT);
+                } else if (arg1.equals("town")) {
+                    contextSet.add(ClaimContexts.TOWN_DEFAULT_CONTEXT);
+                } else if (arg1.equals("wilderness")) {
+                    contextSet.add(ClaimContexts.WILDERNESS_DEFAULT_CONTEXT);
+                } else { 
+                    GriefDefenderPlugin.sendMessage(src, TextComponent.of(contextName + " context requires format '" + contextName + ":type'. \nValid types are 'world', 'server', and 'global'."));
+                    return null;
+                }
+            } else if (contextName.equals("override")) {
+                if (isOption) {
+                    GriefDefenderPlugin.sendMessage(src, TextComponent.of("Options do not support overrides."));
+                    return null;
+                }
+                if (!canManageOverrides) {
+                    GriefDefenderPlugin.sendMessage(src, MessageCache.getInstance().PERMISSION_FLAG_OVERRIDES);
+                    return null;
+                }
+                if (arg1.equals("any") || arg1.equals("global")) {
+                    contextSet.add(ClaimContexts.GLOBAL_OVERRIDE_CONTEXT);
+                } else if (arg1.equals("admin")) {
+                    contextSet.add(ClaimContexts.ADMIN_OVERRIDE_CONTEXT);
+                } else if (arg1.equals("basic")) {
+                    contextSet.add(ClaimContexts.BASIC_OVERRIDE_CONTEXT);
+                } else if (arg1.equals("town")) {
+                    contextSet.add(ClaimContexts.TOWN_OVERRIDE_CONTEXT);
+                } else if (arg1.equals("wilderness")) {
+                    contextSet.add(ClaimContexts.WILDERNESS_OVERRIDE_CONTEXT);
+                } else if (arg1.equals("claim")) {
+                    contextSet.add(((GDClaim) claim).getOverrideClaimContext());
+                } else { 
+                    GriefDefenderPlugin.sendMessage(src, TextComponent.of(contextName + " context requires format '" + contextName + ":type'. \nValid types are 'world', 'server', and 'global'."));
+                    return new HashSet<>();
+                }
+            } else if (contextName.equals("player")) {
+                contextSet.add(new Context(contextName, arg1));
+            } else if (contextName.equals("group")) {
+                contextSet.add(new Context(contextName, arg1));
+            } else if (contextName.equals("meta")) {
+                contextSet.add(new Context(contextName, arg1));
+            } else if (contextName.equals("source")) {
+                contextSet.add(new Context(contextName, id));
+            } else if (contextName.contentEquals("state")) {
+                contextSet.add(new Context(contextName, id));
+            } else if (contextName.equals("used_item")) {
+                final ItemType type = Sponge.getRegistry().getType(ItemType.class, id).orElse(null); 
+                if (type == null) {
+                    GriefDefenderPlugin.sendMessage(src, TextComponent.of("Invalid context entered."));
+                    return null;
+                }
+                contextSet.add(new Context(contextName, type.getId()));
+            } else {
+                if (arg2 == null) {
+                    contextSet.add(new Context(contextName, arg1));
+                } else {
+                    contextSet.add(new Context(contextName, id));
+                }
+            }
+        }
+
+        return contextSet;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/util/ClaimClickData.java b/sponge/src/main/java/com/griefdefender/util/ClaimClickData.java
new file mode 100644
index 0000000..f79db23
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/util/ClaimClickData.java
@@ -0,0 +1,37 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.util;
+
+import com.griefdefender.claim.GDClaim;
+
+public class ClaimClickData {
+    public final GDClaim claim;
+    public final Object value;
+
+    public ClaimClickData(GDClaim claim, Object value) {
+        this.claim = claim;
+        this.value = value;
+    }
+}
\ No newline at end of file
diff --git a/sponge/src/main/java/com/griefdefender/util/EconomyUtil.java b/sponge/src/main/java/com/griefdefender/util/EconomyUtil.java
new file mode 100644
index 0000000..7b3b85a
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/util/EconomyUtil.java
@@ -0,0 +1,213 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.util;
+
+import com.flowpowered.math.vector.Vector3i;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.reflect.TypeToken;
+import com.griefdefender.GDBootstrap;
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.claim.ClaimResult;
+import com.griefdefender.api.claim.ClaimResultType;
+import com.griefdefender.api.claim.ClaimType;
+import com.griefdefender.api.permission.option.Options;
+import com.griefdefender.cache.MessageCache;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.claim.GDClaimResult;
+import com.griefdefender.command.CommandHelper;
+import com.griefdefender.configuration.MessageStorage;
+import com.griefdefender.event.GDCauseStackManager;
+import com.griefdefender.internal.provider.WorldEditProvider;
+import com.griefdefender.internal.util.BlockUtil;
+import com.griefdefender.permission.GDPermissionManager;
+import com.griefdefender.permission.GDPermissionUser;
+import com.griefdefender.text.action.GDCallbackHolder;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.event.ClickEvent;
+import net.kyori.text.format.TextColor;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.command.CommandSource;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.event.CauseStackManager;
+import org.spongepowered.api.event.cause.EventContextKeys;
+import org.spongepowered.api.service.economy.Currency;
+import org.spongepowered.api.service.economy.account.Account;
+import org.spongepowered.api.service.economy.transaction.ResultType;
+import org.spongepowered.api.service.economy.transaction.TransactionResult;
+import org.spongepowered.api.world.World;
+
+import java.math.BigDecimal;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
+import java.util.function.Consumer;
+
+public class EconomyUtil {
+
+    private static EconomyUtil instance;
+
+    public static EconomyUtil getInstance() {
+        return instance;
+    }
+
+    static {
+        instance = new EconomyUtil();
+    }
+
+    public void economyCreateClaimConfirmation(Player player, GDPlayerData playerData, int height, Vector3i point1, Vector3i point2, ClaimType claimType, boolean cuboid, Claim parent) {
+        GDClaim claim = new GDClaim(player.getWorld(), point1, point2, claimType, player.getUniqueId(), cuboid);
+        claim.parent = (GDClaim) parent;
+        final GDPermissionUser user = PermissionHolderCache.getInstance().getOrCreateUser(player);
+        final int claimCost = BlockUtil.getInstance().getClaimBlockCost(player.getWorld(), claim.lesserBoundaryCorner, claim.greaterBoundaryCorner, claim.cuboid);
+        final Double economyBlockCost = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Double.class), user, Options.ECONOMY_BLOCK_COST, claim);
+        final double requiredFunds = claimCost * economyBlockCost;
+        final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.ECONOMY_CLAIM_BUY_CONFIRMATION,
+                ImmutableMap.of("amount", String.valueOf("$" + requiredFunds)));
+        final Component buyConfirmationText = TextComponent.builder()
+                .append(message)
+                .append(TextComponent.builder()
+                    .append("\n[")
+                    .append(MessageCache.getInstance().LABEL_CONFIRM.color(TextColor.GREEN))
+                    .append("]\n")
+                    .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(economyClaimBuyConfirmed(player, playerData, height, requiredFunds, point1, point2, claimType, cuboid, parent)))).build())
+                .build();
+        GriefDefenderPlugin.sendMessage(player, buyConfirmationText);
+    }
+
+    private static Consumer<CommandSource> economyClaimBuyConfirmed(Player player, GDPlayerData playerData, int height, double requiredFunds, Vector3i lesserBoundaryCorner, Vector3i greaterBoundaryCorner, ClaimType claimType, boolean cuboid, Claim parent) {
+        return confirm -> {
+            // try to create a new claim
+            ClaimResult result = null;
+            GDCauseStackManager.getInstance().pushCause(player);
+            result = GriefDefenderPlugin.getInstance().dataStore.createClaim(
+                    player.getWorld(),
+                    lesserBoundaryCorner,
+                    greaterBoundaryCorner,
+                    claimType, player.getUniqueId(), cuboid);
+            GDCauseStackManager.getInstance().popCause();
+
+            GDClaim gdClaim = (GDClaim) result.getClaim().orElse(null);
+            // if it didn't succeed, tell the player why
+            if (!result.successful()) {
+                if (result.getResultType() == ClaimResultType.OVERLAPPING_CLAIM) {
+                    GDClaim overlapClaim = (GDClaim) result.getClaim().get();
+                    GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().CREATE_OVERLAP_SHORT);
+                    Set<Claim> claims = new HashSet<>();
+                    claims.add(overlapClaim);
+                    CommandHelper.showClaims(player, claims, height, true);
+                } else {
+                    GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.CREATE_FAILED_RESULT,
+                            ImmutableMap.of("reason", result.getResultType())));
+                }
+                return;
+            }
+
+            // otherwise, advise him on the /trust command and show him his new claim
+            else {
+                Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.ECONOMY_CLAIM_BUY_CONFIRMED, ImmutableMap.of(
+                            "amount", requiredFunds));
+                GriefDefenderPlugin.sendMessage(player, message);
+                playerData.lastShovelLocation = null;
+                message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.CREATE_SUCCESS, ImmutableMap.of(
+                        "type", gdClaim.getFriendlyNameType(true)));
+                GriefDefenderPlugin.sendMessage(player, message);
+                final WorldEditProvider worldEditProvider = GriefDefenderPlugin.getInstance().worldEditProvider;
+                if (worldEditProvider != null) {
+                    worldEditProvider.stopVisualDrag(player);
+                    worldEditProvider.visualizeClaim(gdClaim, player, playerData, false);
+                }
+                gdClaim.getVisualizer().createClaimBlockVisuals(height, player.getLocation(), playerData);
+                gdClaim.getVisualizer().apply(player, false);
+            }
+        };
+    }
+
+    public static TransactionResult depositFunds(UUID uuid, double amount) {
+        final Account playerAccount = GriefDefenderPlugin.getInstance().economyService.get().getOrCreateAccount(uuid).orElse(null);
+        final Currency defaultCurrency = GriefDefenderPlugin.getInstance().economyService.get().getDefaultCurrency();
+        return playerAccount.deposit(defaultCurrency, BigDecimal.valueOf(amount), Sponge.getCauseStackManager().getCurrentCause());
+    }
+
+    public static TransactionResult withdrawFunds(UUID uuid, double amount) {
+        final Account playerAccount = GriefDefenderPlugin.getInstance().economyService.get().getOrCreateAccount(uuid).orElse(null);
+        final Currency defaultCurrency = GriefDefenderPlugin.getInstance().economyService.get().getDefaultCurrency();
+        return playerAccount.withdraw(defaultCurrency, BigDecimal.valueOf(amount), Sponge.getCauseStackManager().getCurrentCause());
+    }
+
+    public GDClaimResult checkEconomyFunds(GDClaim claim, GDPlayerData newPlayerData, boolean withdrawFunds) {
+        if (!GriefDefenderPlugin.getInstance().isEconomyModeEnabled()) { 
+            return new GDClaimResult(claim, ClaimResultType.ECONOMY_ACCOUNT_NOT_FOUND);
+        }
+
+        final Object root = GDCauseStackManager.getInstance().getCurrentCause().root();
+        final Player player = root instanceof Player ? (Player) root : null; 
+        final World world = claim.getWorld();
+        final int claimCost = BlockUtil.getInstance().getClaimBlockCost(world, claim.lesserBoundaryCorner, claim.greaterBoundaryCorner, claim.cuboid);
+        final GDPermissionUser targetPlayer = newPlayerData.getSubject();
+        final Account playerAccount = GriefDefenderPlugin.getInstance().economyService.get().getOrCreateAccount(targetPlayer.getUniqueId()).orElse(null);
+        if (playerAccount == null) {
+            return new GDClaimResult(claim, ClaimResultType.ECONOMY_ACCOUNT_NOT_FOUND);
+        }
+
+        final Currency defaultCurrency = GriefDefenderPlugin.getInstance().economyService.get().getDefaultCurrency();
+        double requiredFunds = claimCost * claim.getOwnerEconomyBlockCost();
+        final BigDecimal currentFunds = playerAccount.getBalance(defaultCurrency);
+        if (currentFunds.doubleValue() < requiredFunds) {
+            Component message = null;
+            if (player != null) {
+                message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.ECONOMY_NOT_ENOUGH_FUNDS, ImmutableMap.of(
+                        "balance", String.valueOf("$" + currentFunds.doubleValue()),
+                        "amount", String.valueOf("$" + requiredFunds)));
+                GriefDefenderPlugin.sendMessage(player, message);
+            }
+
+            //playerData.lastShovelLocation = null;
+           // playerData.claimResizing = null;
+            return new GDClaimResult(claim, ClaimResultType.ECONOMY_NOT_ENOUGH_FUNDS, message);
+        }
+
+        if (withdrawFunds) {
+            final TransactionResult result = playerAccount.withdraw(defaultCurrency, BigDecimal.valueOf(requiredFunds), Sponge.getCauseStackManager().getCurrentCause());
+            if (result.getResult() != ResultType.SUCCESS) {
+                Component message = null;
+                if (player != null) {
+                    message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.ECONOMY_WITHDRAW_ERROR, ImmutableMap.of(
+                            "reason", result.getResult().name()));
+                    GriefDefenderPlugin.sendMessage(player, message);
+                }
+
+                //playerData.lastShovelLocation = null;
+                //playerData.claimResizing = null;
+                return new GDClaimResult(claim, ClaimResultType.ECONOMY_WITHDRAW_FAIL, message);
+            }
+        }
+
+        return new GDClaimResult(claim, ClaimResultType.SUCCESS);
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/util/EntityUtils.java b/sponge/src/main/java/com/griefdefender/util/EntityUtils.java
new file mode 100644
index 0000000..7824b28
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/util/EntityUtils.java
@@ -0,0 +1,72 @@
+/*
+ * This file is part of Sponge, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) SpongePowered <https://www.spongepowered.org>
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.util;
+
+import net.minecraft.entity.IEntityOwnable;
+import net.minecraft.entity.player.EntityPlayer;
+import org.spongepowered.api.entity.Entity;
+
+import java.util.UUID;
+
+import javax.annotation.Nullable;
+
+public class EntityUtils {
+
+    public static UUID getOwnerUniqueId(Entity entity) {
+        if (entity instanceof EntityPlayer) {
+            return null;
+        }
+
+        UUID ownerUniqueId = entity.getCreator().orElse(null);
+        if (ownerUniqueId == null && entity instanceof IEntityOwnable) {
+            IEntityOwnable ownable = (IEntityOwnable) entity;
+            ownerUniqueId = ownable.getOwnerId();
+        }
+
+        return ownerUniqueId;
+    }
+
+    @Nullable
+    public static Entity getControllingPassenger(Entity entity) {
+        return entity.getPassengers().isEmpty() ? null :entity.getPassengers().get(0);
+    }
+
+    public static String getFriendlyName(net.minecraft.entity.Entity mcEntity) {
+        String entityName = mcEntity.getName();
+        final String[] parts = entityName.split(":");
+        if (parts.length > 1) {
+            entityName = parts[1];
+        }
+        if (entityName.contains(".")) {
+            if ((entityName.indexOf(".") + 1) < entityName.length()) {
+                entityName = entityName.substring(entityName.indexOf(".") + 1, entityName.length());
+            }
+        }
+
+        entityName = entityName.replace("entity", "");
+        entityName = entityName.replaceAll("[^A-Za-z0-9]", "");
+        return entityName;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/util/HttpClient.java b/sponge/src/main/java/com/griefdefender/util/HttpClient.java
new file mode 100644
index 0000000..dcce195
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/util/HttpClient.java
@@ -0,0 +1,107 @@
+/*
+ * This file is part of LuckPerms, licensed under the MIT License.
+ *
+ *  Copyright (c) lucko (Luck) <luck@lucko.me>
+ *  Copyright (c) contributors
+ *
+ *  Permission is hereby granted, free of charge, to any person obtaining a copy
+ *  of this software and associated documentation files (the "Software"), to deal
+ *  in the Software without restriction, including without limitation the rights
+ *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ *  copies of the Software, and to permit persons to whom the Software is
+ *  furnished to do so, subject to the following conditions:
+ *
+ *  The above copyright notice and this permission notice shall be included in all
+ *  copies or substantial portions of the Software.
+ *
+ *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ *  SOFTWARE.
+ */
+package com.griefdefender.util;
+
+import okhttp3.Interceptor;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import okhttp3.ResponseBody;
+
+import java.io.IOException;
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.SocketAddress;
+import java.net.URI;
+import java.util.Collections;
+import java.util.List;
+
+public class HttpClient {
+    private static OkHttpClient client = null;
+
+    private static synchronized OkHttpClient getClient() {
+        if (client == null) {
+            client = new OkHttpClient.Builder()
+                    .proxySelector(new NullSafeProxySelector())
+                    .addInterceptor(new UserAgentInterceptor())
+                    .build();
+        }
+        return client;
+    }
+
+    public static Response makeCall(Request request) throws IOException {
+        Response response = getClient().newCall(request).execute();
+        if (!response.isSuccessful()) {
+            throw exceptionForUnsuccessfulResponse(response);
+        }
+        return response;
+    }
+
+    private static RuntimeException exceptionForUnsuccessfulResponse(Response response) {
+        String msg = "";
+        try (ResponseBody responseBody = response.body()) {
+            if (responseBody != null) {
+                msg = responseBody.string();
+            }
+        } catch (IOException e) {
+            // ignore
+        }
+        return new RuntimeException("Got response: " + response.code() + " - " + response.message() + " - " + msg);
+    }
+
+    private static final class UserAgentInterceptor implements Interceptor {
+        @Override
+        public Response intercept(Chain chain) throws IOException {
+            Request orig = chain.request();
+            Request modified = orig.newBuilder()
+                    .header("User-Agent", "griefdefender")
+                    .build();
+
+            return chain.proceed(modified);
+        }
+    }
+
+    // sometimes ProxySelector#getDefault returns null, and okhttp doesn't like that
+    private static final class NullSafeProxySelector extends ProxySelector {
+        private static final List<Proxy> DIRECT = Collections.singletonList(Proxy.NO_PROXY);
+
+        @Override
+        public List<Proxy> select(URI uri) {
+            ProxySelector def = ProxySelector.getDefault();
+            if (def == null) {
+                return DIRECT;
+            }
+            return def.select(uri);
+        }
+
+        @Override
+        public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
+            ProxySelector def = ProxySelector.getDefault();
+            if (def != null) {
+                def.connectFailed(uri, sa, ioe);
+            }
+        }
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/util/PaginationUtil.java b/sponge/src/main/java/com/griefdefender/util/PaginationUtil.java
new file mode 100644
index 0000000..5d29558
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/util/PaginationUtil.java
@@ -0,0 +1,93 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.util;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+// A bad hack at attempting to track active pages
+public class PaginationUtil {
+
+    private static PaginationUtil instance;
+
+    public static PaginationUtil getInstance() {
+        return instance;
+    }
+
+    static {
+        instance = new PaginationUtil();
+    }
+
+    // The active page resets when a new command is entered by same player
+    private final Map<UUID, Integer> activePageMap = new HashMap<>();
+
+    public void updateActiveCommand(UUID uuid, String command, String args) {
+        if (command.equalsIgnoreCase("callback") || command.equalsIgnoreCase("gpreload")) {
+            // ignore
+            return;
+        }
+
+        if (command.equalsIgnoreCase("page")) {
+            final Integer activePage = this.activePageMap.get(uuid);
+            if (activePage != null) {
+                try {
+                    final Integer page = Integer.parseInt(args);
+                    this.activePageMap.put(uuid, page);
+                } catch (Throwable t) {
+                    
+                }
+            }
+            return;
+        }
+
+        if (command.equalsIgnoreCase("pagination")) {
+            final Integer activePage = this.activePageMap.get(uuid);
+            if (activePage != null) {
+                final boolean isNext = args.contains("next");
+                if (isNext) {
+                    this.activePageMap.put(uuid, activePage + 1);
+                } else if (activePage != 1) {
+                    this.activePageMap.put(uuid, activePage - 1);
+                }
+            }
+            return;
+        }
+
+        resetActivePage(uuid);
+    }
+
+    public void resetActivePage(UUID uuid) {
+        this.activePageMap.put(uuid, 1);
+    }
+
+    public Integer getActivePage(UUID uuid) {
+        return this.activePageMap.get(uuid);
+    }
+
+    public void removeActivePageData(UUID uuid) {
+        this.activePageMap.remove(uuid);
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/util/PermissionUtil.java b/sponge/src/main/java/com/griefdefender/util/PermissionUtil.java
new file mode 100644
index 0000000..c8612e5
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/util/PermissionUtil.java
@@ -0,0 +1,242 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.util;
+
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.Tristate;
+import com.griefdefender.api.claim.Claim;
+import com.griefdefender.api.permission.Context;
+import com.griefdefender.api.permission.PermissionResult;
+import com.griefdefender.api.permission.flag.Flag;
+import com.griefdefender.api.permission.option.Option;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.claim.GDClaim;
+import com.griefdefender.permission.GDPermissionHolder;
+import com.griefdefender.provider.PermissionProvider;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+public class PermissionUtil {
+
+    private final PermissionProvider PERMISSION_PROVIDER;
+
+    private static PermissionUtil instance;
+
+    public static PermissionUtil getInstance() {
+        return instance;
+    }
+
+    static {
+        instance = new PermissionUtil();
+    }
+
+    public PermissionUtil() {
+        this.PERMISSION_PROVIDER = GriefDefenderPlugin.getInstance().getPermissionProvider();
+    }
+
+    public boolean hasGroupSubject(String identifier) {
+        return PERMISSION_PROVIDER.hasGroupSubject(identifier);
+    }
+
+    public UUID lookupUserUniqueId(String name) {
+        return PERMISSION_PROVIDER.lookupUserUniqueId(name);
+    }
+
+    public List<String> getAllLoadedPlayerNames() {
+        return PERMISSION_PROVIDER.getAllLoadedPlayerNames();
+    } 
+
+    public List<String> getAllLoadedGroupNames() {
+        return PERMISSION_PROVIDER.getAllLoadedGroupNames();
+    } 
+
+    public void addActiveContexts(Set<Context> contexts, GDPermissionHolder permissionHolder) {
+        PERMISSION_PROVIDER.addActiveContexts(contexts, permissionHolder);
+    }
+
+    public void addActiveContexts(Set<Context> contexts, GDPermissionHolder permissionHolder, GDPlayerData playerData, Claim claim) {
+        PERMISSION_PROVIDER.addActiveContexts(contexts, permissionHolder, null, null);
+    }
+
+    public boolean containsDefaultContext(Set<Context> contexts) {
+        for (Context context : contexts) {
+            if (context.getKey().equals("gd_claim_default")) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    public boolean containsOverrideContext(Set<Context> contexts) {
+        for (Context context : contexts) {
+            if (context.getKey().equals("gd_claim_override")) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    public void clearPermissions(GDClaim claim) {
+        PERMISSION_PROVIDER.clearPermissions(claim);
+    }
+
+    public void clearPermissions(GDPermissionHolder holder, Context context) {
+        PERMISSION_PROVIDER.clearPermissions(holder, context);
+    }
+
+    public void clearPermissions(GDPermissionHolder holder, Set<Context> contexts) {
+        PERMISSION_PROVIDER.clearPermissions(holder, contexts);
+    }
+
+    public boolean holderHasPermission(GDPermissionHolder holder, String permission) {
+        return PERMISSION_PROVIDER.holderHasPermission(holder, permission);
+    }
+
+    public Map<String, Boolean> getPermissions(GDPermissionHolder holder, Set<Context> contexts) {
+        return PERMISSION_PROVIDER.getPermissions(holder, contexts);
+    }
+
+    public Map<String, String> getOptions(GDPermissionHolder holder, Set<Context> contexts) {
+        return PERMISSION_PROVIDER.getOptions(holder, contexts);
+    }
+
+    public Map<Set<Context>, Map<String, Boolean>> getPermanentPermissions(GDPermissionHolder holder) {
+        return PERMISSION_PROVIDER.getPermanentPermissions(holder);
+    }
+
+    public Map<Set<Context>, Map<String, Boolean>> getTransientPermissions(GDPermissionHolder holder) {
+        return PERMISSION_PROVIDER.getTransientPermissions(holder);
+    }
+
+    public Map<Set<Context>, Map<String, String>> getPermanentOptions(GDPermissionHolder holder) {
+        return PERMISSION_PROVIDER.getPermanentOptions(holder);
+    }
+
+    public Map<Set<Context>, Map<String, String>> getTransientOptions(GDPermissionHolder holder) {
+        return PERMISSION_PROVIDER.getTransientOptions(holder);
+    }
+
+    public Map<String, String> getPermanentOptions(GDPermissionHolder holder, Set<Context> contexts) {
+        return PERMISSION_PROVIDER.getPermanentOptions(holder, contexts);
+    }
+
+    public Map<String, String> getTransientOptions(GDPermissionHolder holder, Set<Context> contexts) {
+        return PERMISSION_PROVIDER.getTransientOptions(holder, contexts);
+    }
+
+    public Map<Set<Context>, Map<String, Boolean>> getAllPermissions(GDPermissionHolder holder) {
+        return PERMISSION_PROVIDER.getAllPermissions(holder);
+    }
+
+    public Tristate getPermissionValue(GDPermissionHolder holder, String permission) {
+        return PERMISSION_PROVIDER.getPermissionValue(holder, permission);
+    }
+
+    public Tristate getPermissionValue(GDClaim claim, GDPermissionHolder holder, String permission, Set<Context> contexts) {
+        return PERMISSION_PROVIDER.getPermissionValue(claim, holder, permission, contexts);
+    }
+
+    public Tristate getPermissionValue(GDClaim claim, GDPermissionHolder holder, String permission, Set<Context> contexts, boolean checkTransient) {
+        return PERMISSION_PROVIDER.getPermissionValue(claim, holder, permission, contexts, checkTransient);
+    }
+
+    public Tristate getPermissionValue(GDPermissionHolder holder, String permission, Set<Context> contexts) {
+        return PERMISSION_PROVIDER.getPermissionValue(holder, permission, contexts);
+    }
+
+    public Tristate getPermissionValueWithRequiredContexts(GDClaim claim, GDPermissionHolder holder, String permission, Set<Context> contexts, String contextFilter) {
+        return PERMISSION_PROVIDER.getPermissionValueWithRequiredContexts(claim, holder, permission, contexts, contextFilter);
+    }
+
+    public String getOptionValue(GDPermissionHolder holder, Option option, Set<Context> contexts) {
+        return PERMISSION_PROVIDER.getOptionValue(holder, option, contexts);
+    }
+
+    public PermissionResult setOptionValue(GDPermissionHolder holder, String permission, String value, Set<Context> contexts) {
+        return PERMISSION_PROVIDER.setOptionValue(holder, permission, value, contexts);
+    }
+
+    public PermissionResult setPermissionValue(GDPermissionHolder holder, Flag flag, Tristate value, Set<Context> contexts) {
+        return PERMISSION_PROVIDER.setPermissionValue(holder, flag, value, contexts);
+    }
+
+    public boolean setPermissionValue(GDPermissionHolder holder, String permission, Tristate value, Set<Context> contexts) {
+        return PERMISSION_PROVIDER.setPermissionValue(holder, permission, value, contexts);
+    }
+
+    public void setTransientOption(GDPermissionHolder holder, String permission, String value, Set<Context> contexts) {
+        PERMISSION_PROVIDER.setTransientOption(holder, permission, value, contexts);
+    }
+
+    public void setTransientPermission(GDPermissionHolder holder, String permission, Boolean value, Set<Context> contexts) {
+        PERMISSION_PROVIDER.setTransientPermission(holder, permission, value, contexts);
+    }
+
+    public void refreshCachedData(GDPermissionHolder holder) {
+        PERMISSION_PROVIDER.refreshCachedData(holder);
+    }
+
+    public boolean containsKey(Set<Context> contexts, String key) {
+        for (Context context : contexts) {
+            if (context.getKey().equalsIgnoreCase(key)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public Tristate getTristateFromString(String value) {
+        Tristate tristate = null;
+        int intValue = -999;
+        try {
+            intValue = Integer.parseInt(value);
+            if (intValue <= -1) {
+                tristate = Tristate.FALSE;
+            } else if (intValue == 0) {
+                tristate = Tristate.UNDEFINED;
+            } else {
+                tristate = Tristate.TRUE;
+            }
+            return tristate;
+
+        } catch (NumberFormatException e) {
+            // ignore
+        }
+
+        try {
+            tristate = Tristate.valueOf(value.toUpperCase());
+        } catch (IllegalArgumentException e) {
+            return null;
+        }
+
+        return tristate;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/util/PlayerUtil.java b/sponge/src/main/java/com/griefdefender/util/PlayerUtil.java
new file mode 100644
index 0000000..84056fc
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/util/PlayerUtil.java
@@ -0,0 +1,207 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.util;
+
+import com.griefdefender.GDPlayerData;
+import com.griefdefender.GriefDefenderPlugin;
+import com.griefdefender.api.claim.ClaimType;
+import com.griefdefender.api.claim.ClaimTypes;
+import com.griefdefender.api.claim.ShovelType;
+import com.griefdefender.api.claim.ShovelTypes;
+import com.griefdefender.api.permission.option.type.CreateModeTypes;
+import com.griefdefender.cache.PermissionHolderCache;
+import com.griefdefender.internal.visual.ClaimVisual;
+import com.griefdefender.internal.visual.GDClaimVisualType;
+import com.griefdefender.permission.GDPermissionUser;
+import net.kyori.text.Component;
+import net.kyori.text.TextComponent;
+import net.kyori.text.format.TextColor;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.data.property.entity.EyeLocationProperty;
+import org.spongepowered.api.data.type.HandTypes;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.entity.living.player.User;
+import org.spongepowered.api.item.ItemType;
+import org.spongepowered.api.item.inventory.ItemStack;
+import org.spongepowered.api.service.user.UserStorageService;
+import org.spongepowered.api.util.Direction;
+
+import java.util.Optional;
+import java.util.UUID;
+
+import javax.annotation.Nullable;
+
+public class PlayerUtil {
+
+    private static Direction[] faces = { Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST};
+
+    private static PlayerUtil instance;
+
+    public static PlayerUtil getInstance() {
+        return instance;
+    }
+
+    static {
+        instance = new PlayerUtil();
+    }
+
+    public Direction getBlockFace(Player player) {
+        return faces[Math.round((int) player.getTransform().getYaw() / 90f) & 0x3].getOpposite();
+    }
+
+    public Direction getBlockFace(String param) {
+        Direction face = null;
+        try {
+            face = Direction.valueOf(param.toUpperCase());
+        } catch (IllegalArgumentException e) {
+            // ignore
+        }
+        return face;
+    }
+
+    public boolean hasItemInOneHand(Player player, ItemType itemType) {
+        ItemStack mainHand = player.getItemInHand(HandTypes.MAIN_HAND).orElse(null);
+        ItemStack offHand = player.getItemInHand(HandTypes.OFF_HAND).orElse(null);
+        if ((mainHand != null && mainHand.getType().equals(itemType)) || (offHand != null && offHand.getType().equals(itemType))) {
+            return true;
+        }
+
+        return false;
+    }
+
+    public boolean hasItemInOneHand(Player player) {
+        ItemStack mainHand = player.getItemInHand(HandTypes.MAIN_HAND).orElse(null);
+        ItemStack offHand = player.getItemInHand(HandTypes.OFF_HAND).orElse(null);
+        if (mainHand != null || offHand != null) {
+            return true;
+        }
+
+        return false;
+    }
+
+    public ClaimType getClaimTypeFromShovel(ShovelType shovelMode) {
+        if (shovelMode == ShovelTypes.ADMIN) {
+            return ClaimTypes.ADMIN;
+        }
+        if (shovelMode == ShovelTypes.SUBDIVISION) {
+            return ClaimTypes.SUBDIVISION;
+        }
+        if (shovelMode == ShovelTypes.TOWN) {
+            return ClaimTypes.TOWN;
+        }
+        return ClaimTypes.BASIC;
+    }
+
+    public Component getClaimTypeComponentFromShovel(ShovelType shovelMode) {
+        return getClaimTypeComponentFromShovel(shovelMode, true);
+    }
+
+    public Component getClaimTypeComponentFromShovel(ShovelType shovelMode, boolean upper) {
+        if (shovelMode == ShovelTypes.ADMIN) {
+            if (upper) {
+                return TextComponent.of(ClaimTypes.ADMIN.getName().toUpperCase(), TextColor.RED);
+            }
+            return TextComponent.of("Admin", TextColor.RED);
+        }
+
+        if (shovelMode == ShovelTypes.TOWN) {
+            if (upper) {
+                return TextComponent.of(ClaimTypes.TOWN.getName().toUpperCase(), TextColor.GREEN);
+            }
+            return TextComponent.of("Town", TextColor.GREEN);
+        }
+
+        if (shovelMode == ShovelTypes.SUBDIVISION) {
+            if (upper) {
+                return TextComponent.of(ClaimTypes.SUBDIVISION.getName().toUpperCase(), TextColor.AQUA);
+            }
+            return TextComponent.of("Subdivision", TextColor.AQUA);
+        }
+        if (upper) {
+            return TextComponent.of(ClaimTypes.BASIC.getName().toUpperCase(), TextColor.YELLOW);
+        }
+        return TextComponent.of("Basic", TextColor.YELLOW);
+    }
+
+    public GDClaimVisualType getVisualTypeFromShovel(ShovelType shovelMode) {
+        if (shovelMode == ShovelTypes.ADMIN) {
+            return ClaimVisual.ADMIN;
+        }
+        if (shovelMode == ShovelTypes.SUBDIVISION) {
+            return ClaimVisual.SUBDIVISION;
+        }
+        if (shovelMode == ShovelTypes.TOWN) {
+            return ClaimVisual.TOWN;
+        }
+        return ClaimVisual.BASIC;
+    }
+
+    @Nullable
+    public String getUserName(UUID uuid) {
+        if (uuid.equals(GriefDefenderPlugin.PUBLIC_UUID)) {
+            return "public";
+        }
+        if (uuid.equals(GriefDefenderPlugin.ADMIN_USER_UUID) || uuid.equals(GriefDefenderPlugin.WORLD_USER_UUID)) {
+            return "administrator";
+        }
+
+        final GDPermissionUser user = PermissionHolderCache.getInstance().getOrCreateUser(uuid);
+        if (user == null) {
+            return "unknown";
+        }
+
+        return user.getName();
+    }
+
+    public String getPlayerName(String uuid) {
+        if (uuid.equals(GriefDefenderPlugin.WORLD_USER_UUID.toString())) {
+            return "administrator";
+        }
+        if (uuid.equals(GriefDefenderPlugin.ADMIN_USER_UUID.toString()) || uuid.equals(GriefDefenderPlugin.WORLD_USER_UUID.toString())) {
+            return "administrator";
+        }
+
+        Optional<User> user = Sponge.getGame().getServiceManager().provide(UserStorageService.class).get().get(UUID.fromString(uuid));
+        if (!user.isPresent()) {
+            return "unknown";
+        }
+
+        return user.get().getName();
+    }
+
+    public int getEyeHeight(Player player) {
+        return player.getProperty(EyeLocationProperty.class).get().getValue().getFloorY();
+    }
+
+    public int getVisualClaimHeight(GDPlayerData playerData, int height) {
+        if (playerData.getClaimCreateMode() == CreateModeTypes.VOLUME) {
+            return height;
+        }
+        if (playerData.getMaxClaimLevel() < 255) {
+            return playerData.getMaxClaimLevel();
+        }
+        return 0;
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/util/SpongeContexts.java b/sponge/src/main/java/com/griefdefender/util/SpongeContexts.java
new file mode 100644
index 0000000..aed2293
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/util/SpongeContexts.java
@@ -0,0 +1,30 @@
+package com.griefdefender.util;
+
+import com.griefdefender.api.claim.ClaimType;
+import org.spongepowered.api.service.context.Context;
+
+public class SpongeContexts {
+
+    public static final Context GLOBAL_DEFAULT_CONTEXT = new Context("gd_claim_default", "global");
+    public static final Context ADMIN_DEFAULT_CONTEXT = new Context("gd_claim_default", "admin");
+    public static final Context BASIC_DEFAULT_CONTEXT = new Context("gd_claim_default", "basic");
+    public static final Context SUBDIVISION_DEFAULT_CONTEXT = new Context("gd_claim_default", "subdivision");
+    public static final Context TOWN_DEFAULT_CONTEXT = new Context("gd_claim_default", "town");
+    public static final Context WILDERNESS_DEFAULT_CONTEXT = new Context("gd_claim_default", "wilderness");
+    public static final Context WORLD_DEFAULT_CONTEXT = new Context("gd_claim_default", "world");
+
+    /**
+     * Override contexts are used to force a permission to a {@link ClaimType}.
+     */
+    public static final Context GLOBAL_OVERRIDE_CONTEXT = new Context("gd_claim_override", "global");
+    public static final Context ADMIN_OVERRIDE_CONTEXT = new Context("gd_claim_override", "admin");
+    public static final Context BASIC_OVERRIDE_CONTEXT = new Context("gd_claim_override", "basic");
+    /**
+     * Used to override a single claim only.
+     */
+    //public static final Context CLAIM_OVERRIDE_CONTEXT = new Context("gd_claim_override", "CLAIM");
+    public static final Context SUBDIVISION_OVERRIDE_CONTEXT = new Context("gd_claim_override", "subdivision");
+    public static final Context TOWN_OVERRIDE_CONTEXT = new Context("gd_claim_override", "tonw");
+    public static final Context WILDERNESS_OVERRIDE_CONTEXT = new Context("gd_claim_override", "wilderness");
+    public static final Context WORLD_OVERRIDE_CONTEXT = new Context("gd_claim_override", "world");
+}
diff --git a/sponge/src/main/java/com/griefdefender/util/SpongeUtil.java b/sponge/src/main/java/com/griefdefender/util/SpongeUtil.java
new file mode 100644
index 0000000..5af2f5e
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/util/SpongeUtil.java
@@ -0,0 +1,106 @@
+package com.griefdefender.util;
+
+import com.griefdefender.api.Tristate;
+import me.lucko.luckperms.api.context.ContextSet;
+import net.kyori.text.Component;
+import net.kyori.text.serializer.gson.GsonComponentSerializer;
+import org.spongepowered.api.service.context.Context;
+import org.spongepowered.api.service.permission.Subject;
+import org.spongepowered.api.text.Text;
+import org.spongepowered.api.text.chat.ChatType;
+import org.spongepowered.api.text.chat.ChatTypes;
+import org.spongepowered.api.text.serializer.TextSerializers;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class SpongeUtil {
+
+    public static org.spongepowered.api.service.context.Context getSpongeContext(com.griefdefender.api.permission.Context context) {
+        return new org.spongepowered.api.service.context.Context(context.getKey(), context.getValue());
+    }
+
+    public static org.spongepowered.api.service.context.Context getSpongeContext(ContextSet context) {
+        Context spongeContext = null;
+        for (Map.Entry<String, String> mapEntry : context) {
+            spongeContext = new Context(mapEntry.getKey(), mapEntry.getValue());
+            break;
+        }
+
+        return spongeContext;
+    }
+
+    public static Text getSpongeText(Component component) {
+        if (component == null) {
+            return Text.EMPTY;
+        }
+        return TextSerializers.JSON.deserialize(GsonComponentSerializer.INSTANCE.serialize(component));
+    }
+
+    public static Set<Context> getSpongeContexts(com.griefdefender.api.permission.Context context) {
+        final Set<Context> spongeContexts = new HashSet<>();
+        spongeContexts.add(new Context(context.getKey(), context.getValue()));
+        return spongeContexts;
+    }
+
+    public static Set<Context> getSpongeContexts(Set<com.griefdefender.api.permission.Context> contexts) {
+        final Set<Context> spongeContexts = new HashSet<>();
+        for (com.griefdefender.api.permission.Context gpContext : contexts) {
+            spongeContexts.add(new Context(gpContext.getKey(), gpContext.getValue()));
+        }
+
+        return spongeContexts;
+    }
+
+    public static Set<Context> getSpongeContexts(ContextSet contexts) {
+        final Set<Context> spongeContexts = new HashSet<>();
+        for (Map.Entry<String, String> mapEntry : contexts) {
+            spongeContexts.add(new Context(mapEntry.getKey(), mapEntry.getValue()));
+        }
+
+        return spongeContexts;
+    }
+
+    public static Set<com.griefdefender.api.permission.Context> fromSpongeContexts(Set<Context> contexts) {
+        final Set<com.griefdefender.api.permission.Context> gpContexts = new HashSet<>();
+        for (Context spongeContext : contexts) {
+            gpContexts.add(new com.griefdefender.api.permission.Context(spongeContext.getKey(), spongeContext.getValue()));
+        }
+        return gpContexts;
+    }
+
+    public static Component fromSpongeText(Text text) {
+        return GsonComponentSerializer.INSTANCE.deserialize(TextSerializers.JSON.serialize(text));
+    }
+
+    public static Tristate fromSpongeTristate(org.spongepowered.api.util.Tristate value) {
+        if (value == org.spongepowered.api.util.Tristate.UNDEFINED) {
+            return Tristate.UNDEFINED;
+        }
+
+        return Tristate.fromBoolean(value.asBoolean());
+    }
+
+    public static org.spongepowered.api.util.Tristate getSpongeTristate(Tristate value) {
+        if (value == Tristate.UNDEFINED) {
+            return org.spongepowered.api.util.Tristate.UNDEFINED;
+        }
+
+        return org.spongepowered.api.util.Tristate.fromBoolean(value.asBoolean());
+    }
+
+    public static ChatType getSpongeChatType(com.griefdefender.api.ChatType chatType) {
+        switch (chatType.getName()) {
+            case "CHAT" :
+                return ChatTypes.CHAT;
+            case "ACTION_BAR" :
+                return ChatTypes.ACTION_BAR;
+        }
+        return ChatTypes.SYSTEM;
+    }
+
+    public static Tristate getPermissionValue(Subject subject, ContextSet contexts, String permission) {
+        return fromSpongeTristate(subject.getPermissionValue(getSpongeContexts(contexts), permission));
+    }
+}
diff --git a/sponge/src/main/java/com/griefdefender/util/TaskUtil.java b/sponge/src/main/java/com/griefdefender/util/TaskUtil.java
new file mode 100644
index 0000000..918417e
--- /dev/null
+++ b/sponge/src/main/java/com/griefdefender/util/TaskUtil.java
@@ -0,0 +1,58 @@
+/*
+ * This file is part of GriefDefender, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) bloodmc
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.griefdefender.util;
+
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+
+public class TaskUtil {
+
+    public static long computeDelay(int targetHour, int targetMin, int targetSec) 
+    {
+        LocalDateTime localNow = LocalDateTime.now();
+        ZoneId currentZone = ZoneId.systemDefault();
+        ZonedDateTime zonedNow = ZonedDateTime.of(localNow, currentZone);
+        ZonedDateTime zonedNextTarget = zonedNow.withHour(targetHour).withMinute(targetMin).withSecond(targetSec);
+        if(zonedNow.compareTo(zonedNextTarget) > 0) {
+            zonedNextTarget = zonedNextTarget.plusDays(1);
+        }
+
+        Duration duration = Duration.between(zonedNow, zonedNextTarget);
+        return duration.getSeconds();
+    }
+
+    public static ZonedDateTime getNextTargetZoneDate(int targetHour, int targetMin, int targetSec) {
+        LocalDateTime localNow = LocalDateTime.now();
+        ZoneId currentZone = ZoneId.systemDefault();
+        ZonedDateTime zonedNow = ZonedDateTime.of(localNow, currentZone);
+        ZonedDateTime zonedNextTarget = zonedNow.withHour(targetHour).withMinute(targetMin).withSecond(targetSec);
+        if(zonedNow.compareTo(zonedNextTarget) > 0) {
+            zonedNextTarget = zonedNextTarget.plusDays(1);
+        }
+        return zonedNextTarget;
+    }
+}
diff --git a/sponge/src/main/resources/1.12.2.json b/sponge/src/main/resources/1.12.2.json
new file mode 100644
index 0000000..a37ddd1
--- /dev/null
+++ b/sponge/src/main/resources/1.12.2.json
@@ -0,0 +1,250 @@
+{
+    "version": "1.12.2",
+    "libraries": [
+        {
+            "name": "com.griefdefender:api:1.0.0",
+            "sha1": "6a52235cf4323e29572c1d45cdc56373404448b4",
+            "path": "com/griefdefender/api/1.0.0-SNAPSHOT/api-1.0.0-20190906.173641-10.jar",
+            "url": "https://repo.glaremasters.me/repository/bloodshot/com/griefdefender/api/1.0.0-SNAPSHOT/api-1.0.0-20190906.173641-10.jar"
+        },
+        {
+            "name": "com.griefdefender:reflect-helper:1.0",
+            "sha1": "7a50bffa9f0062ac4ca376d95a0e6599aa5f3257",
+            "path": "com/griefdefender/reflect-helper/1.0/reflect-helper-1.0.jar",
+            "url": "https://repo.glaremasters.me/repository/bloodshot/com/griefdefender/reflect-helper/1.0/reflect-helper-1.0.jar"
+        },
+        {
+            "name": "com.googlecode.json-simple:json-simple:1.1.1",
+            "sha1": "c9ad4a0850ab676c5c64461a05ca524cdfff59f1",
+            "path": "com/googlecode/json-simple/json-simple/1.1.1/json-simple-1.1.1.jar",
+            "url": "https://repo1.maven.org/maven2/com/googlecode/json-simple/json-simple/1.1.1/json-simple-1.1.1.jar"
+        },
+        {
+            "name": "org.ow2.asm:asm-debug-all:5.2",
+            "sha1": "3354e11e2b34215f06dab629ab88e06aca477c19",
+            "path": "org/ow2/asm/asm-debug-all/5.2/asm-debug-all-5.2.jar",
+            "relocate": "org.ow2.asm:asm",
+            "url": "https://repo1.maven.org/maven2/org/ow2/asm/asm-debug-all/5.2/asm-debug-all-5.2.jar"
+        },
+        {
+            "name": "me.lucko:jar-relocator:1.3",
+            "sha1": "90c6b66b8535f2f5eefc108e34e50f3e02e1f4cf",
+            "path": "me/lucko/jar-relocator/1.3/jar-relocator-1.3.jar",
+            "relocate": "me.lucko:lucko",
+            "url": "https://repo1.maven.org/maven2/me/lucko/jar-relocator/1.3/jar-relocator-1.3.jar"
+        },
+        {
+            "name": "co.aikar:acf-core:0.5.0-SNAPSHOT",
+            "sha1": "72aff420c92e4cb381ba8bccceb0b37cba7a9496",
+            "path": "co/aikar/acf-core/0.5.0-SNAPSHOT/acf-core-0.5.0-20190722.233344-152.jar",
+            "url": "https://repo.glaremasters.me/repository/public/co/aikar/acf-core/0.5.0-SNAPSHOT/acf-core-0.5.0-20190722.233344-152.jar"
+        },
+        {
+            "name": "co.aikar:acf-sponge:0.5.0-SNAPSHOT",
+            "sha1": "c73413c215cfad8f0c2306ed156df74ebdf18d47",
+            "path": "co/aikar/acf-sponge/0.5.0-SNAPSHOT/acf-sponge-0.5.0-20190722.233405-145.jar",
+            "url": "https://repo.glaremasters.me/repository/public/co/aikar/acf-sponge/0.5.0-SNAPSHOT/acf-sponge-0.5.0-20190722.233405-145.jar"
+        },
+        {
+            "name": "co.aikar:locales:1.0-SNAPSHOT",
+            "sha1": "09c89ff1a611600186edf8482d1059544875582b",
+            "path": "co/aikar/locales/1.0-SNAPSHOT/locales-1.0-20181221.115311-17.jar",
+            "url": "https://repo.glaremasters.me/repository/public/co/aikar/locales/1.0-SNAPSHOT/locales-1.0-20181221.115311-17.jar"
+        },
+        {
+            "name": "co.aikar:minecraft-timings:1.0.4",
+            "sha1": "7ed9d44840cd2c0f77b7c5276d60ca901b146332",
+            "path": "co/aikar/minecraft-timings/1.0.4/minecraft-timings-1.0.4.jar",
+            "url": "https://repo.glaremasters.me/repository/public/co/aikar/minecraft-timings/1.0.4/minecraft-timings-1.0.4.jar"
+        },
+        {
+            "name": "co.aikar:Table:1.0.0-SNAPSHOT",
+            "sha1": "ccbfaea11c65e6d7173226d318c577c439673b3a",
+            "path": "co/aikar/Table/1.0.0-SNAPSHOT/Table-1.0.0-20180331.054128-7.jar",
+            "url": "https://repo.glaremasters.me/repository/public/co/aikar/Table/1.0.0-SNAPSHOT/Table-1.0.0-20180331.054128-7.jar"
+        },
+        {
+            "name": "net.jodah:expiringmap:0.5.9",
+            "sha1": "b93ac8a915e38beadc20c3cc284506e15478fd7b",
+            "path": "net/jodah/expiringmap/0.5.9/expiringmap-0.5.9.jar",
+            "relocate": "net.jodah:jodah",
+            "url": "https://repo1.maven.org/maven2/net/jodah/expiringmap/0.5.9/expiringmap-0.5.9.jar"
+        },
+        {
+            "name": "aopalliance:aopalliance:1.0",
+            "sha1": "0235ba8b489512805ac13a8f9ea77a1ca5ebe3e8",
+            "path": "aopalliance/aopalliance/1.0/aopalliance-1.0.jar",
+            "relocate": "org.aopalliance:aopalliance",
+            "url": "https://repo1.maven.org/maven2/aopalliance/aopalliance/1.0/aopalliance-1.0.jar"
+        },
+        {
+            "name": "com.flowpowered:flow-math:1.0.3",
+            "sha1": "d98020239e5015091ad3be927cef9dea0d61a234",
+            "path": "com/flowpowered/flow-math/1.0.3/flow-math-1.0.3.jar",
+            "url": "https://repo1.maven.org/maven2/com/flowpowered/flow-math/1.0.3/flow-math-1.0.3.jar"
+        },
+        {
+            "name": "com.google.inject:guice:4.1.0",
+            "sha1": "eeb69005da379a10071aa4948c48d89250febb07",
+            "path": "com/google/inject/guice/4.1.0/guice-4.1.0.jar",
+            "url": "https://repo1.maven.org/maven2/com/google/inject/guice/4.1.0/guice-4.1.0.jar"
+        },
+        {
+            "name": "javax.inject:javax.inject:1",
+            "sha1": "6975da39a7040257bd51d21a231b76c915872d38",
+            "path": "javax/inject/javax.inject/1/javax.inject-1.jar",
+            "url": "https://repo1.maven.org/maven2/javax/inject/javax.inject/1/javax.inject-1.jar"
+        },
+        {
+            "name": "com.squareup.okhttp3:okhttp:3.14.2",
+            "sha1": "eaed79ed6bc1e14fad462172b6a09524545b165c",
+            "path": "com/squareup/okhttp3/okhttp/3.14.2/okhttp-3.14.2.jar",
+            "relocate": "okhttp3:okhttp3",
+            "url": "https://repo1.maven.org/maven2/com/squareup/okhttp3/okhttp/3.14.2/okhttp-3.14.2.jar"
+        },
+        {
+            "name": "com.squareup.okio:okio:2.2.2",
+            "sha1": "36f483536153f15339a8b48d508e22be7c9c531a",
+            "path": "com/squareup/okio/okio/2.2.2/okio-2.2.2.jar",
+            "relocate": "okio:okio",
+            "url": "https://repo1.maven.org/maven2/com/squareup/okio/okio/2.2.2/okio-2.2.2.jar"
+        },
+        {
+            "name": "me.lucko.luckperms:luckperms-api:4.4",
+            "sha1": "e0356ab83e426ff5e51b3c596ffac8750905af64",
+            "path": "me/lucko/luckperms/luckperms-api/4.4/luckperms-api-4.4.jar",
+            "relocate": "me.lucko:lucko",
+            "url": "https://repo1.maven.org/maven2/me/lucko/luckperms/luckperms-api/4.4/luckperms-api-4.4.jar"
+        },
+        {
+            "name": "com.github.ben-manes.caffeine:caffeine:2.7.0",
+            "sha1": "c3af06be4a7d4e769fce2cef5e77d3becad9818a",
+            "path": "com/github/ben-manes/caffeine/caffeine/2.7.0/caffeine-2.7.0.jar",
+            "relocate": "com.github.benmanes.caffeine:caffeine",
+            "url": "https://repo1.maven.org/maven2/com/github/ben-manes/caffeine/caffeine/2.7.0/caffeine-2.7.0.jar"
+        },
+        {
+            "name": "commons-io:commons-io:2.6",
+            "sha1": "815893df5f31da2ece4040fe0a12fd44b577afaf",
+            "path": "org/apache/commons/commons-io/2.6/commons-io-2.6.jar",
+            "relocate": "org.apache.commons.io:commonsio",
+            "url": "https://repo1.maven.org/maven2/commons-io/commons-io/2.6/commons-io-2.6.jar"
+        },
+        {
+            "name": "org.apache.commons:commons-lang3:3.9",
+            "sha1": "0122c7cee69b53ed4a7681c03d4ee4c0e2765da5",
+            "path": "org/apache/commons/commons-lang3/3.9/commons-lang3-3.9.jar",
+            "relocate": "org.apache.commons.lang3:commonslang3",
+            "url": "https://repo1.maven.org/maven2/org/apache/commons/commons-lang3/3.9/commons-lang3-3.9.jar"
+        },
+        {
+            "name": "org.spongepowered:configurate-core:3.7-SNAPSHOT",
+            "sha1": "e596c439ac71fa2ea5c48f8ba97a7dc6f4c77b16",
+            "path": "org/spongepowered/configurate-core/3.7-SNAPSHOT/configurate-core-3.7-20190531.182437-11.jar",
+            "relocate": "ninja.leaping.configurate:configurate",
+            "url": "https://repo.spongepowered.org/maven/org/spongepowered/configurate-core/3.7-SNAPSHOT/configurate-core-3.7-20190531.182437-11.jar"
+        },
+        {
+            "name": "org.spongepowered:configurate-gson:3.7-SNAPSHOT",
+            "sha1": "265a94f16583621f497eeecc356f35f983484dde",
+            "path": "org/spongepowered/configurate-gson/3.7-SNAPSHOT/configurate-gson-3.7-20190531.182438-11.jar",
+            "relocate": "ninja.leaping.configurate:configurate",
+            "url": "https://repo.spongepowered.org/maven/org/spongepowered/configurate-gson/3.7-SNAPSHOT/configurate-gson-3.7-20190531.182438-11.jar"
+        },
+        {
+            "name": "org.spongepowered:configurate-hocon:3.7-SNAPSHOT",
+            "sha1": "af48dcb9e7456f2f81a633f62ae5c55e5215c4af",
+            "path": "org/spongepowered/configurate-hocon/3.7-SNAPSHOT/configurate-hocon-3.7-20190531.182439-11.jar",
+            "relocate": "ninja.leaping.configurate:configurate",
+            "url": "https://repo.spongepowered.org/maven/org/spongepowered/configurate-hocon/3.7-SNAPSHOT/configurate-hocon-3.7-20190531.182439-11.jar"
+        },
+        {
+            "name": "org.spongepowered:configurate-yaml:3.7-SNAPSHOT",
+            "sha1": "c66110f5ae0098c450e048f78b322590d2e24d06",
+            "path": "org/spongepowered/configurate-yaml/3.7-SNAPSHOT/configurate-yaml-3.7-20190531.182442-11.jar",
+            "relocate": "ninja.leaping.configurate:configurate",
+            "url": "https://repo.spongepowered.org/maven/org/spongepowered/configurate-yaml/3.7-SNAPSHOT/configurate-yaml-3.7-20190531.182442-11.jar"
+        },
+        {
+            "name": "com.typesafe:config:1.3.1",
+            "sha1": "2cf7a6cc79732e3bdf1647d7404279900ca63eb0",
+            "path": "com/typesafe/config/1.3.1/config-1.3.1.jar",
+            "relocate": "com.typesafe:typesafe",
+            "url": "https://repo1.maven.org/maven2/com/typesafe/config/1.3.1/config-1.3.1.jar"
+        },
+        {
+            "name": "it.unimi.dsi:fastutil:8.2.3",
+            "sha1": "f3a26db2204f1779c9958a914422f84284e53a84",
+            "path": "it/unimi/dsi/fastutil/8.2.3/fastutil-8.2.3.jar",
+            "relocate": "it.unimi.dsi:fastutil",
+            "url": "https://repo1.maven.org/maven2/it/unimi/dsi/fastutil/8.2.3/fastutil-8.2.3.jar"
+        },
+        {
+            "name": "org.jetbrains.kotlin:kotlin-stdlib:1.3.31",
+            "sha1": "11289d20fd95ae219333f3456072be9f081c30cc",
+            "path": "org/jetbrains/kotlin/kotlin-stdlib/1.3.31/kotlin-stdlib-1.3.31.jar",
+            "relocate": "org.jetbrains:jetbrains",
+            "url": "https://repo1.maven.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/1.3.31/kotlin-stdlib-1.3.31.jar"
+        },
+        {
+            "name": "net.kyori:event-api:3.0.0",
+            "sha1": "4e207f07d2adaa15e174a085f65bc6ae5a81029e",
+            "path": "net/kyori/event-api/3.0.0/event-api-3.0.0.jar",
+            "url": "https://repo1.maven.org/maven2/net/kyori/event-api/3.0.0/event-api-3.0.0.jar"
+        },
+        {
+            "name": "net.kyori:event-method:3.0.0",
+            "sha1": "85fe9bbf8ebadde4c82602af29352ba5db06e8e5",
+            "path": "net/kyori/event-method/3.0.0/event-method-3.0.0.jar",
+            "url": "https://repo1.maven.org/maven2/net/kyori/event-method/3.0.0/event-method-3.0.0.jar"
+        },
+        {
+            "name": "net.kyori:event-method-asm:3.0.0",
+            "sha1": "69113430c1ba05c9d9fa6e48028edd53e3e16723",
+            "path": "net/kyori/event-method-asm/3.0.0/event-method-asm-3.0.0.jar",
+            "url": "https://repo1.maven.org/maven2/net/kyori/event-method-asm/3.0.0/event-method-asm-3.0.0.jar"
+        },
+        {
+            "name": "net.kyori:text-adapter-bukkit:3.0.3",
+            "sha1": "37033ab1173d73a62a087cbd5c8d356774f4cee3",
+            "path": "net/kyori/text-adapter-bukkit/3.0.3/text-adapter-bukkit-3.0.3.jar",
+            "url": "https://repo1.maven.org/maven2/net/kyori/text-adapter-bukkit/3.0.3/text-adapter-bukkit-3.0.3.jar"
+        },
+        {
+            "name": "net.kyori:text-adapter-bungeecord:3.0.2",
+            "sha1": "d57c245bdc182bdf37d1b7a32691859add018a2b",
+            "path": "net/kyori/text-adapter-bungeecord/3.0.2/text-adapter-bungeecord-3.0.2.jar",
+            "url": "https://repo1.maven.org/maven2/net/kyori/text-adapter-bungeecord/3.0.2/text-adapter-bungeecord-3.0.2.jar"
+        },
+        {
+            "name": "net.kyori:text-adapter-spongeapi:3.0.2",
+            "sha1": "8562afb1594a9d34b891f23add503741d0656873",
+            "path": "net/kyori/text-adapter-spongeapi/3.0.2/text-adapter-spongeapi-3.0.2.jar",
+            "url": "https://repo1.maven.org/maven2/net/kyori/text-adapter-spongeapi/3.0.2/text-adapter-spongeapi-3.0.2.jar"
+        },
+        {
+            "name": "net.kyori:text-api:3.0.2",
+            "sha1": "608cdb44a74bbd68745941760df730ed55e4b47c",
+            "path": "net/kyori/text-api/3.0.2/text-api-3.0.2.jar",
+            "url": "https://repo1.maven.org/maven2/net/kyori/text-api/3.0.2/text-api-3.0.2.jar"
+        },
+        {
+            "name": "net.kyori:text-serializer-gson:3.0.2",
+            "sha1": "9ac22f04f3504c52ff1618c5a8d9a6145d8d9c9e",
+            "path": "net/kyori/text-serializer-gson/3.0.2/text-serializer-gson-3.0.2.jar",
+            "url": "https://repo1.maven.org/maven2/net/kyori/text-serializer-gson/3.0.2/text-serializer-gson-3.0.2.jar"
+        },
+        {
+            "name": "net.kyori:text-serializer-legacy:3.0.2",
+            "sha1": "8acbfb36356259273a8e3a15782e4f2980375bc5",
+            "path": "net/kyori/text-serializer-legacy/3.0.2/text-serializer-legacy-3.0.2.jar",
+            "url": "https://repo1.maven.org/maven2/net/kyori/text-serializer-legacy/3.0.2/text-serializer-legacy-3.0.2.jar"
+        },
+        {
+            "name": "net.kyori:text-serializer-plain:3.0.2",
+            "sha1": "8d60703f579019f7c26959d2e46501c3d389b48d",
+            "path": "net/kyori/text-serializer-plain/3.0.2/text-serializer-plain-3.0.2.jar",
+            "url": "https://repo1.maven.org/maven2/net/kyori/text-serializer-plain/3.0.2/text-serializer-plain-3.0.2.jar"
+        }
+    ]
+}
\ No newline at end of file
diff --git a/sponge/src/main/resources/LICENSE b/sponge/src/main/resources/LICENSE
new file mode 100644
index 0000000..829a203
--- /dev/null
+++ b/sponge/src/main/resources/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) bloodmc
+Copyright (c) contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/sponge/src/main/resources/META-INF/griefdefender_at.cfg b/sponge/src/main/resources/META-INF/griefdefender_at.cfg
new file mode 100644
index 0000000..0a55de2
--- /dev/null
+++ b/sponge/src/main/resources/META-INF/griefdefender_at.cfg
@@ -0,0 +1,5 @@
+public net.minecraft.world.gen.ChunkProviderServer field_73247_e # chunkLoader
+public net.minecraft.world.gen.ChunkProviderServer field_73244_f # id2ChunkMap
+public net.minecraft.world.gen.ChunkProviderServer field_186029_c # chunkGenerator
+public net.minecraft.server.management.PlayerChunkMapEntry field_187286_f # chunk
+public net.minecraft.server.management.PlayerChunkMapEntry field_187283_c # players
\ No newline at end of file
diff --git a/sponge/src/main/resources/assets/lang/en_US.conf b/sponge/src/main/resources/assets/lang/en_US.conf
new file mode 100644
index 0000000..e2db9c2
--- /dev/null
+++ b/sponge/src/main/resources/assets/lang/en_US.conf
@@ -0,0 +1,637 @@
+GriefDefender {
+    descriptions {
+        abandon-all="Abandons ALL your claims."
+        abandon-claim="Abandons a claim."
+        abandon-top="Abandons top level claim."
+        buy-blocks="Purchases additional claim blocks with server money.\nNote: Requires economy plugin."
+        callback="Execute a callback registered as part of a Text object. Primarily for internal use."
+        claim-bank="Used to withdraw or deposit money for use in claim."
+        claim-clear="Allows clearing of entities within one or more claims."
+        claim-debug="Toggles claim flag debug mode."
+        claim-farewell="Sets the farewell message of your claim."
+        claim-greeting="Sets the greeting message of your claim."
+        claim-ignore="Toggles ignore claims mode."
+        claim-info="Displays all known information for claim you are standing in."
+        claim-inherit="Toggles whether this claim should inherit permissions from parent(s)."
+        claim-list="Lists all known claims in area."
+        claim-name="Sets the claim name."
+        claim-restore="Restores claim to its natural state. Use with caution."
+        claim-setspawn="Sets the claim spawn for players."
+        claim-spawn="Teleports you to claim spawn if available."
+        claim-transfer="Transfers a basic or admin claim to another player."
+        claim-worldedit="Uses the worldedit selection to create a claim."
+        cuboid="Toggles cuboid claims mode."
+        debug="Captures all GD actions for debugging purposes."
+        delete-all="Delete all of another player's claims."
+        delete-all-admin="Deletes all administrative claims."
+        delete-claim="Deletes the claim you're standing in, even if it's not your claim."
+        delete-top="Deletes the claim you're standing in, even if it's not your claim."
+        flag-claim="Gets/Sets claim flags in the claim you are standing in."
+        flag-group="Gets/Sets flag permission for a group in claim you are standing in."
+        flag-player="Gets/Sets flag permission for a player in claim you are standing in."
+        flag-reset="Resets a claim to flag defaults."
+        mode-admin="Switches the claim tool to administrative claims mode"
+        mode-basic="Switches the claim tool back to basic claims mode."
+        mode-nature="Switches the claim tool to restoration mode."
+        mode-subdivision="Switches the claim tool to subdivision mode, used to subdivide your claims."
+        mode-town="Switches the claim tool to town claims mode."
+        option-claim="Gets/Sets claim options in the claim you are standing in."
+        permission-group="Sets a permission on a group with a claim context."
+        permission-player="Sets a permission on a player with a claim context."
+        player-adjust-bonus-blocks="Updates a player's bonus claim block total."
+        player-info="Shows information about a player."
+        player-set-accrued-blocks="Updates a player's accrued claim block total."
+        reload="Reloads GriefDefender's configuration settings."
+        schematic="Manages claim schematics. Use '/claimschematic create <name>' to create a live backup of claim."
+        sell-blocks="Sell your claim blocks for server money.\nNote: Requires economy plugin."
+        sell-claim="Puts your claim up for sale. Use /claimsell amount.\nNote: Requires economy plugin."
+        town-chat="Toggles town chat."
+        town-tag="Sets the tag of your town."
+        trust-group="Grants a group access to your claim.\nAccessor: access to interact with all blocks except inventory.\nContainer: access to interact with all blocks including inventory.\nBuilder: access to everything above including ability to place and break blocks.\nManager: access to everything above including ability to manage claim settings."
+        trust-group-all="Grants a group access to ALL your claim(s).\nAccessor: access to interact with all blocks except inventory.\nContainer: access to interact with all blocks including inventory.\nBuilder: access to everything above including ability to place and break blocks.\nManager: access to everything above including ability to manage claim settings."
+        trust-player="Grants a player access to your claim.\nAccessor: access to interact with all blocks except inventory.\nContainer: access to interact with all blocks including inventory.\nBuilder: access to everything above including ability to place and break blocks.\nManager: access to everything above including ability to manage claim settings."
+        trust-player-all="Grants a player access to ALL your claim(s).\nAccessor: access to interact with all blocks except inventory.\nContainer: access to interact with all blocks including inventory.\nBuilder: access to everything above including ability to place and break blocks.\nManager: access to everything above including ability to manage claim settings."
+        untrust-group="Revokes group access to your claim."
+        untrust-group-all="Revokes group access to all your claims."
+        untrust-player="Revokes player access to your claim."
+        untrust-player-all="Revokes player access to all your claims."
+        version="Displays GriefDefender's version information."
+    }
+    messages {
+        abandon-all-delay-warning="&aThese claims were recently created and cannot be abandoned."
+        abandon-all-warning="&6Are you sure you want to abandon &cALL&6 your claims?"
+        abandon-claim-delay-warning="&aThis claim was recently created and cannot be abandoned until &6{date}&a."
+        abandon-claim-failed="&aCould not abandon claim. Claim result was &f{result}&a."
+        abandon-claim-missing="&cNo claim found. Stand in the claim you want to abandon, or consider &f/abandonall&c."
+        abandon-other-success="&6{player}&a's claim has been abandoned. &6{player}&a now has &6{amount}&a available claim blocks."
+        abandon-success="&aClaim abandoned. You now have &6{amount}&a available claim blocks."
+        abandon-top-level="&cThis claim cannot be abandoned as it contains one or more child claims. In order to abandon a claim with child claims, you must use &f/abandontop&c instead."
+        abandon-town-children="&cYou do not have permission to abandon a town with child claims you do not own. Use &f/ignoreclaims&c or have the child claim owner abandon their claim first. If you just want to abandon the town without affecting children then use &f/abandon&c instead."
+        abandon-warning="&6Are you sure you want to abandon this claim? It will no longer be protected from grief."
+        adjust-accrued-blocks-success="&aAdjusted &6{player}&a's accrued claim blocks by &6{amount}&a. New total accrued blocks: &6{total}&a."
+        adjust-bonus-blocks-success="&aAdjusted &6{player}&a's bonus claim blocks by &6{amount}&a. New total bonus blocks: &6{total}&a."
+        bank-click-view-transactions="Click here to view bank transactions"
+        bank-deposit="&aSuccessful deposit of &6{amount}&a into bank."
+        bank-deposit-no-funds="&cYou do not have enough funds to deposit into the bank."
+        bank-info="&aBalance: &6{balance}&a \nTax: &6{tax-amount}&f due in &7{time-remaining}&a \nTax Owed: &6{tax-balance}."
+        bank-no-permission="&cYou don't have permission to manage &6{player}&c's claim bank."
+        bank-tax-system-disabled="&cThe bank/tax system is not enabled. If you want it enabled, set 'bank-tax-system' to true in config."
+        bank-title-transactions="Bank Transactions"
+        bank-withdraw="&aSuccessful withdraw of &6{amount} &afrom bank."
+        bank-withdraw-no-funds="&cThe claim bank has a remaining balance of &a{balance}&c and does not have enough funds to withdraw &a{amount}&c."
+        block-claimed="&aThat block has been claimed by &6{player}&a."
+        block-not-claimed="&cNo one has claimed this block."
+        block-sale-value="&aEach claim block is worth &6{amount}&a. You have &6{total}&a available for sale."
+        claim-above-level="&cUnable to claim block as it is above your maximum claim level limit of &a{limit}&c."
+        claim-action-not-available="&cThis action is not available in {type}&c."
+        claim-automatic-notification="&cThis chest and nearby blocks are protected."
+        claim-below-level="&cUnable to claim block as it is below your minimum claim level limit of &a{limit}&c."
+        claim-chest-confirmation="&cThis chest is protected."
+        claim-chest-outside-level="&cThis chest can't be protected as the position is outside your claim level limits of &a{min-level}&c and &a{max-level}&c."
+        claim-children-warning="&6This claim includes child claims.  If you're sure you want to delete it, use &f/deleteclaim&6 again."
+        claim-context-not-found="&cContext &f{context}&c was not found."
+        claim-disabled-world="&cClaims are disabled in this world."
+        claim-expired-inactivity="&6{player}'s &cclaim with id &f{uuid}&c has expired and has been removed due to inactivity."
+        claim-farewell="&aSet claim farewell to {farewell}&a."
+        claim-farewell-clear="&aThe claim farewell message has been cleared."
+        claim-farewell-invalid="&cClaim flag &f{flag}&c is invalid."
+        claim-greeting="&aSet claim greeting to {greeting}&a."
+        claim-greeting-clear="&aThe claim greeting message has been cleared."
+        claim-ignore="&aNow ignoring claims."
+        claim-last-active="&aClaim last active &6{date}&a."
+        claim-mode-start="{type}&a corner set! Right click again at the opposite corner to claim a rectangle of land. To cancel, type &f/claim&a to exit claim mode."
+        claim-name="&aSet claim name to {name}&a."
+        claim-no-claims="&cYou don't have any land claims."
+        claim-no-set-home="&cYou must be trusted in order to use /sethome here."
+        claim-not-found="&cThere's no claim here."
+        claim-not-yours="&cThis isn't your claim."
+        claim-owner-already="&cYou are already the claim owner."
+        claim-owner-only="&cOnly &6{player}&c can modify this claim."
+        claim-protected-entity="&cThat belongs to &6{player}&c."
+        claim-respecting="&aNow respecting claims."
+        claim-restore-success="&aSuccessfully restored claim."
+        claim-show-nearby="&aFound &6{amount}&a nearby claims."
+        claim-size-max="&cThe claim &6{axis}&c size of &a{size}&c exceeds the max size of &a{max-size}&c.\nThe area needs to be a minimum of &a{min-area}&c and a max of &a{max-area}"
+        claim-size-min="&cThe claim &6{axis}&c size of &a{size}&c is below the min size of &a{min-size}&c.\nThe area needs to be a minimum of &a{min-area}&c and a max of &a{max-area}"
+        claim-size-need-blocks-2d="&cYou don't have enough blocks for this claim size.\nYou need &a{block-amount}&c more blocks."
+        claim-size-need-blocks-3d="&cYou don't have enough blocks for this claim size.\nYou need &a{chunk-amount}&c more chunks. &f({block-amount})"
+        claim-size-too-small="&cThe selected claim size of &a{width}&fx&a{length}&c would be too small. A claim must be at least &a{min-width}&fx&a{min-length}&c in size."
+        claim-start="{type}&a corner set! Use the {item}&a again at the opposite corner to claim a rectangle of land.  To cancel, put your {item}&a away."
+        claim-too-far="&cThat's too far away."
+        claim-transfer-exceeds-limit="&cClaim could not be transferred as it would exceed the new owner's creation limit."
+        claim-transfer-success="&aClaim transferred."
+        claim-type-not-found="&cNo {type}&c claims found."
+        claiminfo-ui-admin-settings="Admin Settings"
+        claiminfo-ui-bank-info="Bank Info"
+        claiminfo-ui-claim-expiration="Claim Expiration"
+        claiminfo-ui-click-admin="Click here to view admin settings"
+        claiminfo-ui-click-bank="Click here to check bank information"
+        claiminfo-ui-click-change-claim="Click here to change claim to {type}"
+        claiminfo-ui-click-toggle="Click here to toggle value"
+        claiminfo-ui-deny-messages="Deny Messages"
+        claiminfo-ui-flag-overrides="Flag Overrides"
+        claiminfo-ui-for-sale="For Sale"
+        claiminfo-ui-inherit-parent="Inherit Parent"
+        claiminfo-ui-last-active="Last Active"
+        claiminfo-ui-north-corners="North Corners"
+        claiminfo-ui-pvp-override="PvP Override"
+        claiminfo-ui-requires-claim-blocks="Requires Claim Blocks"
+        claiminfo-ui-return-bankinfo="Return to bank info"
+        claiminfo-ui-return-claiminfo="Return to claim info"
+        claiminfo-ui-return-settings="Return to standard Settings"
+        claiminfo-ui-size-restrictions="Size Restrictions"
+        claiminfo-ui-south-corners="South Corners"
+        claiminfo-ui-teleport-direction="Click here to teleport to {direction}&f corner of claim"
+        claiminfo-ui-teleport-feature="You do not have permission to use the teleport feature in this claim"
+        claiminfo-ui-teleport-spawn="Click here to teleport to claim spawn"
+        claiminfo-ui-title-claiminfo="Claim Info"
+        claiminfo-ui-town-settings="Town Settings"
+        claimlist-ui-click-info="Click to check more info"
+        claimlist-ui-click-purchase="Click here to purchase claim"
+        claimlist-ui-click-teleport-target="Click here to teleport to {name}&f {target}&f in &6{world}"
+        claimlist-ui-click-toggle-value="Click here to toggle {type} value"
+        claimlist-ui-click-view-children="Click here to view child claim list"
+        claimlist-ui-click-view-claims="Click here to view the claims you own"
+        claimlist-ui-no-claims-found="No claims found in world."
+        claimlist-ui-return-claimlist="Return to claims list"
+        claimlist-ui-title="Claim list"
+        claimlist-ui-title-child-claims="Child Claims"
+        command-blocked="&cThe command &f{command}&c has been blocked by claim owner &6{player}&c."
+        command-claimban-success-block="&aSuccessfully &cBANNED&a block with id {id}&a."
+        command-claimban-success-entity="&aSuccessfully &cBANNED&a entity with id {id}&a."
+        command-claimban-success-item="&aSuccessfully &cBANNED&a item with id {id}&a."
+        command-claimbuy-title="&bClaims for sale"
+        command-claimclear-killed="&cKilled &6{amount}&a entities of type {type}&f."
+        command-claimclear-no-entities="&cCould not locate any entities of type {type}&c."
+        command-claimclear-uuid-deny="&cOnly administrators may clear claims by UUID."
+        command-claimflagdebug-disabled="Claim flags debug &cOFF"
+        command-claimflagdebug-enabled="Claim flags debug &aON"
+        command-claiminfo-not-found="&cNo valid player or claim UUID found."
+        command-claiminfo-uuid-required="&cClaim UUID is required if executing from non-player source."
+        command-claiminherit-disabled="Parent claim inheritance &cOFF"
+        command-claiminherit-enabled="Parent claim inheritance &aON"
+        command-claimmode-disabled="Claim mode &cOFF"
+        command-claimmode-enabled="Claim mode &aON&f\n&aLeft-click to inspect.\n&aRight-click to claim.&b\nNote&f: &aUse &f/claim&a to exit mode."
+        command-claimspawn-not-found="&aCould not locate a claim with name {name}&a."
+        command-claimunban-success-block="&aSuccessfully &6UNBANNED&a block with id {id}&a."
+        command-claimunban-success-entity="&aSuccessfully &6UNBANNED&a entity with id {id}&a."
+        command-claimunban-success-item="&aSuccessfully &6UNBANNED&a item with id {id}&a."
+        command-cuboid-disabled="&aNow claiming in &d2D&a mode."
+        command-cuboid-enabled="&aNow claiming in &d3D&a mode."
+        command-execute-failed="&cFailed to execute command '{command} {args}'"
+        command-giveblocks-confirmation="&6Are you sure you want to give {player}&6 {amount}&6 claim blocks?"
+        command-giveblocks-confirmed="&aClaim block transfer complete."
+        command-giveblocks-not-enough="&cNot enough claim blocks! You only have {amount}&c available claim blocks to transfer.\n&bNote&f: This amount does not include initial claim blocks. It only includes accrued and bonus."
+        command-giveblocks-received="&aYou have been given {amount}&a claim blocks from {player}&a."
+        command-inherit-only-child="&cThis command can only be used in child claims."
+        command-invalid="&cNo valid command entered."
+        command-invalid-amount="&cInvalid amount &6{amount}&c entered."
+        command-invalid-claim="&cThis command cannot be used in {type}&c claims."
+        command-invalid-group="&cGroup &6{group}&c is not valid."
+        command-invalid-player="&cPlayer &6{player}&c is not valid."
+        command-invalid-player-group="&cNot a valid player or group."
+        command-invalid-type="&cInvalid type {type}&c specified."
+        command-not-available-economy="&cThis command is not available while server is in economy mode."
+        command-option-exceeds-admin="&cOption value of &a'{value}&c' exceeds admin set value of '&a{admin-value}&c'. Adjusting to admin value..."
+        command-pet-confirmation="&aPet transferred."
+        command-pet-invalid="&cPet type {type} is not currently supported for transfer."
+        command-pet-transfer-cancel="&aPet giveaway cancelled."
+        command-pet-transfer-ready="&aReady to transfer!  Right-click the pet you'd like to give away, or cancel by left-clicking."
+        command-player-not-found="&cPlayer '&6{player}&c' could not be found."
+        command-world-not-found="&cWorld '&6{world}&c' could not be found."
+        command-worldedit-missing="&cThis command requires WorldEdit to be installed on server."
+        create-cancel="&cThe creation of this claim has been cancelled."
+        create-cuboid-disabled="&cThe creation of &d3D&c cuboid claims has been disabled by an administrator.\nYou can only create &d3D&c claims as an Admin or on a &d2D&c claim that you own."
+        create-failed-claim-limit="&cYou've reached your limit of &a{limit}&c on {type}&c claims. Use &f/abandon&c to remove one before creating another."
+        create-failed-result="&aThe claim failed to be created due to : &6{reason}&a."
+        create-insufficient-blocks-2d="&cYou don't have enough blocks to claim this area.\nYou need &a{block-amount}&c more blocks."
+        create-insufficient-blocks-3d="&cYou don't have enough blocks to claim this area.\nYou need &a{chunk-amount}&c more chunks. &f({block-amount})"
+        create-overlap="&cYou can't create a claim here because it would overlap an existing claim."
+        create-overlap-player="&cYou can't create a claim here because it would overlap &6{player}&c's claim."
+        create-overlap-short="&cYour selected area overlaps an existing claim."
+        create-subdivision-fail="&cNo claim exists at selected corner. Please click a valid block location within parent claim in order to create your subdivision."
+        create-subdivision-only="&cUnable to create claim. Only subdivisions can be created at a single block location."
+        create-success="{type}&a created!  Use &f/trust&a to share it with friends."
+        debug-error-upload="&cError uploading content {content}&c."
+        debug-no-records="&cNo debug records to paste!"
+        debug-paste-success="&aPaste success!"
+        debug-record-end="Record end"
+        debug-record-start="Record start"
+        debug-time-elapsed="Time elapsed"
+        delete-all-player-failed="&aCould not delete all of &6{player}&a's claims. Claim result was &f{result}&a."
+        delete-all-player-success="&aDeleted all of &6{player}&a's claims."
+        delete-all-player-warning="&6Are you sure you want to delete all of {player}&6's claims?"
+        delete-all-type-deny="&cCould not delete all {type}&c claims. A plugin has denied it."
+        delete-all-type-success="&cDeleted all {type}&c claims."
+        delete-all-type-warning="&6Are you sure you want to delete all {type}&6 claims?"
+        delete-claim-failed="&aCould not delete claim. Claim result was &f{result}&a."
+        delete-claim-success="&aDeleted {player}&a's claim."
+        delete-claim-warning="&6Are you sure you want to delete {player}&6's claim?"
+        economy-balance="&aYour new balance is &6{balance}&a."
+        economy-block-available-purchase-2d="&aYou have enough funds to claim up to &6{block-amount} &amore blocks."
+        economy-block-available-purchase-3d="&aYou have enough funds to claim up to &6{chunk-amount} &amore chunks. &f({block-amount})"
+        economy-block-buy-invalid="&cBlock count must be greater than 0."
+        economy-block-buy-sell-disabled="&cSorry, buying and selling claim blocks is disabled."
+        economy-block-cost="&aEach claim block costs &6{amount}&a."
+        economy-block-not-available="&cYou don't have that many claim blocks available for sale."
+        economy-block-only-buy="&cClaim blocks may only be purchased, not sold."
+        economy-block-only-sell="&cClaim blocks may only be sold, not purchased."
+        economy-block-purchase-confirmation="&aWithdrew &6{amount}&a from your account. You now have &6{balance}&a available claim blocks."
+        economy-block-purchase-cost="&aEach claim block costs &6{amount}&a. Your balance is &6{balance}&a."
+        economy-block-purchase-limit="&cThe new claim block total of &a{total}&c will exceed your claim block limit of &a{limit}&c. The transaction has been cancelled."
+        economy-block-sale-confirmation="&aDeposited &6{deposit}&a in your account. You now have &6{amount}&a available claim blocks."
+        economy-block-sell-error="&cCould not sell blocks. Reason: &f{reason}&c."
+        economy-claim-abandon-success="&aClaim(s) abandoned. You have been refunded a total of '&6{amount}&a'."
+        economy-claim-buy-cancelled="&cBuy cancelled! Could not buy claim from &6{player}&c. Result was &a{result}"
+        economy-claim-buy-confirmation="&6Are you sure you want to buy this claim for &a{amount}&6? Click confirm to proceed."
+        economy-claim-buy-confirmed="&aYou have successfully bought the claim for &6{amount}&a."
+        economy-claim-buy-not-enough-funds="&cYou do not have enough funds to purchase this claim for &a{amount}&c. You currently have a balance of &a{balance}&c and need &a{amount_required}&c more for purchase."
+        economy-claim-buy-transfer-cancelled="&cClaim transfer cancelled! Could not transfer owner &6{owner}&c to &6{player}&c. Result was &a{result}"
+        economy-claim-not-for-sale="&cThis claim is not for sale."
+        economy-claim-sale-cancelled="&aYou have cancelled your claim sale."
+        economy-claim-sale-confirmation="&6Are you sure you want to sell your claim for &a{amount}&6 ? If your claim is sold, all items and blocks will be transferred to the buyer. Click confirm if this is OK."
+        economy-claim-sale-confirmed="&aYou have successfully put your claim up for sale for the amount of &6{amount}&a."
+        economy-claim-sale-invalid-price="&cThe sale price of &a{amount}&c must be greater than or equal to &a0&c."
+        economy-claim-sold="&aYour claim sold! The amount of &6{amount}&a has been deposited into your account. Your total available balance is now &6{balance}&a."
+        economy-mode-block-sale-confirmation="&aDeposited &6{deposit}&a in your account. Your total balance is &6{balance}&a. You now have enough funds to claim up to &6{amount}&a more claim blocks."
+        economy-mode-resize-success-2d="&aClaim resized. Your new balance is &6{balance}&a. You have enough funds to claim up to &6{block-amount} &amore blocks."
+        economy-mode-resize-success-3d="&aClaim resized. Your new balance is &6{balance}&a. You have enough funds to claim up to &6{chunk-amount} &amore chunks. &f({block-amount})"
+        economy-not-enough-funds="&cYou do not have enough funds to purchase this land. Your current economy balance is '&a{balance}&c' but you require '&a{amount}&c' to complete the purchase."
+        economy-not-installed="&cEconomy plugin not installed!."
+        economy-player-not-found="&cNo economy account found for player &6{player}&c."
+        economy-remaining-funds="&aYou have &6{amount}&a available for claiming land."
+        economy-virtual-not-supported="&cEconomy plugin does not support virtual accounts which is required. Use another economy plugin or contact plugin dev for virtual account support."
+        economy-withdraw-error="&cCould not withdraw funds. Reason: &f{reason}&c."
+        feature-not-available="&cThis feature is currently being worked on and will be available in a future release."
+        flag-description-block-break="Controls whether a block can be broken.\n&dExample&f : To prevent any source from breaking dirt blocks, enter\n&a/cf block-break minecraft:dirt false\n&bNote&f : minecraft represents the modid and dirt represents the block id.\nSpecifying no modid will always default to minecraft."
+        flag-description-block-grow="Controls whether a block can grow.\n&dExample&f : To prevent a cactus from growing, enter\n&a/cf block-grow minecraft:cactus false\n&bNote&f : minecraft represents the modid and cactus represents the block id.\nSpecifying no modid will always default to minecraft."
+        flag-description-block-modify="Controls whether a block can be modified.\n&dExample&f : To prevent any source from igniting a block, enter\n&a/cf block-modify minecraft:fire false\n&bNote&f : minecraft represents the modid and fire represents the block id.\nSpecifying no modid will always default to minecraft."
+        flag-description-block-place="Controls whether a block can be placed.\n&dExample&f : To prevent any source from placing dirt blocks, enter\n&a/cf block-place minecraft:dirt false\n&bNote&f : minecraft represents the modid and dirt represents the block id.\nSpecifying no modid will always default to minecraft."
+        flag-description-block-spread="Controls whether a block can spread to another block.\n&dExample&f : To prevent fire from spreading, enter\n&a/cf block-spread any false context[source=fire]\n&bNote&f : 'any' represents any target block and fire represents the source block id.\nSpecifying no modid will always default to minecraft."
+        flag-description-collide-block="Controls whether an entity can collide with a block.\n&dExample&f : To prevent entity collisions with stone pressure plates, enter\n&a/cf collide-block minecraft:stone_pressure_plate false\n&bNote&f : minecraft represents the modid and stone_pressure_plate represents the block id.\nSpecifying no modid will always default to minecraft."
+        flag-description-collide-entity="Controls whether an entity can collide with an entity.\n&dExample&f : To prevent entity collisions with item frames, enter\n&a/cf collide-entity minecraft:item_frame false\n&bNote&f : minecraft represents the modid and item_frame represents the entity id.\nSpecifying no modid will always default to minecraft."
+        flag-description-command-execute="Controls whether a command can be executed.\n&dExample&f : To prevent pixelmon's command '/shop select' from being run, enter\n&a/cf command-execute pixelmon:shop[select] false\n&bNote&f : &o&6pixelmon&f represents the modid and &o&6shop&f represents the base command, and &o&6select&f represents the argument.\nSpecifying no modid will always default to minecraft."
+        flag-description-command-execute-pvp="Controls whether a command can be executed while engaged in PvP.\n&dExample&f : To prevent pixelmon's command '/shop select' from being run, enter\n&a/cf command-execute pixelmon:shop[select] false\n&bNote&f : &o&6pixelmon&f represents the modid and &o&6shop&f represents the base command, and &o&6select&f represents the argument.\nSpecifying no modid will always default to minecraft."
+        flag-description-custom-block-break="Controls whether blocks can be broken."
+        flag-description-custom-block-grow="Controls whether blocks can grow."
+        flag-description-custom-block-place="Controls whether blocks can be placed."
+        flag-description-custom-block-spread="Controls whether blocks can spread."
+        flag-description-custom-build="Controls whether actions are allowed against blocks and entities such as mining, placement, and interactions."
+        flag-description-custom-chest-access="Controls whether a player can access chest inventories."
+        flag-description-custom-chorus-fruit-teleport="Controls whether a player can use chorus fruit to teleport."
+        flag-description-custom-crop-growth="Controls whether crops can grow."
+        flag-description-custom-damage-animals="Controls whether animals can be damaged."
+        flag-description-custom-enderman-grief="Controls whether enderman can grief."
+        flag-description-custom-enderpearl="Controls whether enderpearl can be used."
+        flag-description-custom-enter-player="Controls whether a player can enter this claim."
+        flag-description-custom-exit-player="Controls whether a player can exit this claim."
+        flag-description-custom-exp-drop="Controls whether experience orbs can drop."
+        flag-description-custom-explosion-block="Controls whether explosions affect blocks."
+        flag-description-custom-explosion-creeper="Controls whether a creeper can explode."
+        flag-description-custom-explosion-entity="Controls whether explosions affect entities."
+        flag-description-custom-explosion-tnt="Controls whether tnt can explode."
+        flag-description-custom-fall-damage="Controls whether players can take fall damage."
+        flag-description-custom-fire-damage="Controls whether fire can cause damage."
+        flag-description-custom-fire-spread="Controls whether fire can spread."
+        flag-description-custom-grass-growth="Controls whether grass can grow."
+        flag-description-custom-ice-form="Controls whether ice can form."
+        flag-description-custom-ice-melt="Controls whether ice can melt."
+        flag-description-custom-interact-block="Controls whether players can interact with blocks.\n&bNote&f: This does not include inventory blocks such as chests."
+        flag-description-custom-interact-entity="Controls whether players can interact with entities.\n&bNote&f: This does not include chest access with entities such as horses."
+        flag-description-custom-interact-inventory="Controls whether players can interact with inventory."
+        flag-description-custom-invincible="Controls whether players are invincible against damage."
+        flag-description-custom-item-drop="Controls whether players can drop items."
+        flag-description-custom-item-pickup="Controls whether players can pickup items."
+        flag-description-custom-lava-flow="Controls whether lava can flow."
+        flag-description-custom-leaf-decay="Controls whether leaves can decay."
+        flag-description-custom-lighter="Controls whether a player can use flint and steel."
+        flag-description-custom-lightning="Controls whether lightning can cause harm."
+        flag-description-custom-monster-damage="Controls whether monsters can deal damage."
+        flag-description-custom-mushroom-growth="Controls whether mushrooms can grow."
+        flag-description-custom-mycelium-spread="Controls whether mycelium can spread."
+        flag-description-custom-pistons="Controls whether pistons can be used."
+        flag-description-custom-portal-use="Controls whether portals can be used."
+        flag-description-custom-pvp="Controls whether PvP combat is allowed."
+        flag-description-custom-ride="Controls whether vehicles(including animals) can be mounted."
+        flag-description-custom-sleep="Controls whether players can sleep in beds."
+        flag-description-custom-snow-fall="Controls whether snow can fall."
+        flag-description-custom-snow-melt="Controls whether snow can melt."
+        flag-description-custom-snowman-trail="Controls whether snowmen can create snow beneath them."
+        flag-description-custom-soil-dry="Controls whether soil will dry."
+        flag-description-custom-spawn-ambient="Controls whether ambients, such as bats, can spawn."
+        flag-description-custom-spawn-animal="Controls whether animals, such as cows and pigs, can spawn."
+        flag-description-custom-spawn-aquatic="Controls whether aquatics, such as squids and guardians, can spawn."
+        flag-description-custom-spawn-monster="Controls whether monsters, such as creepers and skeletons, can spawn."
+        flag-description-custom-teleport-from="Controls whether players can teleport from this claim."
+        flag-description-custom-teleport-to="Controls whether players can teleport to this claim."
+        flag-description-custom-use="Controls whether players can use non-inventory blocks in this claim."
+        flag-description-custom-vehicle-destroy="Controls whether vehicles can be destroyed."
+        flag-description-custom-vehicle-place="Controls whether vehicles(boats, minecarts, etc.) can be placed."
+        flag-description-custom-vine-growth="Controls whether vines(and kelp) can grow."
+        flag-description-custom-water-flow="Controls whether water can flow."
+        flag-description-custom-wither-damage="Controls whether withers can do damage."
+        flag-description-enter-claim="Controls whether an entity can enter a claim.\n&dExample&f : To prevent players from entering a claim, enter\n&a/cf enter-claim player false\n&bNote&f : If you want to use this for groups, use the /cfg command."
+        flag-description-entity-chunk-spawn="Controls whether a saved entity can be spawned during chunk load.\n&dExample&f : To prevent horses from spawning during chunk load, enter\n&a/cf entity-chunk-spawn minecraft:horse false\n&bNote&f : This will remove &cALL&f saved entities when a chunk loads. If a chunk is already loaded, it will take affect after it reloads. Use with extreme caution."
+        flag-description-entity-damage="Controls whether an entity can be damaged.\n&dExample&f : To prevent animals from being damaged, enter\n&a/cf entity-damage minecraft:animal false."
+        flag-description-entity-riding="Controls whether an entity can be mounted.\n&dExample&f : To prevent horses from being mounted, enter\n&a/cf entity-riding minecraft:horse false."
+        flag-description-entity-spawn="Controls whether an entity can be spawned into the world.\n&dExample&f : To prevent pigs from spawning, enter\n&a/cf entity-spawn minecraft:pig false\n&bNote&f : This does not include entity items. See item-spawn flag"
+        flag-description-entity-teleport-from="Controls whether an entity can teleport from a claim.\n&dExample&f : To prevent players from teleporting from this claim, enter\n&a/cf entity-teleport-from player false\n&bNote&f : If you want to use this for groups, use the /cfg command."
+        flag-description-entity-teleport-to="Controls whether an entity can teleport to a claim.\n&dExample&f : To prevent players from teleporting to this claim, enter\n&a/cf entity-teleport-to player false\n&bNote&f : If you want to use this for groups, use the /cfg command."
+        flag-description-exit-claim="Controls whether an entity can exit a claim.\n&dExample&f : To prevent players from exiting this claim, enter\n&a/cf exit-claim player false\n&bNote&f : If you want to use this for groups, use the /cfg command."
+        flag-description-explosion-block="Controls whether an explosion can damage blocks in a claim.\n&dExample&f : To prevent an explosion from affecting any block, enter\n&a/cf explosion-block any false"
+        flag-description-explosion-entity="Controls whether an explosion can damage entities in a claim.\n&dExample&f : To prevent an explosion from affecting any entity, enter\n&a/cf explosion-entity any false"
+        flag-description-interact-block-primary="Controls whether a player can left-click(attack) a block.\n&dExample&f : To prevent players from left-clicking chests, enter\n&a/cf interact-block-primary minecraft:chest false"
+        flag-description-interact-block-secondary="Controls whether a player can right-click a block.\n&dExample&f : To prevent players from right-clicking(opening) chests, enter\n&a/cf interact-block-secondary minecraft:chest false"
+        flag-description-interact-entity-primary="Controls whether a player can left-click(attack) an entity.\n&dExample&f : To prevent players from left-clicking cows, enter\n&a/cf interact-entity-primary minecraft:cow false"
+        flag-description-interact-entity-secondary="Controls whether a player can right-click on an entity.\n&dExample&f : To prevent players from interacting with villagers, enter\n&a/cf interact-entity-secondary minecraft:villager false"
+        flag-description-interact-inventory="Controls whether a player can right-click(open) a block that contains inventory such as a chest.\n&dExample&f : To prevent players from opening chests, enter\n&a/cf interact-inventory minecraft:chest false"
+        flag-description-interact-inventory-click="Controls whether a player can click on an inventory slot.\n&dExample&f : To prevent players from clicking on an inventory slot that contains a diamond, enter\n&a/cf interact-inventory-click minecraft:diamond false"
+        flag-description-interact-item-primary="Controls whether a player can left-click(attack) with an item.\n&dExample&f : To prevent players from left-clicking with a diamond sword, enter\n&a/cf interact-item-primary minecraft:diamond_sword false"
+        flag-description-interact-item-secondary="Controls whether a player can right-click with an item.\n&dExample&f : To prevent players from right-clicking with flint and steel, enter\n&a/cf interact-item-secondary minecraft:flint_and_steel false"
+        flag-description-item-drop="Controls whether an item can be dropped in a claim.\n&dExample&f : To prevent diamond's from being dropped by players in this claim, enter\n&a/cf item-drop minecraft:flint_and_steel false context[source=player]"
+        flag-description-item-pickup="Controls whether an item can be picked up in a claim.\n&dExample&f : To prevent diamond's from being picked up by players, enter\n&a/cf item-pickup minecraft:diamond false"
+        flag-description-item-spawn="Controls whether an item can be spawned in a claim.\n&dExample&f : To prevent feather's from being spawned in this claim, enter\n&a/cf item-spawn minecraft:feather false"
+        flag-description-item-use="Controls whether an item can be used.\n&dExample&f : To prevent apples from being eaten in this claim, enter\n&a/cf item-use minecraft:apple false"
+        flag-description-leaf-decay="Controls whether leaves can decay in a claim.\n&dExample&f : To prevent leaves from decaying in this claim, enter\n&a/cf leaf-decay any false"
+        flag-description-liquid-flow="Controls whether liquid, such as lava and water, is allowed to flow in a claim.\n&dExample&f : To prevent any type of liquid flow in this claim, enter\n&a/cf liquid-flow any false"
+        flag-description-portal-use="Controls whether a portal can be used.\n&dExample&f : To prevent only players from using portal without affecting non-players, enter\n&a/cf portal-use any false context[source=player]"
+        flag-description-projectile-impact-block="Controls whether a projectile can impact(collide) with a block.\n&dExample&f : To prevent pixelmon pokeball's from impacting blocks, enter\n&a/cf projectile-impact-block any false[source=pixelmon:occupiedpokeball]\n&bNote&f : This involves things such as potions, arrows, throwables, pixelmon pokeballs, etc."
+        flag-description-projectile-impact-entity="Controls whether a projectile can impact(collide) with an entity.\n&dExample&f : To prevent arrows shot by players from impacting entities, enter\n&a/cf projectile-impact-entity minecraft:arrow false[source=player]\n&bNote&f : This involves things such as potions, arrows, throwables, pixelmon pokeballs, etc."
+        flag-invalid-context="&cInvalid context '&f{context}&c' entered for base flag &f{flag}&c."
+        flag-invalid-meta="&cInvalid target meta '&f{value}&c' entered for base flag &f{flag}&c."
+        flag-invalid-target="&cInvalid target '&f{target}&c' entered for base flag &f{flag}&c."
+        flag-not-found="&cFlag {flag}&c not found."
+        flag-not-set="{flag}&f is currently not set.\nThe default claim value of {value}&f will be active until set."
+        flag-overridden="&cFailed to set claim flag. The flag &f{flag}&c has been overridden by an admin."
+        flag-override-not-supported="&cClaim type {type}&c does not support flag overrides."
+        flag-reset-success="&aClaim flags reset to defaults successfully."
+        flag-reset-warning="&6Are you sure you want to reset this claim's flag data back to default?"
+        flag-set-permission-target="&aSet {type}&a permission &b{permission}&a to {value}&a with contexts &7{contexts}&a on &6{target}&a."
+        flag-ui-click-allow="Click here to allow this flag."
+        flag-ui-click-deny="Click here to deny this flag."
+        flag-ui-click-remove="Click here to remove this flag."
+        flag-ui-click-toggle="Click here to toggle {flag}&f value."
+        flag-ui-info-claim="Claim is checked before default values. Allows claim owners to specify flag settings in claim only."
+        flag-ui-info-default="Default is last to be checked. Both claim and override take priority over this."
+        flag-ui-info-inherit="Inherit is an enforced flag set by a parent claim that cannot changed."
+        flag-ui-info-override="Override has highest priority and is checked above both default and claim values. Allows admins to override all basic and admin claims."
+        flag-ui-inherit-parent="This flag is inherited from parent claim {name}&f and &ncannot&f be changed."
+        flag-ui-override-no-permission="This flag has been overridden by an administrator and can &n&cNOT&f be changed."
+        flag-ui-override-permission="{flag}&f is currently being &coverridden&f by an administrator.\nClick here to remove this flag."
+        flag-ui-return-flags="Return to flags"
+        label-accessors=Accessors
+        label-area=Area
+        label-blocks=Blocks
+        label-builders=Builders
+        label-buy=Buy
+        label-cancel=Cancel
+        label-children=children
+        label-confirm=Confirm
+        label-containers=Containers
+        label-context=Context
+        label-created=Created
+        label-displaying=Displaying
+        label-expired=Expired
+        label-farewell=Farewell
+        label-flag=Flag
+        label-greeting=Greeting
+        label-group=Group
+        label-inherit=Inherit
+        label-location=Location
+        label-managers=Managers
+        label-name=Name
+        label-no=No
+        label-output=Output
+        label-owner=Owner
+        label-permission=Permission
+        label-player=Player
+        label-price=Price
+        label-raid=Raid
+        label-resizable=Resizable
+        label-result=Result
+        label-schematic=Schematic
+        label-source=Source
+        label-spawn=Spawn
+        label-target=Target
+        label-trust=Trust
+        label-type=Type
+        label-unknown=Unknown
+        label-user=User
+        label-world=World
+        label-yes=Yes
+        mode-admin="&aAdministrative claims mode active. Any claims created will be free and editable by other administrators."
+        mode-basic="&aBasic claim creation mode enabled."
+        mode-nature="&aReady to restore claim! Right click on a block to restore, and use &f/modebasic&c to stop."
+        mode-subdivision="&aSubdivision creation mode enabled. Use &f/modebasic&a to exit."
+        mode-town="&aTown creation mode enabled."
+        option-description-abandon-delay="&aThe amount of days before a newly created claim can be abandoned."
+        option-description-abandon-return-ratio="&aThe portion of basic claim blocks returned to a player when a claim is abandoned."
+        option-description-blocks-accrued-per-hour="&aBlocks earned per hour.\n&dNote&f: See /playerinfo for more information."
+        option-description-chest-expiration="&aNumber of days of inactivity before an automatic chest claim will expire.\n&dNote&f: On expiration, a claim may either be restored back to original state or be deleted. This depends on the server configuration. Contact an administrator for more information."
+        option-description-create-limit="&aMaximum number of basic claims per player.\n&dNote&f: Setting a value under 0 will make it unlimited."
+        option-description-create-mode="&aThe claim create mode type (Area = 2D, Volume = 3D).\n&dNote&f: &bArea&a only affects x and z axis.\n&bVolume&a affects x, y, and z axis."
+        option-description-economy-block-cost="&aThe economy amount to charge per block of a claim.\n&dNote&f: The formula to calculate price is amount * total claim blocks."
+        option-description-economy-block-sell-return="&aThe return ration for selling claim blocks.\n&dNote&f: The formula to calculate return is ratio * total claim blocks."
+        option-description-expiration="&aNumber of days of inactivity before a claim will expire.\n&dNote&f: On expiration, a claim may either be restored back to original state or be deleted. This depends on the server configuration. Contact an administrator for more information."
+        option-description-initial-blocks="&aThe number of claim blocks a player has initially, by default."
+        option-description-max-accrued-blocks="&aThe limit on accrued blocks (over time).\n&dNote&f: This doesn't limit purchased or admin-gifted blocks."
+        option-description-max-level="&aThe maximum level, on y-axis, that a claim can be created in."
+        option-description-max-size-x="&aThe max size in blocks that the x-axis can be."
+        option-description-max-size-y="&aThe max size in blocks that the y-axis can be."
+        option-description-max-size-z="&aThe max size in blocks that the z-axis can be."
+        option-description-min-level="&aThe minimum level, on y-axis, that a claim can be created in."
+        option-description-min-size-x="&aThe min size in blocks that the x-axis can be."
+        option-description-min-size-y="&aThe min size in blocks that the y-axis can be."
+        option-description-min-size-z="&aThe min size in blocks that the z-axis can be."
+        option-description-player-command="&aUsed for executing a command with specific contexts."
+        option-description-player-deny-flight="&aUsed to determine if a player is unable to fly in a claim.\n&dNote&f: This does not give players the ability to fly, it merely removes the ability if set. This provides the greatest compatibility with plugins."
+        option-description-player-deny-godmode="&aUsed to determine if a player can be in godmode within a claim.\n&dNote&f: This does not give players the ability to be in godmode, it merely removes the ability if set. This provides the greatest compatibility with plugins."
+        option-description-player-deny-hunger="&aUsed to if a player's hunger is denied in a claim.\n&dNote&f: This does not give players the ability to gain hunger, it merely removes the ability to cause hunger if set. This provides the greatest compatibility with plugins."
+        option-description-player-gamemode="&aUsed to determine the gamemode of a player in a claim."
+        option-description-player-health-regen="&aUsed to set the health regen amount for a player in a claim.\n&dNote&f: If the player is at max health, this will have no effect. \n&dNote&f: A value of &6-1&f disables this option."
+        option-description-player-keep-inventory="&aUsed to determine if a player can keep inventory after death in a claim."
+        option-description-player-keep-level="&aUsed to determine if a player can keep their level after death in a claim."
+        option-description-player-walk-speed="&aUsed to set a player's walk speed in a claim.\n&dNote&f: A value of &6-1&f disables this option."
+        option-description-player-weather="&aUsed to set a player's weather in a claim."
+        option-description-radius-inspect="&aThe radius in blocks used to search for nearby claims while inspecting."
+        option-description-radius-list="&aThe radius in blocks used to list nearby claims."
+        option-description-tax-expiration="&aNumber of days after not paying taxes before a claim will be frozen.\n&dNote&f: A frozen state means you will have no access to build or interact in claim until taxes are paid."
+        option-description-tax-expiration-days-keep="&aNumber of days to keep a basic claim after frozen and before expiring.\n&dNote&f: On expiration, a claim may either be restored back to original state or be deleted. This depends on the server configuration. Contact an administrator for more information."
+        option-description-tax-rate="&aThe tax rate of claim.\n&dNote&f: Tax rate is calculated by the number of claimblocks in the basic claim."
+        option-invalid-context="&cInvalid context '&f{context}&c' entered for option &f{option}&c."
+        option-invalid-target="&cInvalid target '&f{target}&c' entered for option &f{option}&c."
+        option-invalid-value="&cInvalid value '&6{value}&c' entered for option &f{option}&c.\n&dNote&f: This option only accepts &f{type}&c values."
+        option-not-found="&cOption {option}&c not found."
+        option-not-set="{option}&f is currently not set.\n&dNote&f: The default option value of {value}&f will be active until set."
+        option-override-not-supported="&cClaim type {type}&c does not support option overrides."
+        option-player-deny-flight="&cYou do not have access to fly in this claim and have been teleported to a safe spot on ground."
+        option-reset-success="&aClaim options reset to defaults successfully."
+        option-set-target="&aSet {type}&a option &b{option}&a to {value}&a with contexts &7{contexts}&a on &6{target}&a."
+        option-ui-click-toggle="Click here to toggle {option}&f value."
+        option-ui-inherit-parent="This option is inherited from parent claim {name}&f and &ncannot&f be changed."
+        option-ui-overridden="&cFailed to set option. The option &f{option}&c has been overridden by an admin."
+        option-ui-override-no-permission="This option has been overridden by an administrator and can &n&cNOT&f be changed."
+        owner-admin="an administrator"
+        permission-access="&cYou don't have &6{player}&c's permission to access that."
+        permission-assign-without-having="&cYou are not allowed to assign a permission that you do not have."
+        permission-ban-block="&cThe block {id}&c has been &l&nBANNED&c and cannot be used."
+        permission-ban-entity="&cThe entity {id}&c has been &l&nBANNED&c and cannot be used."
+        permission-ban-item="&cThe item {id}&c has been &l&nBANNED&c and cannot be used."
+        permission-build="&cYou don't have &6{player}&c's permission to build."
+        permission-build-near-claim="&cYou don't have &6{player}&c's permission to build near claim."
+        permission-claim-create="&cYou don't have permission to claim land."
+        permission-claim-delete="&cYou don't have permission to delete {type}&c claims."
+        permission-claim-enter="&cYou don't have permission to enter this claim."
+        permission-claim-exit="&cYou don't have permission to exit this claim."
+        permission-claim-ignore="&cYou do not have permission to ignore {type}&c claims."
+        permission-claim-list="&cYou don't have permission to get information about another player's land claims."
+        permission-claim-manage="&cYou don't have permission to manage {type}&c claims."
+        permission-claim-reset-flags="&cYou don't have permission to reset {type}&c claims to flag defaults."
+        permission-claim-reset-flags-self="&cYou don't have permission to reset your claim flags to defaults."
+        permission-claim-resize="&cYou don't have permission to resize this claim."
+        permission-claim-sale="&cYou don't have permission to sell this claim."
+        permission-claim-transfer-admin="&cYou don't have permission to transfer admin claims."
+        permission-clear="&cCleared permissions in this claim. To set permission for ALL your claims, stand outside them."
+        permission-clear-all="&cOnly the claim owner can clear all permissions."
+        permission-command-trust="&cYou don't have permission to use this type of trust."
+        permission-cuboid="&cYou don't have permission to create/resize basic claims in 3D mode."
+        permission-edit-claim="&cYou don't have permission to edit this claim."
+        permission-fire-spread="&cYou don't have permission to spread fire in this claim."
+        permission-flag-defaults="&cYou don't have permission to manage flag defaults."
+        permission-flag-overrides="&cYou don't have permission to manage flag overrides."
+        permission-flag-use="&cYou don't have permission to use this flag."
+        permission-flow-liquid="&cYou don't have permission to flow liquid in this claim."
+        permission-global-option="&cYou don't have permission to manage global options."
+        permission-grant="&cYou can't grant a permission you don't have yourself."
+        permission-group-option="&cYou don't have permission to assign an option to a group."
+        permission-interact-block="&cYou don't have &6{player}'s &cpermission to interact with the block &d{block}&c."
+        permission-interact-entity="&cYou don't have &6{player}'s &cpermission to interact with the entity &d{entity}&c."
+        permission-interact-item="&cYou don't have &6{player}'s &cpermission to interact with the item &d{item}&c."
+        permission-interact-item-block="&cYou don't have permission to use &d{item}&c on a &b{block}&c."
+        permission-interact-item-entity="&cYou don't have permission to use &d{item}&c on a &b{entity}&c."
+        permission-inventory-open="&cYou don't have &6{player}'s&c permission to open &d{block}&c."
+        permission-item-drop="&cYou don't have &6{player}'s&c permission to drop the item &d{item}&c in this claim."
+        permission-item-use="&cYou can't use the item &d{item}&c in this claim."
+        permission-option-defaults="&cYou don't have permission to manage option defaults."
+        permission-option-overrides="&cYou don't have permission to manage option overrides."
+        permission-option-use="&cYou don't have permission to use this option."
+        permission-override-deny="&cThe action you are attempting to perform has been denied by an administrator's override flag."
+        permission-player-admin-flags="&cYou don't have permission to change flags on an admin player."
+        permission-player-option="&cYou don't have permission to assign an option to a player."
+        permission-player-view-others="&cYou don't have permission to view other players."
+        permission-portal-enter="&cYou can't use this portal because you don't have &6{player}'s &cpermission to enter the destination claim."
+        permission-portal-exit="&cYou can't use this portal because you don't have &6{player}'s &cpermission to exit the destination claim."
+        permission-protected-portal="&cYou don't have permission to use portals in this claim owned by &6{player}'s&c."
+        permission-trust="&cYou don't have &6{player}'s&c permission to manage permissions here."
+        permission-visual-claims-nearby="&cYou don't have permission to visualize nearby claims."
+        player-accrued-blocks-exceeded="&cPlayer &6{player}&c has a total of &6{total}&c and will exceed the maximum allowed accrued claim blocks if granted an additional &6{amount}&c of blocks.\nEither lower the amount or have an admin grant the user with an override."
+        player-remaining-blocks-2d="&aYou may claim up to &6{block-amount}&a more blocks."
+        player-remaining-blocks-3d="&aYou may claim up to &6{chunk-amount}&a more chunks. &f({block-amount})"
+        playerinfo-ui-abandon-return-ratio="&eAbandoned Return Ratio&f : &a{ratio}"
+        playerinfo-ui-block-accrued="&eAccrued Blocks&f : &a{amount}&7(&d{block_amount}&f per hour&7)"
+        playerinfo-ui-block-bonus="&eBonus Blocks&f : &a{amount}"
+        playerinfo-ui-block-initial="&eInitial Blocks&f : &a{amount}"
+        playerinfo-ui-block-max-accrued="&eMax Accrued Blocks&f : &a{amount}"
+        playerinfo-ui-block-remaining="&eRemaining Blocks&f : &a{amount}"
+        playerinfo-ui-block-total="&eTotal Blocks&f : &a{amount}"
+        playerinfo-ui-chunk-total="&eTotal Claimable Chunks&f : &a{amount}"
+        playerinfo-ui-claim-level="&eMin/Max Claim Level&f : &a{level}"
+        playerinfo-ui-claim-size-limit="&eClaim Size Limits&f : &a{limit}"
+        playerinfo-ui-claim-total="&eTotal Claims&f : &a{amount}"
+        playerinfo-ui-economy-block-available-purchase="&eRemaining Blocks for Purchase&f : &a{amount}"
+        playerinfo-ui-economy-block-cost="&eClaim Block Price&f : &a{amount} per block"
+        playerinfo-ui-economy-block-sell-return="&eClaim Block Sell Return&f : &a{amount} per block"
+        playerinfo-ui-last-active="&eLast Active&f : {date}"
+        playerinfo-ui-tax-current-rate="&eCurrent Claim Tax Rate&f : &a{rate}"
+        playerinfo-ui-tax-global-claim-rate="&eGlobal Claim Tax Rate&f : &a{rate}"
+        playerinfo-ui-tax-global-town-rate="&eGlobal Town Tax Rate&f : &a{rate}"
+        playerinfo-ui-tax-total="&eTotal Tax&f : &a{amount}"
+        playerinfo-ui-title="&bPlayer Info"
+        playerinfo-ui-uuid="&eUUID&f : &7{id}"
+        playerinfo-ui-world="&eWorld&f : &7{name}"
+        plugin-command-not-found="&cCould not locate the command '&a{command}&c' for plugin &b{id}&c."
+        plugin-event-cancel="&cA plugin has cancelled this action."
+        plugin-not-found="&cCould not locate plugin with id &b{id}&c."
+        plugin-reload="&aGriefDefender has been reloaded."
+        pvp-claim-not-allowed="&aYou are not allowed to PvP in this claim."
+        pvp-source-not-allowed="&aYou are not allowed to PvP."
+        pvp-target-not-allowed="&aYou cannot attack players who are not participating in PvP."
+        registry-block-not-found="&cThe block {id}&c could not be found in registry."
+        registry-entity-not-found="&cThe entity {id}&c could not be found in registry."
+        registry-item-not-found="&cThe item {id}&c could not be found in registry."
+        resize-overlap="&cCan't resize here because it would overlap another nearby claim."
+        resize-overlap-subdivision="&cYou can't create a subdivision here because it would overlap an existing subdivision."
+        resize-same-location="&cYou must select a different block location to resize claim."
+        resize-start="&aResizing claim.  Use your tool again at the new location for this corner."
+        resize-success-2d="&aClaim resized. You have &6{block-amount} &amore blocks remaining."
+        resize-success-3d="&aClaim resized. You have &6{chunk-amount} &amore chunks remaining. &f({block-amount})"
+        result-type-change-deny="&cYou cannot change a claim to {type}."
+        result-type-change-not-admin="&cYou do not have administrative permissions to change type to {type}&c."
+        result-type-child-same="{type}&c claims cannot have direct {type}&c children claims."
+        result-type-create-deny="{type}'s&c cannot be created in the {target_type}."
+        result-type-no-children="{type}'s&c cannot contain children claims."
+        result-type-only-subdivision="{type}'s&c can only contain subdivisions."
+        result-type-requires-owner="&cCould not convert {type} claim to {target_type}. Owner is required."
+        schematic-abandon-all-restore-warning="&6Are you sure you want to &nabandon&6 &cALL&6 your claims? &cALL DATA WILL BE LOST&f!!&6 Your claims will be restored back to their original state of creation on confirmation."
+        schematic-abandon-restore-warning="&6Are you sure you want to &nabandon&6 this claim? &cALL DATA WILL BE LOST&f!!&6 This claim will be restored back to its original state of creation on confirmation."
+        schematic-create="&aCreating schematic backup..."
+        schematic-create-complete="&aSchematic backup complete."
+        schematic-create-fail="&cSchematic could not be created."
+        schematic-deleted="&aSchematic {name} has been deleted."
+        schematic-none="&aThere are no schematic backups for this claim."
+        schematic-restore-click="&aClick here to restore claim schematic.\nName: {name}\nCreated: {date}"
+        schematic-restore-confirmation="&6Are you sure you want to restore? Clicking confirm will restore &cALL&6 claim data with schematic. Use cautiously!"
+        schematic-restore-confirmed="&aYou have successfully restored your claim from schematic backup &b{name}&a."
+        spawn-not-set="&cNo claim spawn has been set."
+        spawn-set-success="&aSuccessfully set claim spawn to &b{location}&a."
+        spawn-teleport="&aTeleported to claim spawn at &b{location}&a."
+        tax-claim-expired="&cThis claim has been frozen due to unpaid taxes. The current amount owed is '&a{amount}&c'.\nThere are '&a{days}&c' days left to deposit payment to claim bank in order to unfreeze this claim.\nFailure to pay this debt will result in deletion of claim.\nNote: To deposit funds to claimbank, use &f/claimbank&c deposit <amount>."
+        tax-claim-paid-balance="&aThe tax debt of '&6{amount}&a' has been paid. Your claim has been unfrozen and is now available for use."
+        tax-claim-paid-partial="&aThe tax debt of '&6{amount}&a' has been partially paid. In order to unfreeze your claim, the remaining tax owed balance of '&6{balance}&a' must be paid."
+        tax-info="&aYour next scheduled tax payment of &6{amount}&a will be withdrawn from your account on &b{date}&a."
+        tax-past-due="&cYou currently have a past due tax balance of &a{balance}&c that must be paid by &b{date}&c. Failure to pay off your tax balance will result in losing your property."
+        teleport-confirm="&aAre you sure you want to teleport to {pos}? Click confirm to proceed."
+        teleport-delay-notice="&aYou will teleport in {delay}&a seconds."
+        teleport-move-cancel="&cTeleport cancelled! You cannot move while attempting to teleport."
+        teleport-no-safe-location="&cNo safe location found in claim to teleport!\n&aUse the '&f/claiminfo&a' command to set a safe spawn point instead."
+        teleport-success="&aYou have been teleported to {name}&a."
+        title-accessor=ACCESSOR
+        title-all=ALL
+        title-builder=BUILDER
+        title-claim=CLAIM
+        title-container=CONTAINER
+        title-default=DEFAULT
+        title-inherit=INHERIT
+        title-manager=MANAGER
+        title-override=OVERRIDE
+        title-own=OWN
+        tool-not-equipped="&cYou do not have {tool}&c equipped."
+        town-chat-disabled="&aTown chat disabled."
+        town-chat-enabled="&aTown chat enabled."
+        town-create-not-enough-funds="&cYou do not have enough funds to create this town for &a{amount}&c. You currently have a balance of &a{balance}&c and need &a{amount-needed}&c more for creation."
+        town-name="&aSet town name to {name}&a."
+        town-not-found="&cTown not found."
+        town-not-in="&cYou are not in a town."
+        town-owner="&cThat belongs to the town."
+        town-tag="&aSet town tag to {tag}."
+        town-tag-clear="&aThe town tag has been cleared."
+        town-tax-no-claims="&cYou must own property in this town in order to be taxed."
+        trust-already-has="&c{target} already has {type}&c permission."
+        trust-click-show-list="Click here to show list of all players and groups trusted in claim."
+        trust-grant="&aGranted &6{target}&a permission to {type}&a in current claim."
+        trust-individual-all-claims="&aGranted &6{player}'s&a full trust to all your claims.  To unset permissions for ALL your claims, use &f/untrustall&a."
+        trust-invalid="&cInvalid trust type entered.\nThe allowed types are : accessor, builder, container, and manager."
+        trust-list-header="Explicit permissions here:"
+        trust-no-claims="&cYou have no claims to trust."
+        trust-plugin-cancel="&cCould not trust {target}&c. A plugin has denied it."
+        trust-self="&cYou cannot trust yourself."
+        tutorial-claim-basic="&eClick for Land Claim Help: &ahttp://bit.ly/mcgpuser"
+        ui-click-confirm="Click to confirm."
+        ui-click-filter-type="Click here to filter by {type}&f."
+        untrust-individual-all-claims="&aRevoked &6{target}'s&a access to ALL your claims.  To set permissions for a single claim, stand inside it and use &f/untrust&a."
+        untrust-individual-single-claim="&aRevoked &6{target}'s&a access to this claim.  To unset permissions for ALL your claims, use &f/untrustall&a."
+        untrust-no-claims="&cYou have no claims to untrust."
+        untrust-owner="&6{owner}&a is owner of claim and cannot be untrusted."
+        untrust-self="&cYou cannot untrust yourself."
+    }
+}
diff --git a/sponge/src/main/resources/assets/lang/fr_FR.conf b/sponge/src/main/resources/assets/lang/fr_FR.conf
new file mode 100644
index 0000000..2863cf2
--- /dev/null
+++ b/sponge/src/main/resources/assets/lang/fr_FR.conf
@@ -0,0 +1,637 @@
+GriefDefender {
+    descriptions {
+        abandon-all="Abandonne l'ENSEMBLE de tes terrains."
+        abandon-claim="Abandonne un terrain."
+        abandon-top="Abandonne le terrain de plus haut niveau."
+        buy-blocks="Achète des blocs supplémentaires avec l'argent du serveur.\nNote: Nécessite un plug-in d'économie."
+        callback="Exécute un rappel défini comme faisant partie d'un objet textuel. Utilisée principalement pour un usage interne."
+        claim-bank="Utilisée pour retirer ou déposer de l'argent pour une utilisation dans le terrain."
+        claim-clear="Permet de nettoyer les entités dans un ou plusieurs terrain(s)."
+        claim-debug="Active/Désactive le mode débug pour les marques du terrain."
+        claim-farewell="Définis le message de sortie du terrain."
+        claim-greeting="Définis le message d'accueil du terrain."
+        claim-ignore="Active/Désactive le mode pour ignorer les terrains."
+        claim-info="Affiche l'ensemble des informations connues pour le terrain dans lequel tu te trouves."
+        claim-inherit="Active/Désactive l'héritage des permissions depuis un terrain parent."
+        claim-list="Liste l'ensemble des terrains connus dans la zone."
+        claim-name="Définis le nom du terrain."
+        claim-restore="Restaure le terrain à son état naturel. À utiliser avec prudence."
+        claim-setspawn="Définis le point d'apparition pour les joueurs."
+        claim-spawn="Te téléporte au point d'apparition si disponible."
+        claim-transfer="Transfert un terrain basique ou admin à un autre joueur."
+        claim-worldedit="Utilise la sélection WorldEdit pour créer un terrain."
+        cuboid="Active/Désactive le mode terrain cubique."
+        debug="Capture l'ensemble des actions GD à des fins de débuggage."
+        delete-all="Supprime l'ensemble des terrains d'un autre joueur."
+        delete-all-admin="Supprime l'ensemble des terrains admin."
+        delete-claim="Supprime la protection dans laquelle tu es, même s'il ne t'appartiens pas."
+        delete-top="Supprime le terrain de plus haut niveau dans lequel tu es, même s'il ne t'appartiens pas."
+        flag-claim="Récupère/Définis les marques du terrain dans lequel tu es."
+        flag-group="Récupère/Définis les marques de permission pour un groupe dans le terrain dans lequel tu es."
+        flag-player="Récupère/Définis les marques de permission pour un joueur dans le terrain dans lequel tu es."
+        flag-reset="Remets par défaut les marques dans le terrain."
+        mode-admin="Bascule l'outil pelle en mode terrain admin."
+        mode-basic="Re-bascule l'outil pelle en mode terrain basique."
+        mode-nature="Bascule l'outil pelle en mode restauration."
+        mode-subdivision="Bascule l'outil pelle en mode sous-division, utilisé pour sous-diviser les terrains."
+        mode-town="Bascule l'outil pelle en mode terrain Village."
+        option-claim="Récupère/Définis les options dans le terrain dans lequel tu es."
+        permission-group="Définis une permission sur un groupe avec un contexte de terrain."
+        permission-player="Définis une permission sur un joueur avec un contexte de terrain."
+        player-adjust-bonus-blocks="Mets à jour le nombre total de blocs de terrain bonus pour un joueur."
+        player-info="Affiche les informations concernant un joueur."
+        player-set-accrued-blocks="Mets à jour le nombre total de blocs gagné par un joueur."
+        reload="Recharge la configuration des paramètres de GriefDefender."
+        schematic="Gère les patrons de terrain. Utilises '/claimschematic create <nom>' pour créer directement une copie du terrain."
+        sell-blocks="Vends tes blocs de terrain contre de l'argent serveur.\nNote: Nécessite un plug-in d'économie."
+        sell-claim="Mets en vente ton terrain. Utilises /claimsell <montant>.\nNote: Nécessite un plug-in d'économie."
+        town-chat="Active/Désactive le chat du Village."
+        town-tag="Définis le blason du village."
+        trust-group="Donne à un groupe accès à ton terrain.\nAccessor: Permet d'intéragir avec l'ensemble des blocs sans inventaire.\nContainer: Permet d'intéragir avec l'ensemble des blocs avec inventaire.\nBuilder: Permet la même chose qu'au dessus avec en plus la possibilité de placer et casser des blocs.\nManager: Permet la même chose qu'au dessus avec en plus la possibilité de gérer les paramètres de la protection."
+        trust-group-all="Donne à un groupe l'accès à l'ENSEMBLE de tes terrains.\nAccessor: Permet d'intéragir avec l'ensemble des blocs sans inventaire.\nContainer: Permet d'intéragir avec l'ensemble des blocs avec inventaire.\nBuilder: Permet la même chose qu'au dessus avec en plus la possibilité de placer et casser des blocs.\nManager: Permet la même chose qu'au dessus avec en plus la possibilité de gérer les paramètres de la protection."
+        trust-player="Donne à un joueur accès à ton terrain.\nAccessor: Permet d'intéragir avec l'ensemble des blocs sans inventaire.\nContainer: Permet d'intéragir avec l'ensemble des blocs avec inventaire.\nBuilder: Permet la même chose qu'au dessus avec en plus la possibilité de placer et casser des blocs.\nManager: Permet la même chose qu'au dessus avec en plus la possibilité de gérer les paramètres de la protection."
+        trust-player-all="Donne à un joueur accès à l'ENSEMBLE de tes terrains.\nAccessor: Permet d'intéragir avec l'ensemble des blocs sans inventaire.\nContainer: Permet d'intéragir avec l'ensemble des blocs avec inventaire.\nBuilder: Permet la même chose qu'au dessus avec en plus la possibilité de placer et casser des blocs.\nManager: Permet la même chose qu'au dessus avec en plus la possibilité de gérer les paramètres de la protection."
+        untrust-group="Supprime les accès d'un groupe à ton terrain."
+        untrust-group-all="Supprime les accès d'un groupe à l'ENSEMBLE de tes terrains."
+        untrust-player="Supprime les accès d'un joueur à ton terrain."
+        untrust-player-all="Supprime les accès d'un joueur à l'ENSEMBLE de tes terrains."
+        version="Affiche les informations sur la version de GriefDefender."
+    }
+    messages {
+        abandon-all-delay-warning="&aCes terrains ont été créés récemment et ne peuvent être abandonnés."
+        abandon-all-warning="&6Es-tu sûr de vouloir abandonner &cTOUS&6 tes terrains ?"
+        abandon-claim-delay-warning="&aCe terrain a été créé récemment et ne peut pas être abandonnée avant le &6{date}&a."
+        abandon-claim-failed="&aN'a pas réussi à abandonner le terrain. Résultat du terrain était: &f{result}&a."
+        abandon-claim-missing="&cPas de terrain trouvé. Va dans le terrain que tu veux abandonner ou envisages &f/abandonall&c."
+        abandon-other-success="Le terrain de &6{player}&a a été abandonné. &6{player}&a dispose de maintenant &6{amount}&a blocs de terrain disponibles."
+        abandon-success="&aTerrain abandonné. Tu as maintenant &6{amount}&a blocs de terrain disponibles."
+        abandon-top-level="&cCe terrain ne peut être abandonné car il contient un ou plusieurs terrain(s) enfant(s). Pour abandonner un terrain avec enfant, tu dois utiliser  &f/abandontop&c à la place."
+        abandon-town-children="&cTu n'as pas la permission pour abandonner un village contenant des terrains enfants qui ne t'appartiennent pas. Utilises &f/ignoreclaims&c ou fais en sorte que le propriétaire des terrains enfants les abandonne d'abord. Si tu veux abandonner le village sans affecter les terrains enfants, utilises &f/abandon&c à la place."
+        abandon-warning="&6Es-tu sûr de vouloir abandonner ce terrain ? Il ne sera plus protégé contre les dégâts."
+        adjust-accrued-blocks-success="&aAjustement du nombre de blocs de terrain gagnés par &6{player}&a de &6{amount}&a. Nouveau total de blocs de terrain gagnés: &6{total}&a."
+        adjust-bonus-blocks-success="&aAjustement du nombre de blocs de terrain, bonus &6{player}&a de &6{amount}&a. Nouveau total de blocs de terrain bonus: &6{total}&a."
+        bank-click-view-transactions="Clique ici pour voir les transactions bancaires"
+        bank-deposit="&aDépôt de &6{amount}&a avec succès dans la banque."
+        bank-deposit-no-funds="&cTu n'as pas suffisamment de fonds pour faire un dépôt en banque."
+        bank-info="&aSolde: &6{balance}&a \nTaxe: &6{tax-amount}&f prélevée au &7{time-remaining}&a \nTaxe due: &6{tax-balance}."
+        bank-no-permission="&cTu n'as pas la permission de gérer la banque de terrain de &6{player}&c."
+        bank-tax-system-disabled="&cLa banque/système de taxe n'est pas activé. Si tu veux l'activer, paramètre 'bank-tax-system' à true dans le fichier de configuration."
+        bank-title-transactions="Transactions Bancaire"
+        bank-withdraw="&aRetrait de &6{amount}&a de la banque avec succès."
+        bank-withdraw-no-funds="&cLa banque de terrain a un solde de &a{balance}&c et n'a pas suffisamment de fonds pour un retrait de &a{amount}&c."
+        block-claimed="&aCe bloc a été réclamé par &6{player}&a."
+        block-not-claimed="&cPersonne n'a réclamé ce bloc."
+        block-sale-value="&aChaque bloc de terrain vaut &6{amount}&a. Tu as &6{total}&a disponible pour la vente."
+        claim-above-level="&cImpossible de réclamer le bloc car il est au-dessus de niveau maximum limite de &a{limit}&c."
+        claim-action-not-available="&cCette action n'est pas disponible dans {type}&c."
+        claim-automatic-notification="&cCe coffre et les blocs à proximités sont protégés."
+        claim-below-level="&cImpossible de réclamer le bloc car il est en-dessous du niveau minimum limite de &a{limit}&c."
+        claim-chest-confirmation="&cCe coffre est protégé."
+        claim-chest-outside-level="&cCe coffre ne peut pas être protégé car sa position est en dehors du niveau limite de terrain de &a{min-level}&c et &a{max-level}&c."
+        claim-children-warning="&6Cet terrain contient des terrains enfants. Si tu es sûr de vouloir la supprimer, utilises &f/deleteclaim&6 à nouveau."
+        claim-context-not-found="&cContexte &f{context}&c non trouvé."
+        claim-disabled-world="&cLes terrains sont désactivées dans ce monde."
+        claim-expired-inactivity="&cLe terrain du joueur &6{player} avec l'id &f{uuid}&c a expiré et a été supprimé pour cause d'inactivité."
+        claim-farewell="&aDéfinis le message de sortie à {farewell}&a."
+        claim-farewell-clear="&aLe message de sortie a été supprimé."
+        claim-farewell-invalid="&cLa marque de terrain &f{flag}&c n'est pas valide."
+        claim-greeting="&aDéfinis le message d'accueil sur {greeting}&a."
+        claim-greeting-clear="&aLe message de d'accueil a été supprimé."
+        claim-ignore="&aIgnore maintenant les terrains."
+        claim-last-active="&aDernière activité du terrain le &6{date}&a."
+        claim-mode-start="coin {type}&a définis ! Clique droit à nouveau dans le coin opposé pour protéger un rectangle de terrain. Pour annuler, écris &f/claim&a pour sortir du terrain."
+        claim-name="&aDéfinis le nom du terrain à {name}&a."
+        claim-no-claims="&cTu n'as aucun terrain reclamé."
+        claim-no-set-home="&cTu dois avoir la confiance pour utiliser /sethome ici."
+        claim-not-found="&cIl n'y a pas de terrain ici."
+        claim-not-yours="&cCe n'est pas ton terrain."
+        claim-owner-already="&cTu es déjà le propriétaire de ce terrain."
+        claim-owner-only="&cSeulement &6{player}&c peut modifier ce terrain."
+        claim-protected-entity="&cCela appartient à &6{player}&c."
+        claim-respecting="&aRespecte maintenant les terrains."
+        claim-restore-success="&aRestauration du terrain avec succès."
+        claim-show-nearby="&aTrouvé &6{amount}&a terrain(s) à proximité."
+        claim-size-max="&cLa taille de &6{axis}&c sur &a{size}&c du terrain excède la taille maximum de &a{max-size}&c.\nLa zone a besoin d'être un minimum de &a{min-area}&c et de maximum &a{max-area}"
+        claim-size-min="&cLa taille de &6{axis}&c sur &a{size}&c du terrain est sous la taille minimum de &a{min-size}&c.\nLa zone a besoin d'être un minimum de &a{min-area}&c et de maximum &a{max-area}"
+        claim-size-need-blocks-2d="&cTu n'as pas suffisamment de blocs pour un terrain de cette taille.\nTu as besoin de &a{block-amount}&c blocs supplémentaires."
+        claim-size-need-blocks-3d="&cTu n'as pas suffisamment de blocs pour un terrain de cette taille.\nTu as besoin de &a{chunk-amount}&c chunks supplémentaires. &f({block-amount})"
+        claim-size-too-small="&cLa taille de la zone de terrain sélectionnée de &a{width}&fx&a{length}&c sera trop petite. Une protection doit être au minimum de  &a{min-width}&fx&a{min-length}&c en taille."
+        claim-start="{type}&a Coin défini ! Utilise la pelle au coin opposé pour protéger un rectangle de terre. Pour annuler, met la pelle de côté."
+        claim-too-far="&cC'est trop loin."
+        claim-transfer-exceeds-limit="&cLe terrain ne peut pas être transféré car cela dépassera la limite de création du nouveau propriétaire."
+        claim-transfer-success="&aTerrain transféré."
+        claim-type-not-found="&cPas de terrain {type}&c trouvé."
+        claiminfo-ui-admin-settings="Paramètres Admin"
+        claiminfo-ui-bank-info="Information Bancaire"
+        claiminfo-ui-claim-expiration="Expiration du terrain"
+        claiminfo-ui-click-admin="Clique ici pour voir les paramètres admin"
+        claiminfo-ui-click-bank="Clique ici pour vérifier les informations bancaire"
+        claiminfo-ui-click-change-claim="Clique ici pour changer le terrain à {type}"
+        claiminfo-ui-click-toggle="Clique ici pour basculer la valeur"
+        claiminfo-ui-deny-messages="Messages de refus"
+        claiminfo-ui-flag-overrides="Marque outrepassant"
+        claiminfo-ui-for-sale="À Vendre"
+        claiminfo-ui-inherit-parent="Héritage parent"
+        claiminfo-ui-last-active="Dernière activité"
+        claiminfo-ui-north-corners="Coin nord"
+        claiminfo-ui-pvp-override="Outrepasser PvP"
+        claiminfo-ui-requires-claim-blocks="Blocs de terrain nécessaires"
+        claiminfo-ui-return-bankinfo="Retour aux informations bancaires"
+        claiminfo-ui-return-claiminfo="Retour aux informations de terrain"
+        claiminfo-ui-return-settings="Retour aux informations standards"
+        claiminfo-ui-size-restrictions="Restriction de taille"
+        claiminfo-ui-south-corners="Coin sud"
+        claiminfo-ui-teleport-direction="Clique ici pour te téléporter au coin {direction}&f du terrain"
+        claiminfo-ui-teleport-feature="Tu n'as pas la permission pour utiliser la fonction de téléportation dans ce terrain"
+        claiminfo-ui-teleport-spawn="Clique ici pour te téléporter au point d'appartition du terrain"
+        claiminfo-ui-title-claiminfo="Information du terrain"
+        claiminfo-ui-town-settings="Paramètre de la ville"
+        claimlist-ui-click-info="Clique ici pour voir plus d'informations"
+        claimlist-ui-click-purchase="Clique ici pour acheter le terrain"
+        claimlist-ui-click-teleport-target="Clique ici pour te téléporter à {name}&f {target}&f dans &6{world}"
+        claimlist-ui-click-toggle-value="Clique ici pour basculer la valeur {type}"
+        claimlist-ui-click-view-children="Clique ici pour lister les terrains enfants"
+        claimlist-ui-click-view-claims="Clique ici pour voir les terrains qui t'appartiennent"
+        claimlist-ui-no-claims-found="Pas de terrain trouvé dans ce monde."
+        claimlist-ui-return-claimlist="Retourne à la liste des terrains"
+        claimlist-ui-title="Liste les terrains"
+        claimlist-ui-title-child-claims="Terrains enfants"
+        command-blocked="&cLa commande &f{command}&c a été bloquée par le propriétaire du terrain &6{player}&c."
+        command-claimban-success-block="&cBANNISSEMENT&a du bloc avec l'id {id}&a avec succès."
+        command-claimban-success-entity="&cBANNISSEMENT&a de l'entité avec l'id {id}&a avec succès."
+        command-claimban-success-item="&cBANNISSEMENT&a de l'objet avec l'id {id}&a avec succès."
+        command-claimbuy-title="&bTerrain à vendre"
+        command-claimclear-killed="&c &6{amount}&a entités de type {type}&f ont été tuées."
+        command-claimclear-no-entities="&cImpossible de localiser une entité de type {type}&c."
+        command-claimclear-uuid-deny="&cSeulement les admins peuvent nettoyer les terrains par UUID."
+        command-claimflagdebug-disabled="Mode débug de marque de terrain &cOFF"
+        command-claimflagdebug-enabled="Mode débug de marque de terrain &aON"
+        command-claiminfo-not-found="&cPas de joueur valide ou de terrain avec cet UUID trouvé."
+        command-claiminfo-uuid-required="&cUUID du terrain nécessaire si exécuté depuis une source non joueur."
+        command-claiminherit-disabled="Héritage terrain parent &cOFF"
+        command-claiminherit-enabled="Héritage terrain parent &aON"
+        command-claimmode-disabled="Mode terrain &cOFF"
+        command-claimmode-enabled="Mode terrain &aON&f\n&aClique gauche pour inspecter.\n&aClique droit pour réclamer.&b\nNote&f: &aUtilise &f/claim&a pour sortir du mode."
+        command-claimspawn-not-found="&aNe peut pas localiser un terrain avec le nom {name}&a."
+        command-claimunban-success-block="&cDÉBANISSEMENT&a du bloc avec l'id {id}&a avec succès."
+        command-claimunban-success-entity="&cDÉBANISSEMENT&a de l'entité avec l'id {id}&a avec succès."
+        command-claimunban-success-item="&cDÉBANISSEMENT&a de l'objet avec l'id {id}&a avec succès."
+        command-cuboid-disabled="&aRéclame maintenant en mode &d2D&a."
+        command-cuboid-enabled="&aRéclame maintenant en mode &d3D&a."
+        command-execute-failed="&cÉchec de l'exécution de la commande '{command} {args}'"
+        command-giveblocks-confirmation="&6Es-tu sûr de vouloir donner à {player}&6 {amount}&6 blocs de terrain ?"
+        command-giveblocks-confirmed="&aTransfert de bloc de terrain complet."
+        command-giveblocks-not-enough="&cPas suffisamment de blocs de terrain ! Tu as seulement {amount}&c bloc(s) de terrain disponible pour le transfert.\n&bNote&f: Ce montant n'inclut pas le nombre de blocs de terrain initial. Cela inclut uniquement le nombre de blocs gagnés et bonus."
+        command-giveblocks-received="&aTu as reçu {amount}&a blocs de terrain du joueur {player}&a."
+        command-inherit-only-child="&cCette commande ne peut être utilisée que dans des terrains enfants."
+        command-invalid="&cPas de commande valide entrée."
+        command-invalid-amount="&cMontant invalide &6{amount}&c entré."
+        command-invalid-claim="&cCette commande ne peut être utilisée dans les terrains de type {type}&c."
+        command-invalid-group="&cGroupe &6{group}&c n'est pas valide."
+        command-invalid-player="&cJoueur &6{player}&c n'est pas valide."
+        command-invalid-player-group="&cPas un joueur ou groupe valide."
+        command-invalid-type="&cType {type}&c invalide spécifié."
+        command-not-available-economy="&cCette commande n'est pas disponible quand le serveur est en mode économie."
+        command-option-exceeds-admin="&cLa valeur &a'{value}&c' de l'option dépasse la valeur admin de '&a{admin-value}&c'. Ajustement à la valeur admin..."
+        command-pet-confirmation="&aAnimal transféré."
+        command-pet-invalid="&cL'animal de type {type} n'est pas actuellement transférable."
+        command-pet-transfer-cancel="&aTransfert animal annulé."
+        command-pet-transfer-ready="&aPrêt pour le transfert!  Fait un clique droit sur l'animal que tu veux donner, ou annule avec un clique gauche."
+        command-player-not-found="&cJoueur '&6{player}&c' introuvable."
+        command-world-not-found="&cMonde '&6{world}&c' introuvable."
+        command-worldedit-missing="&cCette commande a besoin que WorldEdit soit installé sur le serveur."
+        create-cancel="&cLa création de ce terrain a été annulé."
+        create-cuboid-disabled="&cLa création de terrain en mode &d3D&c a été désactivé par un administrateur.\nTu peux uniquement créer un terrain en mode &d3D&c en tant qu'Admin ou dans un terrain en mode &d2D&c qui t'appartient."
+        create-failed-claim-limit="&cTu as atteint la limite de &a{limit}&c pour les terrains de type {type}&c. Utilises &f/abandon&c pour en supprimer un avant d'en créer un nouveau."
+        create-failed-result="&aLa création du terrain a échoué à cause de : &6{reason}&a."
+        create-insufficient-blocks-2d="&cTu n'as pas suffisamment de blocs pour protéger cette zone.\nTu as besoin de &a{amount}&c blocs supplémentaires."
+        create-insufficient-blocks-3d="&cTu n'as pas suffisamment de blocs pour protéger cette zone.\nTu as besoin de &a{amount}&c chunks supplémentaires. &f({block-amount})"
+        create-overlap="&cTu ne peut pas créer un terrain ici car il chevaucherait ton autre terrain. Utilises &f/abandonclaim&c pour le supprimer ou utilise la pelle dans un coin pour le redimensionner."
+        create-overlap-player="&cTu ne peut pas créer un terrain ici car il chevaucherait le terrain de &6{player}&c."
+        create-overlap-short="&cPas de terrain existant sur le coin sélectionné. Cliques sur un bloc valide à l'intérieur d'un terrain parent afin de créer une sous-division."
+        create-subdivision-fail="&cPas de terrain existant au coin sélectionné. Cliques sur un bloc valide dans une zone à l'intérieur du terrain parent pour créer la sous-division."
+        create-subdivision-only="&cImpossible de créer le terrain. Seulement les sous-divisions peuvent être créées à l'endroit d'un bloc unique."
+        create-success="{type}&a créée ! Utilises &f/trust&a pour la partager avec tes amis."
+        debug-error-upload="&cErreur d'envoi du contenu {content}&c."
+        debug-no-records="&cPas d'enregistrement de débug à coller !"
+        debug-paste-success="&aCollage avec succès !"
+        debug-record-end="Fin d'enregistrement"
+        debug-record-start="Démarrage d'enregistrement"
+        debug-time-elapsed="Temps passé"
+        delete-all-player-failed="&aNe peut pas supprimer l'ENSEMBLE des terrains de &6{player}&a. Résultat de terrain était &f{result}&a."
+        delete-all-player-success="&aSuppression de l'ENSEMBLE des terrains de &6{player}&a avec succès."
+        delete-all-player-warning="&6Es-tu sûr de vouloir supprimer l'ENSEMBLE des terrains de &6{player}&6 ?"
+        delete-all-type-deny="&cImpossible de supprimer l'ensemble des terrains de type {type}&c. Un plug-in l'a refusé."
+        delete-all-type-success="&&cSuppression de l'ensemble des terrains {type}&c."
+        delete-all-type-warning="&6Es-tu sûr de vouloir supprimer l'ENSEMBLE des terrains de type {type}&6 ?"
+        delete-claim-failed="&aNe peut pas supprimer le terrain. Le résultat du terrain était : &f{result}&a."
+        delete-claim-success="&aSuppression des terrains de {player}&a."
+        delete-claim-warning="&6Es-tu sûr de vouloir supprimer les terrains de {player}&6 ?"
+        economy-balance="&aVotre nouveau solde est de &6{balance}&a."
+        economy-block-available-purchase-2d="&aTu as suffisamment de fonds pour créer un terrain jusqu'à &6{block-amount} &ablocs supplémentaires."
+        economy-block-available-purchase-3d="&aTu as suffisamment de fonds pour créer un terrain jusqu'à &6{chunk-amount} &achunks supplémentaires. &f({block-amount})"
+        economy-block-buy-invalid="&cLe nombre de blocs doit être supérieur à 0."
+        economy-block-buy-sell-disabled="&cDésolé, l'achat et la vente de blocs de terrain est désactivé."
+        economy-block-cost="&aChaque bloc de terrain coûte &6{amount}&a."
+        economy-block-not-available="&cTu n'as pas autant de blocs disponible pour la vente."
+        economy-block-only-buy="&cLes blocs de terrain ne peuvent qu'être achetés, pas vendus."
+        economy-block-only-sell="&cLes blocs de terrain ne peuvent qu'être vendus, pas achetés."
+        economy-block-purchase-confirmation="&aRetrait de &6{amount}&a depuis ton compte. Tu as maintenant &6{balance}&a blocs de terrain disponibles."
+        economy-block-purchase-cost="&aChaque bloc de terrain coûte &6{amount}&a. Ton solde est de &6{balance}&a."
+        economy-block-purchase-limit="&cLe nouveau nombre de bloc de terrain total de &a{total}&c va dépasser la limite de bloc maximum de &a{limit}&c. La transaction a été annulée."
+        economy-block-sale-confirmation="&aDéposé &6{deposit}&a sur ton compte. Tu as maintenant &6{amount}&a blocs de terrain disponibles."
+        economy-block-sell-error="&cImpossible de vendre les blocs. Raison: &f{reason}&c."
+        economy-claim-abandon-success="&Terrain abandonné. Tu as été remboursé d'un total de '&6{amount}&a'."
+        economy-claim-buy-cancelled="&cAchat annulé ! Impossible d'acheter le terrain de &6{player}&c. Resultat est &a{result}"
+        economy-claim-buy-confirmation="&6Es-tu sûr de vouloir acheter ce terrain pour &a{amount}&6 ? Clique confirm pour procéder."
+        economy-claim-buy-confirmed="&aTu as acheté le terrain avec succès pour un montant de &6{amount}&a."
+        economy-claim-buy-not-enough-funds="&cTu n'as pas suffisamment de fond pour acheter ce terrain pour &a{amount}&c. Tu as actuellement un solde de &a{balance}&c et tu as besoin de &a{amount_required}&c supplémentaire pour l'achat"
+        economy-claim-buy-transfer-cancelled="&cTransfert de terrain annulé ! Ne peut transférer du propriétaire &6{owner}&c à &6{player}&c. Resultat est &a{result}"
+        economy-claim-not-for-sale="&cCe terrain n'est pas à vendre."
+        economy-claim-sale-cancelled="&aTu as annulé la vente de ton terrain."
+        economy-claim-sale-confirmation="&6Es-tu sûr de vouloir vendre ton terrain pour &a{amount}&6 ? Si la protection est vendue, l'ensemble des objets et blocs vont être transférés à l'acheteur. Clique confirm pour valider."
+        economy-claim-sale-confirmed="&aTu as mis en vente ton terrain avec succès pour un montant de &6{amount}&a."
+        economy-claim-sale-invalid-price="&cLe prix de vente de &a{amount}&c doit être supérieur ou égal à &a0&c."
+        economy-claim-sold="&aTon terrain est vendu ! Un montant de &6{amount}&a a été déposé sur ton compte. Ton solde total est maintenant de &6{balance}&a."
+        economy-mode-block-sale-confirmation="&aDépôt de &6{deposit}&a sur votre compte. Votre solde total est de &6{balance}&a. Tu as maintenant suffisamment de fonds pour protéger jusqu'à &6{amount}&a blocs supplémentaires."
+        economy-mode-resize-success-2d="&Terrain redimensionné. Ton nouveau solde est de &6{balance}&a. Tu as maintenant suffisamment de fonds pour protéger jusqu'à &6{block-amount} &ablocs supplémentaires."
+        economy-mode-resize-success-3d="&Terrain redimensionné. Ton nouveau solde est de &6{balance}&a. Tu as maintenant suffisamment de fonds pour protéger jusqu'à &6{chunk-amount} &achunks supplémentaires. &f({block-amount})"
+        economy-not-enough-funds="&cTu n'as pas suffisamment de fonds pour acheter ce terrain. Ton solde actuel est de '&a{balance}&c' mais tu as besoin de '&a{amount}&c' pour valider l'achat."
+        economy-not-installed="&cPlug-in d'économie non installé !"
+        economy-player-not-found="&cPas de compte bancaire trouvé pour le joueur &6{player}&c."
+        economy-remaining-funds="&aTu as &6{amount}&a de disponible pour protéger du terrain."
+        economy-virtual-not-supported="&cLe plug-in d'économie ne supporte pas les comptes virtuels, ce qui est nécessaire. Utilises un autre plug-in d'économie ou contact le dev du plug-in pour qu'il supporte les comptes virtuels."
+        economy-withdraw-error="&cImpossible de retirer des fonds. Raison: &f{reason}&c."
+        feature-not-available="&cCette fonctionnalité est actuellement en cours de développement et sera disponible dans une version future."
+        flag-description-block-break="Contrôle si un bloc peut être cassé.\n&dExemple&f : Pour prévenir n'importe quel source de casser un bloc de terre, entre\n&a/cf block-break minecraft:dirt false\n&bNote&f : minecraft représente le modID et dirt représente le blockID.\nNe pas spécifier de modID prendra toujours minecraft par défaut."
+        flag-description-block-grow="Contrôle si un bloc peut pousser.\n&dExemple&f : Pour prévenir un cactus de pousser, entre\n&a/cf block-grow minecraft:cactus false\n&bNote&f : minecraft représente le modID et cactus représente le blockID.\nNe pas spécifier de modID prendra toujours minecraft par défaut."
+        flag-description-block-modify="Contrôle si un bloc peut être modifié.\n&dExemple&f : Pour prévenir n'importe quelle source d'allumer un bloc, entre\n&a/cf block-modify minecraft:fire false\n&bNote&f : minecraft représente le modID et fire représente le blockID.\nNe pas spécifier de modID prendra toujours minecraft par défaut."
+        flag-description-block-place="Contrôle si un bloc peut être placé.\n&dExemple&f : Pour prévenir n'importe quelle source de placer un bloc de terre, entre\n&a/cf block-place minecraft:dirt false\n&bNote&f : minecraft représente le modID et dirt représente le blockID.\nNe pas spécifier de modID prendra toujours minecraft par défaut."
+        flag-description-block-spread="Contrôle si un bloc peut s'étendre à un autre.\n&dExemple&f : Pour prévenir le feu de s'étendre, entre\n&a/cf block-spread any false context[source=fire]\n&bNote&f : 'any' réprésente n'importe quel bloc cible et fire représente le blockID sourced.\nNe pas spécifier de modID prendra toujours minecraft par défaut."
+        flag-description-collide-block="Contrôle si une entité peut entrer en collision avec un bloc.\n&dExemple&f : Pour prévenir une entité d'entrer en collision avec une plaque de pression en pierre, entre\n&a/cf collide-block minecraft:stone_pressure_plate false\n&bNote&f : minecraft représente le modID et stone_pressure_plate représente le blockID.\nNe pas spécifier de modID prendra toujours minecraft par défaut."
+        flag-description-collide-entity="Contrôle si une entité peut entrer en collision avec une entité.\n&dExemple&f : Pour prévenir une entité d'entrer en collision avec un cadre, entre\n&a/cf collide-entity minecraft:item_frame false\n&bNote&f : minecraft représente le modID et item_frame représente le blockID.\nNe pas spécifier de modID prendra toujours minecraft par défaut."
+        flag-description-command-execute="Contrôle si une commande peut être exécutée.\n&dExemple&f : Pour prévenir la commandes pixelmon '/shop select' d'être exécutée, entre\n&a/cf command-execute pixelmon:shop[select] false\n&bNote&f : &o&6pixelmon&f représente le modID et &o&6shop&f représente la commande de base, et &o&6select&f représente l'argument.\nNe pas spécifier de modID prendra toujours minecraft par défaut."
+        flag-description-command-execute-pvp="Contrôle si une commande peut être exécutée en état PvP.\n&dExemple&f : Pour prévenir la commandes pixelmon '/shop select' d'être exécutée, entre\n&a/cf command-execute pixelmon:shop[select] false\n&bNote&f : &o&6pixelmon&f représente le modID et &o&6shop&f représente la commande de base, et &o&6select&f représente l'argument.\nNe pas spécifier de modID prendra toujours minecraft par défaut."
+        flag-description-custom-block-break="Contrôle si un bloc peut être cassé."
+        flag-description-custom-block-grow="Contrôle si un bloc peut pousser."
+        flag-description-custom-block-place="Contrôle si un bloc peut être placé."
+        flag-description-custom-block-spread="Contrôle si un bloc peut s'étendre."
+        flag-description-custom-build="Contrôle les actions autorisées contre les blocs comme le minage, placement et intéraction."
+        flag-description-custom-chest-access="Contrôle si un joueur peut accéder à l'inventaire d'un coffre."
+        flag-description-custom-chorus-fruit-teleport="Contrôle si un joueur peut se téléporter en utilisant un fruit chorus."
+        flag-description-custom-crop-growth="Contrôle si les pousses peuvent grandir."
+        flag-description-custom-damage-animals="Contrôle si les animaux peuvent prendre des dégâts."
+        flag-description-custom-enderman-grief="Contrôle si les enderman peuvent grief."
+        flag-description-custom-enderpearl="Contrôle si une enderpearl peut être utilisée."
+        flag-description-custom-enter-player="Contrôle si un joueur peut entrer dans une protection."
+        flag-description-custom-exit-player="Contrôle si un joueur peut sortir de la protection."
+        flag-description-custom-exp-drop="Contrôle si les orbes d'expériences peuvent apparaîtres."
+        flag-description-custom-explosion-block="Contrôle si les explosions affectent les blocs."
+        flag-description-custom-explosion-creeper="Contrôle si un creeper peut exploser."
+        flag-description-custom-explosion-entity="Contrôle si les explosions affectent les entités."
+        flag-description-custom-explosion-tnt="Contrôle si une TnT peut exploser."
+        flag-description-custom-fall-damage="Contrôle si le joueur peut prendre des dégâts de chute."
+        flag-description-custom-fire-damage="Contrôle si le feu fait des dégâts."
+        flag-description-custom-fire-spread="Contrôle si le feu peut se répandre."
+        flag-description-custom-grass-growth="Contrôle si l'herbe peut pousser."
+        flag-description-custom-ice-form="Contrôle si la glace peut se former."
+        flag-description-custom-ice-melt="Contrôle si la glace peut fondre."
+        flag-description-custom-interact-block="Contrôle si un joueur peut intéragir avec les blocs.\n&bNote&f: Cela n'inclut PAS les blocs avec inventaire comme les coffres."
+        flag-description-custom-interact-entity="Contrôle si un joueur peut intéragir avec une entité.\n&bNote&f: Cela n'inclut PAS l'accès au coffre des entités comme les chevaux."
+        flag-description-custom-interact-inventory="Contrôle si un joueur peut intéragir avec un inventaire."
+        flag-description-custom-invincible="Contrôle si un joueur est invincible contre les dégâts."
+        flag-description-custom-item-drop="Contrôle si un joueur peut jeter un objet."
+        flag-description-custom-item-pickup="Contrôle si un joueur peut ramasser un objet."
+        flag-description-custom-lava-flow="Contrôle si la lave peut couler."
+        flag-description-custom-leaf-decay="Contrôle si les feuilles peuvent dépérir."
+        flag-description-custom-lighter="Contrôle si un joueur peut utiliser un briquet."
+        flag-description-custom-lightning="Contrôle si un éclair peut causer des dégâts."
+        flag-description-custom-monster-damage="Contrôle si un monstre peut faire des dégâts."
+        flag-description-custom-mushroom-growth="Contrôle si les champignons peuvent grandir."
+        flag-description-custom-mycelium-spread="Contrôle si le mycelium peut s'étendre."
+        flag-description-custom-pistons="Contrôle si un piston peut être utilisé."
+        flag-description-custom-portal-use="Contrôle si un portail peut être utilisé."
+        flag-description-custom-pvp="Contrôle si le combat Joueur contre Joueur est autorisé."
+        flag-description-custom-ride="Contrôle si les véhicules (incluant les animaux) peuvent être montés."
+        flag-description-custom-sleep="Contrôle si les joueurs peuvent dormir dans les lits."
+        flag-description-custom-snow-fall="Contrôle si la neige peut tomber."
+        flag-description-custom-snow-melt="Contrôle si la neige peut fondre."
+        flag-description-custom-snowman-trail="Contrôle si un bonhomme de neige peut laisser de la neige derrière lui."
+        flag-description-custom-soil-dry="Contrôle si la terre peut sécher."
+        flag-description-custom-spawn-ambient="Contrôle si les environnementaux, comme les chauves-souris, peuvent apparaître."
+        flag-description-custom-spawn-animal="Contrôle si les animaux, comme les vaches ou cochons, peuvent apparaître."
+        flag-description-custom-spawn-aquatic="Contrôle si les aquatiques, comme les poulpes et gardiens, peuvent apparaître."
+        flag-description-custom-spawn-monster="Contrôle si les monstres, comme les creepers ou les squelettes, peuvent apparaître."
+        flag-description-custom-teleport-from="Contrôle si les joueurs peuvent se téléporter depuis la protection."
+        flag-description-custom-teleport-to="Contrôle si les joueur peuvent se téléporter vers la protection."
+        flag-description-custom-use="Contrôle si les joueurs peuvent utiliser des blocs sans inventaire dans la protection."
+        flag-description-custom-vehicle-destroy="Contrôle si un vehicule peut être détruit."
+        flag-description-custom-vehicle-place="Contrôle si un véhicule (bateau, minecart) peut être placé."
+        flag-description-custom-vine-growth="Contrôle si les vignes (et les algues) peuvent grandir."
+        flag-description-custom-water-flow="Contrôle si l'eau peut couler."
+        flag-description-custom-wither-damage="Contrôle si un Withers peut faire des dégâts."
+        flag-description-enter-claim="Contrôle si une entité peut rentrer dans une protection.\n&dExemple&f : Pour prévenir les joueurs de rentrer dans une protection, entre\n&a/cf enter-claim player false\n&bNote&f : Si tu veux utiliser ça sur un groupe, utilises la commande /cfg."
+        flag-description-entity-chunk-spawn="Contrôle si une entité sauvegardée peut apparaître pendant le chargement d'un chunk.\n&dExemple&f : Pour prévenir les chevaux d'apparaître pendant le chargement du chunk, entre\n&a/cf entity-chunk-spawn minecraft:horse false\n&bNote&f : Cela va supprimer l'ENSEMBLE des entités sauvegardées quand le chunk va charger.  Si le chunk est déjà chargé, l'effet aura lieu après le rechargement. À utiliser avec une extrême prudence."
+        flag-description-entity-damage="Contrôle si une entité peut prendre des dégâts.\n&dExemple&f : Pour prévenir les animaux de prendre des dégâts, entre\n&a/cf entity-damage minecraft:animal false."
+        flag-description-entity-riding="Contrôle si une entité peut être chevauchée.\n&dExemple&f : Pour prévenir les chevaux d'être montés, entre\n&a/cf entity-riding minecraft:horse false."
+        flag-description-entity-spawn="Contrôle si une entité peut apparaître dans le monde.\n&dExemple&f : Pour prévenir les cochons d'apparaîtres, entre\n&a/cf entity-spawn minecraft:pig false\n&bNote&f : Cela n'inclut pas les objets d'entités. Regarde les flags item-spawn"
+        flag-description-entity-teleport-from="Contrôle si une entité peut se téléporter depuis une protection.\n&dExemple&f : Pour prévenir les joueurs de se téléporter depuis la protection, entre\n&a/cf entity-teleport-from player false\n&bNote&f : Si tu veux utiliser ça pour les groupes, utilises la commande /cfg."
+        flag-description-entity-teleport-to="Contrôle si une entité peut se téléporter dans une protection.\n&dExemple&f : Pour prévenir les joueur de se téléporter dans une protection, entre\n&a/cf entity-teleport-to player false\n&bNote&f : Si tu veux utiliser ça pour les groupes, utilises la commande /cfg."
+        flag-description-exit-claim="Contrôle si une entité peut sortir d'une protection.\n&dExample&f : Pour prévenir les joueurs de sortir d'une protection, entre\n&a/cf exit-claim player false\n&bNote&f : Si tu veux utiliser ça pour les groupes, utilises la commande /cfg."
+        flag-description-explosion-block="Contrôle si une explosion peut endommager les blocs dans la protection.\n&dExemple&f : Pour prévenir une explosion d'affecter n'importe quel bloc, entre\n&a/cf explosion-block any false"
+        flag-description-explosion-entity="Contrôle si une explosion peut blesser une entité dans une protection.\n&dExample&f : Pour prévenir une explosion d'affecter n'importe quel entité, entre\n&a/cf explosion-entity any false"
+        flag-description-interact-block-primary="Contrôle si un joueur peut faire un clique-gauche(attaque) un bloc.\n&dExemple&f : Pour prévenir un joueur de faire un clique-gauche sur un coffre, entre\n&a/cf interact-block-primary minecraft:chest false"
+        flag-description-interact-block-secondary="Contrôle si un joueur peut faire un clique-droit sur un bloc.\n&dExemple&f : Pour prévenir un joueur de faire un clique-droit(ouvrir) sur un coffre, entre\n&a/cf interact-block-secondary minecraft:chest false"
+        flag-description-interact-entity-primary="Contrôle si un joueur peut faire un clique-gauche(attaque) sur une entité.\n&dExemple&f : Pour prévenir un joueur de faire un clique-gauche sur une vache, entre\n&a/cf interact-entity-primary minecraft:cow false"
+        flag-description-interact-entity-secondary="Contrôle si un joueur peut faire un clique-droit sur une entité.\n&dExemple&f : Pour prévenir un joueur d'intéragir avec un villageois, entre\n&a/cf interact-entity-secondary minecraft:villager false"
+        flag-description-interact-inventory="Contrôle si un joueur peut faire un clique-droit(ouvrir) un bloc avec un inventaire comme un coffre.\n&dExemple&f : Pour prévenir un joueur d'ouvrir un coffre, entre\n&a/cf interact-inventory minecraft:chest false"
+        flag-description-interact-inventory-click="Contrôle si un joueur peut cliquer sur un emplacement d'inventaire.\n&dExemple&f : Pour prévenir un joueur de cliquer sur un emplacement d'inventaire qui contient un diamant, entre\n&a/cf interact-inventory-click minecraft:diamond false"
+        flag-description-interact-item-primary="Contrôle si un joueur peut clique-gauche(attaque) avec un objet.\n&dExemple&f : Pour prévenir un joueur de clique-gauche avec un épée en diamant, entre\n&a/cf interact-item-primary minecraft:diamond_sword false"
+        flag-description-interact-item-secondary="Contrôle si un joueur peut clique-droit avec un objet.\n&dExample&f : Pour prévenir un joueur de faire un clique droit avec un briquet,  entre\n&a/cf interact-item-secondary minecraft:flint_and_steel false"
+        flag-description-item-drop="Contrôle si un objet peut être lâché dans une protection.\n&dExemple&f : Pour prévenir le diamant d'être lâché par les joueurs dans la protection, entre\n&a/cf item-drop minecraft:flint_and_steel false context[source=player]"
+        flag-description-item-pickup="Contrôle si un item peut être ramassé dans la protection.\n&dExemple&f : Pour prévenir un diamant d'être ramassé par les joueurs, entre\n&a/cf item-pickup minecraft:diamond false"
+        flag-description-item-spawn="Contrôle si un objet peut apparaître dans une protection.\n&dExemple&f : Pour prévenir les plumes d'apparaître dans la protection, entre\n&a/cf item-spawn minecraft:feather false"
+        flag-description-item-use="Contrôle si un objet peut être utilisé.\n&dExemple&f : Pour prévenir les pommes d'être mangées dans la protection, entre\n&a/cf item-use minecraft:apple false"
+        flag-description-leaf-decay="Contrôle si les feuilles peuvent dépérir dans la protection.\n&dExemple&f : Pour prévenir les feuilles de dépérir dans la protection, entre\n&a/cf leaf-decay any false"
+        flag-description-liquid-flow="Contrôle si un liquide, comme la lave ou l'eau, est autorisé à couler dans la protection.\n&dExemple&f : Pour prévenir n'importe quel type de liquide de couler dans la protection, entre\n&a/cf liquid-flow any false"
+        flag-description-portal-use="Contrôle si un portail peut être utilisé.\n&dExemple&f : Pour prévenir seulement les joueurs d'utiliser un portail sans affecter les non joueurs, entre\n&a/cf portal-use any false context[source=player]"
+        flag-description-projectile-impact-block="Contrôle si un projectile peut avoir un impact(collision) avec un bloc.\n&dExemple&f : Pour prévenir les pokéballs de pixelmon d'impacter un bloc, entre\n&a/cf projectile-impact-block any false[source=pixelmon:occupiedpokeball]\n&bNote&f : Cela concerne les choses comme les potions, flèches, lançable, pokéball pixelmon, etc."
+        flag-description-projectile-impact-entity="Contrôle si un projectile peut avoir un impact(collision) avec une entité.\n&dExample&f : Pour prévenir les joueurs de faire un tir de flèche pouvant impacter une entité, entre\n&a/cf projectile-impact-entity minecraft:arrow false[source=player]\n&bNote&f : Cela concerne les choses comme les potions, flèches, lançable, pokéball pixelmon, etc."
+        flag-invalid-context="&cContexte entré '&f{context}&c' invalide pour le flag de base &f{flag}&c."
+        flag-invalid-meta="&cCible meta entrée '&f{value}&c' invalide pour le flag de base &f{flag}&c."
+        flag-invalid-target="&cCible '&f{target}&c' entré invalide pour le flag de base &f{flag}&c."
+        flag-not-found="&cFlag {flag}&c introuvable."
+        flag-not-set="{flag}&f est actuellement non définis.\nLa valeur par défaut {value}&f de la protection sera active jusqu'à définition."
+        flag-overridden="&cÉchec de la définition du flag de protection. Le flag &f{flag}&c a été outrepassé par un admin."
+        flag-override-not-supported="&cLe type de protection {type}&c ne supporte pas les flags outrepassés."
+        flag-reset-success="&aFlags remis par défaut dans la protection avec succès."
+        flag-reset-warning="&6Es-tu sûr de vouloir remettre par défaut les paramètres de la protection ?"
+        flag-set-permission-target="&aDéfinis {type}&a permission &b{permission}&a avec contexte &7{contexts}&a à {value}&a sur &6{target}&a."
+        flag-ui-click-allow="Clique ici pour autoriser ce flag."
+        flag-ui-click-deny="Clique ici pour interdire ce flag."
+        flag-ui-click-remove="Clique ici pour supprimer ce flag."
+        flag-ui-click-toggle="Clique ici pour basculer la valeur de {flag}&f."
+        flag-ui-info-claim="La protection est vérifiée avant les valeurs par défaut. Autorise les propriétaires de protection de spécifier le paramètre de flag dans leurs protections seulement."
+        flag-ui-info-default="Default est le dernier à être vérifié. Protection et Outrepassant prenent la priorité sur cela."
+        flag-ui-info-inherit="Héritage est un flag forcé par la protection parente et ne peut être changé."
+        flag-ui-info-override="Outrepassant est la plus haute priorité et est vérifié avant les valeurs Default et Protection. Permet aux admins d'outrepasser l'ensemble des protections Basiques et Admins."
+        flag-ui-inherit-parent="Ce flag hérite depuis la protection parente {name}&f et ne peut &npas&f être changé."
+        flag-ui-override-no-permission="Ce flag a été outrepassé par un administrateur et ne peut &n&cPAS&f être changé."
+        flag-ui-override-permission="{flag}&f est actuellement &coutrepassé&f par un administrateur.\nClique ici pour supprimer ce flag."
+        flag-ui-return-flags="Retourne au flag"
+        label-accessors=Accédant
+        label-area=Zone
+        label-blocks=Blocs
+        label-builders=Constructeurs
+        label-buy=Achat
+        label-cancel=Annuler
+        label-children=Enfant
+        label-confirm=Confirme
+        label-containers=Conteneur
+        label-context=Contexte
+        label-created=Créé
+        label-displaying=Affiche
+        label-expired=Expiré
+        label-farewell="Message de sortie"
+        label-flag=Flag
+        label-greeting="Message d'accueil"
+        label-group=Groupe
+        label-inherit=Héritage
+        label-location=Location
+        label-managers=Manageurs
+        label-name=Nom
+        label-no=Non
+        label-output=Sortie
+        label-owner=Propriétaire
+        label-permission=Permission
+        label-player=Joueur
+        label-price=Prix
+        label-raid=Raid
+        label-resizable=Redimensionnable
+        label-result=Résultat
+        label-schematic=Patron
+        label-source=Source
+        label-spawn="Point d'apparition"
+        label-target=Cible
+        label-trust=Confiance
+        label-type=Type
+        label-unknown=Inconnu
+        label-user=Utilisateur
+        label-world=Monde
+        label-yes=Oui
+        mode-admin="&aMode protection Administratif activé. Chaque protection créée sera gratuite et éditable par les autres administrateur."
+        mode-basic="&aMode de création protection basique activé."
+        mode-nature="&aPrêt pour restaurer la protection ! Clique-droit sur un bloc pour restaurer, et utilises &f/modebasic&c pour arrêter."
+        mode-subdivision="&aMode Sous-divions. Utilises la pelle pour créer une sous-division dans ta protection existante. Utilises &f/modebasic&a pour sortir."
+        mode-town="&aMode création de Village activé."
+        option-description-abandon-delay="&aLe nombre de jours avant qu'une nouvelle protection créée puisse être abandonnée."
+        option-description-abandon-return-ratio="&aLa portion de bloc de protection basique rendu au joueur quand une protection est abandonnée."
+        option-description-blocks-accrued-per-hour="&aBlocs gagnés par heure.\n&dNote&f: Regarde /playerinfo pour plus d'informations."
+        option-description-chest-expiration="&aNombre de jour d'inactivité avant qu'une protection de coffre automatique expire.\n&dNote&f: Lors de l'expiration, une protection peut soit être restaurée à son état d'origine ou supprimée. Cela dépend de la configuration du serveur. Contacte un administrateur pour plus d'informations."
+        option-description-create-limit="&aNombre maximum de protection par joueur.\n&dNote&f: Mettre une valeur en dessous de 0 donnera illimité."
+        option-description-create-mode="&aLe mode de création de la protection (Zone = 2D, Volume = 3D).\n&dNote&f: &bZone&a affecte seulement les axes x et y.\n&bVolume&a affecte les axes x, y, et z."
+        option-description-economy-block-cost="&aLe montant économique chargé par bloc de protection.\n&dNote&f: La formule de calcul du prix est montant * nombre total de blocs de protection."
+        option-description-economy-block-sell-return="&aLe ratio de retour pour vendre des blocs de protection.\n&dNote&f: La formule de calcul est ratio de retour * nombre total de blocs de protection."
+        option-description-expiration="&aNombre de jour d'inactivité avant que la protection expire.\n&dNote&f: Lors de l'expiration, une protection peut soit être restaurée à son état d'origine ou supprimée. Cela dépend de la configuration du serveur. Contacte un administrateur pour plus d'informations."
+        option-description-initial-blocks="&aLe nombre de blocs de protection qu'a initialement un joueur, par défaut."
+        option-description-max-accrued-blocks="&aLa limite de bloc accrues (dans le temps).\n&dNote&f: Cela ne limite pas les blocs achetés ou donnés par un admin."
+        option-description-max-level="&aLe niveau maximum, sur l'axe y, une protection peut être créée."
+        option-description-max-size-x="&aLa taille maximum de blocs l'axe x peut être."
+        option-description-max-size-y="&aLa taille maximum de blocs l'axe y peut être."
+        option-description-max-size-z="&aLa taille maximum de blocs l'axe z peut être."
+        option-description-min-level="&aLe niveau minimum, sur l'axe y, une protection peut être créée."
+        option-description-min-size-x="&aLa taille minimum de blocs l'axe x peut être."
+        option-description-min-size-y="&aLa taille minimum de blocs l'axe y peut être."
+        option-description-min-size-z="&aLa taille minimum de blocs l'axe z peut être."
+        option-description-player-command="&aUtilisé pour exécuter une commande avec un contexte spécifique."
+        option-description-player-deny-flight="&aUtilisé pour déterminer si un joueur est incapable de fly dans une protection.\n&dNote&f: Cela ne donne pas l'abilité de fly au joueur, ça supprime juste l'abilité si elle a été donnée. Cela donne la meilleurs compatibilité avec les plugins."
+        option-description-player-deny-godmode="&aUtilisé pour déterminer si un joueur est incapable de godmode dans une protection.\n&dNote&f: Cela ne donne pas l'abilité de godmode au joueur, ça supprime juste l'abilité si elle a été donnée. Cela donne la meilleurs compatibilité avec les plugins."
+        option-description-player-deny-hunger="&aUtilisé pour refuser la famine dans une protection.\n&dNote&f: Cela ne donne pas l'abilité de gagner de la famine au joueur, ça supprime l'abilité de cause de la famine si défini. Cela donne la meilleurs compatibilité avec les plugins."
+        option-description-player-gamemode="&aUtilisé pour déterminer le gamemode d'un joueur dans un protection."
+        option-description-player-health-regen="&aUtilisé pour définir le nombre de vies régénérée pour un joueur dans la protection.\n&dNote&f: Si le joueur a la vie au maximum, cela n'aura pas d'effet. \n&dNote&f: Une valeur de&6-1&f désactive cette option."
+        option-description-player-keep-inventory="&aUtilisé pour déterminer si un joueur à le droit de garder son inventaire après la mort dans une protection."
+        option-description-player-keep-level="&aUtilisé pour déterminer si un joueur à le droit de garder son niveau après la mort dans une protection."
+        option-description-player-walk-speed="&aUtilisé pour définir la vitesse de marche dans une protection.\n&dNote&f: Une valeur de &6-1&f désactive cette option."
+        option-description-player-weather="&aUtilisé pour définir la météo d'un joueur dans une protection."
+        option-description-radius-inspect="&aLe rayon de recherche pour les protections à proximité lors de l'inspection."
+        option-description-radius-list="&aLe rayon en blocs utilisés pour lister les protections à proximité."
+        option-description-tax-expiration="&aNombre de jour après ne pas avoir payé les taxes avant que la protection soi mise en demeure.\n&dNote&f: Une mise en demeure signifie que tu n'auras plus accès à la construction ou l'intération avec la protection jusqu'au paiement des taxes."
+        option-description-tax-expiration-days-keep="&aNombre de jour pour garder une protection basique mise en demeure et avant expiration.\n&dNote&f: Lors de l'expiration, une protection peut soit être restaurée à son état d'origine ou supprimée. Cela dépend de la configuration du serveur. Contacte un administrateur pour plus d'informations."
+        option-description-tax-rate="&aLe taux de taxe de la protection.\n&dNote&f: Le taux de taxe est calculé par le nombre de blocs de protection dans les protections basiques."
+        option-invalid-context="&cContexte '&f{context}&c' invalide entré pour l'option &f{option}&c."
+        option-invalid-target="&cCible '&f{target}&c' invalide entrée pour l'option &f{option}&c."
+        option-invalid-value="&cValeur '&6{value}&c' invalide entrée pour l'option &f{option}&c.\n&dNote&f: Cette option accepte uniquement les valeurs &f{type}&c."
+        option-not-found="&cL'option {option}&c n'a pas été trouvée."
+        option-not-set="{option}&f est actuellement non définit.\n&dNote&f: La valeur par défaut {value}&f de l'option sera active jusqu'à définition."
+        option-override-not-supported="&cProtection de type {type}&c ne supporte pas les options outrepassantes."
+        option-player-deny-flight="&cTu n'as pas accès au fly dans cette protection et a été téléporté dans une zone sécurisée au sol."
+        option-reset-success="&aOption de la protection remises par défaut avec succès."
+        option-set-target="&aDéfinis {type}&a de l'option &b{option}&a à {value}&a avec le contexte &7{contexts}&a sur la cible &6{target}&a."
+        option-ui-click-toggle="Clique ici pour changer la valeur de {option}&f."
+        option-ui-inherit-parent="Cette option est héritée depuis la protection parente {name}&f et ne peut &nPAS&f être changée."
+        option-ui-overridden="&cÉchec de la définition de l'option. L'option &f{option}&c a été outrepassée par un admin."
+        option-ui-override-no-permission="Cette option a été outrepassée par un administration et ne peut &n&cPAS&f être changée."
+        owner-admin="un administrateur"
+        permission-access="&cTu n'as pas la permission de &6{player}&c pour accéder à ça."
+        permission-assign-without-having="&cTu n'as pas l'autorisation d'assigner une permission que tu ne possèdes pas."
+        permission-ban-block="&cLe bloc {id}&c a été &l&nBANNIT&c et ne peut être utilisé."
+        permission-ban-entity="&cL'entité {id}&c a été &l&nBANNIT&c et ne peut être utilisé."
+        permission-ban-item="&cL'objet {id}&c a été &l&nBANNIT&c et ne peut être utilisé."
+        permission-build="&cTu n'as pas la permission de &6{player}&c pour construire."
+        permission-build-near-claim="&cTu n'as pas la permission de &6{player}&c de construire à proximité de protection."
+        permission-claim-create="&cTu n'as pas la permission de protéger une zone."
+        permission-claim-delete="&cTu n'as pas la permission de supprimer les protections {type}&c."
+        permission-claim-enter="&cTu n'as pas la permission de rentrer dans cette protection."
+        permission-claim-exit="&cTu n'as pas la permission de sortir de cette protection."
+        permission-claim-ignore="&cTu n'as pas la permission pour ignorer les protections {type}&c."
+        permission-claim-list="&cTu n'as pas la permission pour récupérer les informations concernant les protections d'autres joueurs."
+        permission-claim-manage="&cTu n'as pas la permission pour gérer les protections {type}&c."
+        permission-claim-reset-flags="&cTu n'as pas la permission de remettre par défaut les flags dabs les protections {type}&c."
+        permission-claim-reset-flags-self="&cTu n'as pas la permission pour remettre par défaut les flags dans tes protections."
+        permission-claim-resize="&cTu n'as pas la permission pour redimensionner cette protection."
+        permission-claim-sale="&cTu n'as pas la permission pour vendre cette protection."
+        permission-claim-transfer-admin="&cTu n'as pas la permission de transférer les protections admin."
+        permission-clear="&cNettoyage des permissions de cette protection. Pour le définir pour l'ENSEMBLE des protections, sors de la zone des protections."
+        permission-clear-all="&cSeulement le propriétaire de la protection peut nettoyer l'ensemble des permissions."
+        permission-command-trust="&cTu n'as pas la permission pour utiliser ce type de confiance."
+        permission-cuboid="&cTu n'as pas la permission pour créer/redimensionner les protections basiques en mode 3D."
+        permission-edit-claim="&cTu n'as pas la permission pour éditer cette protection."
+        permission-fire-spread="&cTu n'as pas la permission pour propager le feu dans cette protection."
+        permission-flag-defaults="&cTu n'as pas la permission pour gérer les flags par défaut."
+        permission-flag-overrides="&cTu n'as pas la permission pour gérer les flags outrepassant."
+        permission-flag-use="&cTu n'as pas la permission pour utiliser ce flag."
+        permission-flow-liquid="&cTu n'as pas la permission pour faire couler le liquide dans cette protection."
+        permission-global-option="&cTu n'as pas la permission pour gérer les options globals."
+        permission-grant="&cTu ne peut pas te donner une permission que tu ne possèdes pas toi-même."
+        permission-group-option="&cTu n'as pas la permission pour assigner une option à un groupe."
+        permission-interact-block="&cTu n'as pas la permission de &6{player}&c pour intéragir avec le bloc &d{block}&c."
+        permission-interact-entity="&cTu n'as pas la permission de &6{player}&c pour intéragir avec l'entité &d{entity}&c."
+        permission-interact-item="&cTu n'as pas la permission de &6{player}& pour intéragir avec l'objet &d{item}&c."
+        permission-interact-item-block="&cTu n'as pas la permission d'utiliser l'objet &d{item}&c sur &b{block}&c."
+        permission-interact-item-entity="&cTu n'as pas la permission d'utiliser l'objet &d{item}&c sur &b{entity}&c."
+        permission-inventory-open="&cTu n'as pas la permission de &6{player}&c d'ouvrir &d{block}&c."
+        permission-item-drop="&cTu n'as pas la permission de &6{player}&c pour jeter l'objet &d{item}&c dans cette protection."
+        permission-item-use="&cTu ne peut pas utiliser l'objet &d{item}&c dans cette protection."
+        permission-option-defaults="&cTu n'as pas la permission pour gérer les options par défaut."
+        permission-option-overrides="&cTu n'as pas la permission pour gérer les options outrepassant."
+        permission-option-use="&cTu n'as pas la permission d'utiliser cette option."
+        permission-override-deny="&cL'action que tu essayes d'effectuer a été refusée par un flag outrepassant administrateur."
+        permission-player-admin-flags="&cTu n'as pas la permission de changer un flag sur un joueur admin."
+        permission-player-option="&cTu n'as pas la permission pour assigner une option sur un joueur."
+        permission-player-view-others="&cTu n'as pas la permission pour voir les autres joueurs."
+        permission-portal-enter="&cTu ne peut pas utiliser le portail car tu n'as pas la permission de &6{player}&c d'entrer dans la protection de destination."
+        permission-portal-exit="&cTu ne peut pas utiliser le portail car tu n'as pas la permission de &6{player}&c pour sortir de la protection de destination."
+        permission-protected-portal="&cTu n'as pas la permission d'utiliser les portails dans les protections appartenant à &6{player}&c."
+        permission-trust="&cTu n'as pas la permission de &6{player}&c pour gérer les permissions ici."
+        permission-visual-claims-nearby="&cTu n'as pas la permission pour voir les protections à proximité."
+        player-accrued-blocks-exceeded="&cLe joueur &6{player}&c a un total de &6{total}&c et vas dépasser le maximum autorisé de blocs de protection gagnés s'il est donné un nombre additionnel de &6{amount}&c bloc.\nDescends le nombre ou demande un admin de donner à l'utilisateur un outrepassement."
+        player-remaining-blocks-2d="&aTu peut protéger jusqu'à &6{block-amount}&a blocs supplémentaires."
+        player-remaining-blocks-3d="&aTu peut protéger jusqu'à &6{chunk-amount}&a chunks supplémentaire. &f({block-amount})"
+        playerinfo-ui-abandon-return-ratio="&eAbandonné, Ratio de retour&f : &a{ratio}"
+        playerinfo-ui-block-accrued="&eBlocs gagnés&f : &a{amount}&7(&d{block_amount}&f par heure&7)"
+        playerinfo-ui-block-bonus="&eBlocs bonus&f : &a{amount}"
+        playerinfo-ui-block-initial="&eBloc Initial&f : &a{amount}"
+        playerinfo-ui-block-max-accrued="&eMaximum de blocs gagnés&f : &a{amount}"
+        playerinfo-ui-block-remaining="&eBlocs restants&f : &a{amount}"
+        playerinfo-ui-block-total="&eTotal de Blocs&f : &a{amount}"
+        playerinfo-ui-chunk-total="&eTotal Chunks Protectable&f : %a{amount}"
+        playerinfo-ui-claim-level="&eMin/Niveau de protection max&f : &a{level}"
+        playerinfo-ui-claim-size-limit="&eLimite de taille de protection&f : &a{limit}"
+        playerinfo-ui-claim-total="&eProtections Total&f : &a{amount}"
+        playerinfo-ui-economy-block-available-purchase="&eBlocs restants pour l'achat&f : &a{amount}"
+        playerinfo-ui-economy-block-cost="&ePrix de blocs de protection&f : &a{amount} par bloc"
+        playerinfo-ui-economy-block-sell-return="&ePrix de retour de blocs de proteciton vendus&f : &a{amount} par bloc"
+        playerinfo-ui-last-active="&eDernière Activité&f : {date}"
+        playerinfo-ui-tax-current-rate="&eTaux de taxe actuel de la protection&f : &a{rate}"
+        playerinfo-ui-tax-global-claim-rate="&eTaux de taxe de la protection Global&f : &a{rate}"
+        playerinfo-ui-tax-global-town-rate="&eTaux de taxe de Village Global&f : &a{rate}"
+        playerinfo-ui-tax-total="&eTaxe Total&f : &a{amount}"
+        playerinfo-ui-title="&bInfo joueur"
+        playerinfo-ui-uuid="&eUUID&f : &7{id}"
+        playerinfo-ui-world="&eMonde&f : &7{name}"
+        plugin-command-not-found="&cImpossible de localiser la commande '&a{command}&c' pour le plug-in &b{id}&a."
+        plugin-event-cancel="&cUn plug-in a annulé cette action."
+        plugin-not-found="&cImpossible de localiser le plug-in avec l'id &b{id}&c."
+        plugin-reload="&aGriefDefender a été rechargé."
+        pvp-claim-not-allowed="&aTu n'as pas le droit de PvP dans cette protection."
+        pvp-source-not-allowed="&aTu n'as pas le droit de PvP."
+        pvp-target-not-allowed="&aTu ne peut pas attaquer les joueurs qui ne participent pas au PvP."
+        registry-block-not-found="&cLe bloc {id} ne peut pas être trouvé dans le registre."
+        registry-entity-not-found="&cL'entité {id} ne peut pas être trouvé dans le registre."
+        registry-item-not-found="&cL'objet {id} ne peut pas être trouvé dans le registre."
+        resize-overlap="&cImpossible de redimensionner ici car cela chauvecherait une protection à proximité."
+        resize-overlap-subdivision="&cTu ne peut pas créer une sous-division ici car cela chevaucherait une autre sous-division. Considère &f/abandon&c pour la supprimer ou utilise la pelle sur un coin pour le redimensionner."
+        resize-same-location="&cTu dois sélectionner un bloc à un endroit différent pour redimensionner une protection."
+        resize-start="&aRedimensionnement de la protection. Utilise la pelle à nouveau au nouvel endroit pour ce coin."
+        resize-success-2d="&aProtection redimensionnée. Tu as encore &6{amount} &ablocs restants."
+        resize-success-3d="&aProtection redimensionnée. Tu as encore &6{amount} &achunks restants. &f({block-amount})"
+        result-type-change-deny="&cTu ne peut pas changer une protection en {type}."
+        result-type-change-not-admin="&cTu n'as pas la permission d'un administrateur pour changer le type en {type}&c."
+        result-type-child-same="Protection {type}&c ne peuvent pas avoir directement des protections enfant de type {type}&c."
+        result-type-create-deny="{type}&c ne peut pas être créé dans {target_type}."
+        result-type-no-children="{type}&c ne peut pas contenir de protection enfant."
+        result-type-only-subdivision="{type}&c peut seulement contenir des sous-division."
+        result-type-requires-owner="&cImpossible de convertir la protection {type} en {target_type}. Le propriétaire est requis."
+        schematic-abandon-all-restore-warning="&6Es-tu sûr de vouloir &nabandonner&6 &cTOUTES&6 tes protections ? &cL'ENSEMBLE DES DONNÉES SERONT PERDUES&f !&6 Tes protections seront restorées à leur état d'origine lors de la confirmation."
+        schematic-abandon-restore-warning="&6Es-tu sûr de vouloir &nabandonner&6 cette protection ? &cL'ENSEMBLE DES DONNÉES SERONT PERDUES&f !&6 Cette protection sera restorée à son état d'origine lors de la confirmation."
+        schematic-create="&aCréation d'une sauvegarde du patron..."
+        schematic-create-complete="&aSauvegarde du patron complète."
+        schematic-create-fail="&cLe patron n'a pas pu être créé."
+        schematic-deleted="&aLe patron {name} a été supprimé."
+        schematic-none="&aIl n'y a pas de patron de sauvegardé pour cette protection."
+        schematic-restore-click="&aCLique ici pour restaurer le patron de la protection.\nNom: {name}\nCréé: {date}"
+        schematic-restore-confirmation="&6Es-tu sûr de vouloir restaurer ? Clique confirme va restaurer l'&ENSEMBLE&6 des données de la protection avec le patron. Utilisation avec prudence !"
+        schematic-restore-confirmed="&aTu as restauré la protection depuis le patron sauvegardé &b{name}&a avec succès."
+        spawn-not-set="&cPas de point d'apparition de la protection définis."
+        spawn-set-success="&aDéfinition du point d'apparition à &b{location}&a avec succès."
+        spawn-teleport="&aTéléportation au point d'apparition de la protection à &b{location}&a."
+        tax-claim-expired="&cCette protection a été mise en demeure à cause de taxes impayées. Le montant actuel du est de '&a{amount}&c'.\nIl reste '&a{days}&c' jours pour effectuer le dépôt de paiement à la banque de protection pour lever la mise en demeure.\nNe pas payer cette dette aura pour conséquence la suppression de la protection.\nNote: Pour déposer des fonds dans la banque de protection, utilises &f/claimbank&c deposit <nombre>."
+        tax-claim-paid-balance="&aLa dette de taxe de '&6{amount}&a' a été payée. La mise en demeure a été levée et la protection est disponible pour usage."
+        tax-claim-paid-partial="&aLa dette de taxe de '&6{amount}&a' a été partiellement payée. Pour lever la mise en demeure de la protection, le reste de la taxe due de '&6{balance}&a' doit être payé."
+        tax-info="&aTon prélèvement de taxe d'un montant de &6{amount}&a va être prélevé depuis ton compte le &b{date}&a."
+        tax-past-due="&cTu as actuellement un défaut de paiement passé de taxe de &a{balance}&c qui nécessite d'être payé pour le &b{date}&c. L'échec de paiement de cette taxe entrainera la perte de la propriété."
+        teleport-confirm="&aEs-tu sûr de vouloir te téléporter à {pos}? Clique confirme pour procéder."
+        teleport-delay-notice="&aTu seras téléporté dans {delay} secondes. Clique sur annuler pour arrêter."
+        teleport-move-cancel="&cTéléportation annulée ! Tu ne peux pas bouger lors d'une tentative de téléportation."
+        teleport-no-safe-location="&cPas de zone sécurisée trouvée dans la protection pour se téléporter !\n&aClique confirm pour téléporter malgré tout ou &autilise la commande '&f/claiminfo&a' pour définir une un point d'apparition sécurisé à la place."
+        teleport-success="&aTu as été téléporté à {name}&a."
+        title-accessor=ACCÉDANT
+        title-all=TOUS
+        title-builder=CONSTRUTEUR
+        title-claim=PROTECTION
+        title-container=CONTENEUR
+        title-default=DEFAUT
+        title-inherit=HÉRITAGE
+        title-manager=GÉRANT
+        title-override=OUTREPASSANT
+        title-own=POSSÈDE
+        tool-not-equipped="&cTu n'as pas {tool}&c équipé."
+        town-chat-disabled="&aChat de Village désactivé."
+        town-chat-enabled="&aChat de Village activé."
+        town-create-not-enough-funds="&cTy n'as pas suffisamment de fonds pour créer un village pour &a{amount}&c. Tu as actuellement un solde de &a{balance}&c et a besoin de &a{amount-needed}&c supplémentaire pour la création."
+        town-name="&aDéfinis le nom du village à {name}&a."
+        town-not-found="&cVillage non trouvé."
+        town-not-in="&cTu n'es pas dans un Village."
+        town-owner="&cCela appartient au village."
+        town-tag="&aDéfinis le blason du village à {tag}."
+        town-tag-clear="&aLe blason du village a été supprimé."
+        town-tax-no-claims="&cTu dois avoir une propriété dans ce village pour être taxé."
+        trust-already-has="&c{target} a déjà la permission {type}&c."
+        trust-click-show-list="Clique ici pour afficher la liste de l'ensemble des players et groupes ayant la confiance dans cette protection."
+        trust-grant="&aDonne à &6{target}&a la permission de {type}&a dans la protection actuelle."
+        trust-individual-all-claims="&aDonne &6{player}'s&a total confiance dans l'ENSEMBLE de tes protections. Pour supprimer les permissions dans l'ENSEMBLE de tes protections, utilises &f/untrustall&a."
+        trust-invalid="&cType de confiance invalide entré.\nLes types autorisés sont : accessor, builder, container, et manager."
+        trust-list-header="Permission explicite ici :"
+        trust-no-claims="&cTu n'as pas de protection pour donner Confiance."
+        trust-plugin-cancel="&cImpossible d'avoir Confiance en {target}&c. Un plug-in l'a refusé."
+        trust-self="&cTu ne peut pas te faire Confiance à toi-même."
+        tutorial-claim-basic="&eClique pour l'aide sur la protection: &ahttp://bit.ly/mcgpuser"
+        ui-click-confirm="Clique pour confirmer."
+        ui-click-filter-type="Clique ici pour filtrer par {type}&f."
+        untrust-individual-all-claims="&aRévoque &6{target}&a accès à l'ENSEMBLE de tes protections.  Pour définir la permission pour une seule protection, va dedans et utilises &f/untrust&a."
+        untrust-individual-single-claim="&aRévoque &6{target}&a accès à cette protection.  Pour définir la permission pour l'ENSEMBLE de tes protections, utilises &f/untrustall&a."
+        untrust-no-claims="&cTu n'as pas de protection où enlever la confiance."
+        untrust-owner="&6{owner}&a est le propriétaire et ne pas pas perdre la confiance."
+        untrust-self="&cTu ne peut pas te retirer la confiance toi-même."
+    }
+}
diff --git a/sponge/src/main/resources/assets/lang/ru_RU.conf b/sponge/src/main/resources/assets/lang/ru_RU.conf
new file mode 100644
index 0000000..0583ea8
--- /dev/null
+++ b/sponge/src/main/resources/assets/lang/ru_RU.conf
@@ -0,0 +1,634 @@
+GriefDefender {
+    descriptions {
+        abandon-all="Удалить ВСЕ ваши регионы."
+        abandon-claim="Удалить регион."
+        abandon-top="Удалить регион верхнего уровня."
+        buy-blocks="Увеличить за плату серверной валютой количество доступных блоков региона.\nВнимание: требует плагин для экономики."
+        callback="Запустить функцию, зарегистрированную как часть текстового объекта. Предназначена для внутреннего использования."
+        claim-bank="Используется для снятия или начисления денег для использования в регионе."
+        claim-clear="Позволяет очистить сущности в одном или нескольких регионах."
+        claim-debug="Переключить режим отладки регионов."
+        claim-farewell="Установить сообщение при выходе из региона."
+        claim-greeting="Установить сообщение при входе в регион."
+        claim-ignore="Переключить режим игнорирования разрешений в регионах."
+        claim-info="Вывести всю доступную информацию о регионе, в котором вы находитесь."
+        claim-inherit="Переключить наследование текущим регионом разрешений от родителя(ей)."
+        claim-list="Вывести все регионы в заданной области."
+        claim-name="Установить имя региона."
+        claim-restore="Восстановить регион в его начальное состояние. Использовать с осторожностью."
+        claim-setspawn="Установить точку возрождения игроков в этом регионе."
+        claim-spawn="Телепортироваться в точку возрождения игроков в этом регионе, если она задана."
+        claim-transfer="Передать обычный или администраторский регион другому игроку."
+        claim-worldedit="Создать регион, используя выделенную при помощи WorldEdit область."
+        cuboid="Переключить режим создания регионов - кубоид/чанк."
+        debug="Собирать все действия GD для отладки."
+        delete-all="Удалить все регионы другого игрока."
+        delete-all-admin="Удалить все администраторские регионы."
+        delete-claim="Удалить регион, в котором вы находитесь, даже если он вам не принадлежит."
+        delete-top="Удалить регион, в котором вы находитесь, вместе с его суб-регионами, даже если он вам не принадлежит."
+        flag-claim="Вывести/Настроить флаги в регионе, в котором вы находитесь."
+        flag-group="Вывести/Настроить разрешения флагов для группы в регионе, в котором вы находитесь."
+        flag-player="Вывести/Настроить разрешения флагов для игрока в регионе, в котором вы находитесь."
+        flag-reset="Установить значения по умолчанию для всех флагов в регионе."
+        mode-admin="Переключить лопату в режим создания администраторских регионов."
+        mode-basic="Переключить лопату в режим создания обычных регионов."
+        mode-nature="Переключить лопату в режим восстановления."
+        mode-subdivision="Переключить лопату в режим создания суб-регионов."
+        mode-town="Переключить лопату в режим создания городских регионов."
+        option-claim="Вывести/Настроить опции в регионе, в котором вы находитесь."
+        permission-group="Установить разрешение для группы в контексте региона."
+        permission-player="Установить разрешение для игрока в контексте региона."
+        player-adjust-bonus-blocks="Обновить общее количество бонусных блоков региона для игрокв."
+        player-info="Показать информацию об игроке."
+        player-set-accrued-blocks="Обновить общее количество накопленных блоков региона для игрока."
+        reload="Перезагрузить настройки GriefDefender."
+        schematic="Управление резервными копиями региона. Воспользуйтесь '/claimschematic create <name>', чтобы создать резервную копию региона."
+        sell-blocks="Продать блоки региона за серверную валюту.\nПримечание: требует плагин на экономику."
+        sell-claim="Выставить ваш регион на продажу. Воспользуйтесь /claimsell amount.\nПримечание: требует плагин на экономику."
+        town-chat="Переключить чат города."
+        town-tag="Установить собственный тег для города."
+        trust-group="Выдать группе доступ к вашему региону.\nДоступ: доступ к взаимодействию со всем, кроме инвентарей.\nКонтейнеры: доступ к взаимодействию со всем, включая инвентари.\nСтроительство: доступ ко всему вышеперечисленному плюс установка и поломка блоков.\nУправление: доступ ко всему вышеперечисленному плюс возможность изменять настройки региона."
+        trust-group-all="Выдать группе доступ ко ВСЕМ вашим регионам.\nДоступ: доступ к взаимодействию со всем, кроме инвентарей.\nКонтейнеры: доступ к взаимодействию со всем, включая инвентари.\nСтроительство: доступ ко всему вышеперечисленному плюс установка и поломка блоков.\nУправление: доступ ко всему вышеперечисленному плюс возможность изменять настройки региона."
+        trust-player="Выдать игроку доступ к вашему региону.\nДоступ: доступ к взаимодействию со всем, кроме инвентарей.\nКонтейнеры: доступ к взаимодействию со всем, включая инвентари.\nСтроительство: доступ ко всему вышеперечисленному плюс установка и поломка блоков.\nУправление: доступ ко всему вышеперечисленному плюс возможность изменять настройки региона."
+        trust-player-all="Выдать игроку доступ ко ВСЕМ вашим регионам.\nДоступ: доступ к взаимодействию со всем, кроме инвентарей.\nКонтейнеры: доступ к взаимодействию со всем, включая инвентари.\nСтроительство: доступ ко всему вышеперечисленному плюс установка и поломка блоков.\nУправление: доступ ко всему вышеперечисленному плюс возможность изменять настройки региона."
+        untrust-group="Отозвать доступ группы к вашему региону."
+        untrust-group-all="Отозвать доступ группы ко ВСЕМ вашим регионам."
+        untrust-player="Отозвать доступ игрока к вашему региону."
+        untrust-player-all="Отозвать доступ игрока ко ВСЕМ вашим регионам."
+        version="Вывести информацию о версии GriefDefender."
+    }
+    messages {
+        abandon-all-warning="&6Вы уверены, что хотите удалить &cВСЕ&6 ваши регионы?"
+        abandon-claim-delay-warning="&aЭтот регион был создан недавно и не может быть удалён до &6{date}&a."
+        abandon-claim-failed="&aНе удалось удалить регион. Результат действия: &f{result}&a."
+        abandon-claim-missing="&cНе найдено ни одного региона. Войдите в регион, который вы хотите удалить, или воспользуйтесь &f/abandonall&c."
+        abandon-other-success="&aРегион игрока &6{player}&a удалён. Теперь &6{player}&a имеет &6{amount}&a доступных для занятия блоков."
+        abandon-success="&aРегион удалён. Теперь у вас есть &6{amount}&a доступных для занятия блоков."
+        abandon-top-level="&cРегион не может быть удалён, поскольку в нём есть один или несколько суб-регионов. Чтобы удалить регион вместе с суб-регионами, воспользуйтесь &f/abandontop&c."
+        abandon-town-children="&cУ вас нет разрешения на удаление города, в котором есть суб-регионы, не принадлежащие вам. Воспользуйтесь &f/ignoreclaims&c или попросите владельцев суб-регионов удалить их. Если вы хотите удалить город, но не суб-регионы, воспользуйтесь &f/abandon&c."
+        abandon-warning="&6Вы уверены, что хотите удалить этот регион? Он больше не будет защищён."
+        adjust-accrued-blocks-success="&aКоличество накопленных блоков региона для &6{player}&a изменено на &6{amount}&a. Новое количество накопленных блоков: &6{total}&a."
+        adjust-bonus-blocks-success="&aКоличество бонусных блоков региона для &6{player}&a изменено на &6{amount}&a. Новое количество бонусных блоков: &6{total}&a."
+        bank-click-view-transactions="Нажмите здесь, чтобы просмотреть историю банковских переводов"
+        bank-deposit="&aУспешно передано &6{amount}&a в банк."
+        bank-deposit-no-funds="&cУ вас недостаточно средств для взноса в банк."
+        bank-info="&aБаланс: &6{balance}&a\nНалог: &6{tax-amount}&f. Срок сдачи &7{time-remaining}&a\nЗадолженность: &6{tax-balance}."
+        bank-no-permission="&cУ вас нет разрешения от пользователя &6{player}&c на управление банком этого региона."
+        bank-tax-system-disabled="&cСистема банков/налогов выключена. Если вы хотите её включить - измените значение поля 'bank-tax-system' в настроечном файле на true."
+        bank-title-transactions="Банковские переводы"
+        bank-withdraw="&aУспешно списано &6{amount}&a из банка."
+        bank-withdraw-no-funds="&cНа счету банка региона сейчас &a{balance}&c, поэтому с него нельзя списать &a{amount}&c."
+        block-claimed="&aЭтот блок занят пользователем &6{player}&a."
+        block-not-claimed="&cЭтот блок никем не занят."
+        block-sale-value="&aКаждый блок региона стоит &6{amount}&a. Вы можете продать &6{total}&a блоков."
+        claim-above-level="&cВы не можете занять этот блок, потому что он выше лимита по уровню региона - &a{limit}&c."
+        claim-action-not-available="&cЭто действие недоступно в регионах вида {type}&c."
+        claim-automatic-notification="&cЭтот сундук и блоки поблизости защищёны от поломки и ограбления."
+        claim-below-level="&cВы не можете занять этот блок, потому что он ниже лимита по уровню региона - &a{limit}&c."
+        claim-chest-confirmation="&cЭтот сундук защищён."
+        claim-chest-outside-level="&cЭтот сундук нельзя защитить, потому что он находится вне доступных вам границ по уровню региона - &a{min-level}&c и &a{max-level}&c."
+        claim-children-warning="&6У данного региона есть суб-регионы.  Если вы уверены, что хотите удалить его, используйте &f/deleteclaim&6 ещё раз."
+        claim-context-not-found="&cКонтекст &f{context}&c не найден."
+        claim-disabled-world="&cВ этом мире нельзя создать регион."
+        claim-expired-inactivity="&cРегион игрока &6{player} с id &f{uuid}&c был удалён ввиду отсутствия активности."
+        claim-farewell="&aСообщение при выходе из региона установлено: &f{farewell}&a."
+        claim-farewell-clear="&aСообщение при выходе из региона удалено."
+        claim-farewell-invalid="&cФлаг региона &f{flag}&c не существует."
+        claim-greeting="&aПриветствие при входе в регион установлено: &f{greeting}&a."
+        claim-greeting-clear="&aПриветствие при входе в регион удалено."
+        claim-ignore="&aИгнорирование регионов включено."
+        claim-last-active="&aВ последний раз активность в этой области зарегистрирована &6{date}&a."
+        claim-mode-start="{type}&a угол выбран! Нажмите ПКМ ещё раз на противоположный угол, чтобы создать прямоугольный регион. Для отмены и выхода из режима наберите &f/claim&a."
+        claim-name="&aУстановлено имя региона: &6{name}&a."
+        claim-no-claims="&cУ вас нет регионов."
+        claim-no-set-home="&cВы должны быть вписаны в регион, чтобы использовать /sethome."
+        claim-not-found="&cЗдесь нет ни одного региона."
+        claim-not-yours="&cЭто не ваш регион."
+        claim-owner-already="&cВы уже являетесь владельцем этого региона."
+        claim-owner-only="&cТолько &6{player}&c может редактировать этот регион."
+        claim-protected-entity="&cЭто принадлежит игроку &6{player}&c."
+        claim-respecting="&aИгнорирование регионов выключено."
+        claim-restore-success="&aРегион успешно восстановлен."
+        claim-show-nearby="&aПоблизости найдено &6{amount}&a регионов."
+        claim-size-max="&cРазмер региона по оси &6{axis}&c &a({size})&c больше максимального допустимого размера &a{max-size}&c.\nОбласть должна быть минимум &a{min-area}&c и максимум &a{max-area}."
+        claim-size-min="&cРазмер региона по оси &6{axis}&c &a({size})&c меньше минимального допустимого размера &a{min-size}&c.\nОбласть должна быть минимум &a{min-area}&c и максимум &a{max-area}."
+        claim-size-need-blocks-2d="&cУ вас недостаточно блоков, чтобы создать регион этого размера.\nВам нужно ещё &a{block-amount}&c блоков."
+        claim-size-need-blocks-3d="&cУ вас недостаточно блоков, чтобы создать регион этого размера.\nВам нужно ещё &a{chunk-amount}&c чанков. &f({block-amount} блоков)"
+        claim-size-too-small="&cРазмер выделенной области - &a{width}&fx&a{length}&c - слишком мал. Регион должен иметь размер как минимум &a{min-width}&fx&a{min-length}&c."
+        claim-start="&aУгол региона вида &f{type}&a установлен! Используйте лопату на противоположном углу, чтобы создать прямоугольный регион.  Для отмены уберите лопату."
+        claim-too-far="&cСлишком далеко."
+        claim-transfer-exceeds-limit="&cНе удалось передать регион - у нового владельца слишком много регионов."
+        claim-transfer-success="&aРегион передан."
+        claim-type-not-found="&cРегионов вида &f{type}&c не найдено."
+        claiminfo-ui-admin-settings="Администраторские настройки"
+        claiminfo-ui-bank-info="Банковская информация"
+        claiminfo-ui-claim-expiration="Истечение срока действия региона"
+        claiminfo-ui-click-admin="Нажмите, чтобы отобразить администраторские настройки"
+        claiminfo-ui-click-bank="Нажмите, чтобы проверить банковскую информацию"
+        claiminfo-ui-click-change-claim="Нажмите, чтобы изменить вид региона на {type}"
+        claiminfo-ui-click-toggle="Нажмите, чтобы переключить значение"
+        claiminfo-ui-deny-messages="Сообщения об отклонении"
+        claiminfo-ui-flag-overrides="Переопределения флагов"
+        claiminfo-ui-for-sale=Продаётся
+        claiminfo-ui-inherit-parent="Наследуется от родителя"
+        claiminfo-ui-last-active="Последняя активность"
+        claiminfo-ui-north-corners="Северные углы"
+        claiminfo-ui-pvp-override="Переопределение PvP"
+        claiminfo-ui-requires-claim-blocks="Требует блоки региона"
+        claiminfo-ui-return-bankinfo="Вернуться к банковской информации"
+        claiminfo-ui-return-claiminfo="Вернуться к информации о регионе"
+        claiminfo-ui-return-settings="Вернуться к стандартным настройкам"
+        claiminfo-ui-size-restrictions="Ограничения по размеру"
+        claiminfo-ui-south-corners="Южные углы"
+        claiminfo-ui-teleport-direction="Нажмите, чтобы телепортироваться на {direction}&f угол региона"
+        claiminfo-ui-teleport-feature="У вас нет разрешения на использование функции телепортации в этом регионе"
+        claiminfo-ui-teleport-spawn="Нажмите, чтобы телепортироваться на точку возрождения в регионе"
+        claiminfo-ui-title-claiminfo="Информация о регионе"
+        claiminfo-ui-town-settings="Настройки города"
+        claimlist-ui-click-info="Нажмите, чтобы увидеть больше информации"
+        claimlist-ui-click-purchase="Нажмите, чтобы приобрести регион"
+        claimlist-ui-click-teleport-target="Нажмите, чтобы телепортироваться к {name}&f {target}&f в &6{world}"
+        claimlist-ui-click-toggle-value="Нажмите, чтобы переключить значение вида {type}"
+        claimlist-ui-click-view-children="Нажмите, чтобы отобразить список регионов"
+        claimlist-ui-click-view-claims="Нажмите, чтобы отобразить ваши регионы"
+        claimlist-ui-no-claims-found="В мире регионов не найдено."
+        claimlist-ui-return-claimlist="Вернуться к списку регионов"
+        claimlist-ui-title="Список регионов"
+        claimlist-ui-title-child-claims=Суб-регионы
+        command-blocked="&cИспользование команды &f{command}&c заблокировано игроком &6{player}&c, владельцем региона."
+        command-claimban-success-block="&aБлок с id &c{id}&a успешно &cЗАБАНЕН&a."
+        command-claimban-success-entity="&aСущность с id &c{id}&a успешно &cЗАБАНЕНА&a."
+        command-claimban-success-item="&aПредмет с id &c{id}&a успешно &cЗАБАНЕН&a."
+        command-claimbuy-title="&bРегионы в продаже"
+        command-claimclear-killed="&cУбито &6{amount}&a сущностей вида {type}&f."
+        command-claimclear-no-entities="&cНе найдено ни одной сущности вида {type}&c."
+        command-claimclear-uuid-deny="&cТолько администраторы могут удалять регионы при помощи UUID."
+        command-claimflagdebug-disabled="Отладка флагов региона &cВЫКЛЮЧЕНА"
+        command-claimflagdebug-enabled="Отладка флагов региона &aВКЛЮЧЕНА"
+        command-claiminfo-not-found="&cНе найдено валидных UUID игрока или региона."
+        command-claiminfo-uuid-required="&cПри запуске не от имени игрока требуется UUID региона ."
+        command-claiminherit-disabled="Наследование от родителя &cВЫКЛЮЧЕНО"
+        command-claiminherit-enabled="Наследование от родителя &aВКЛЮЧЕНО"
+        command-claimspawn-not-found="&aНе удалось найти регион с именем {name}&a."
+        command-claimunban-success-block="&aБлок с id &6{id}&a успешно &6РАЗБАНЕН&a."
+        command-claimunban-success-entity="&aСущность с id &6{id}&a успешно &6РАЗБАНЕНА&a."
+        command-claimunban-success-item="&aПредмет с id &6{id}&a успешно &6РАЗБАНЕН&a."
+        command-cuboid-disabled="&aВключён режим создания &d2D&a-регионов."
+        command-cuboid-enabled="&aВключён режим создания &d3D&a-регионов."
+        command-execute-failed="&cНе удалось выполнить команду '{command} {args}'."
+        command-giveblocks-confirmation="&6Вы уверены, что хотите отдать игроку {player}&6 {amount}&6 блоков региона?"
+        command-giveblocks-confirmed="&aПередача блоков региона завершена."
+        command-giveblocks-not-enough="&cНе достаточно блоков региона! У вас есть только {amount}&c блоков региона, доступных к передаче.\n&bПримечание&f: Это количество не включает начальные блоки региона. Только накопленные и бонусные."
+        command-giveblocks-received="&aВам передано {amount}&a блоков региона от {player}&a."
+        command-inherit-only-child="&cЭту команду можно использовать только в суб-регионах."
+        command-invalid="&cКоманда не найдена."
+        command-invalid-amount="&cВведено неверное количество: &6{amount}&c."
+        command-invalid-claim="&cЭту команду нельзя использовать в регионах вида &f{type}&c."
+        command-invalid-group="&cГруппа &6{group}&c не существует."
+        command-invalid-player="&cИгрок &6{player}&c не найден."
+        command-invalid-player-group="&cНе является игроком или группой."
+        command-invalid-type="&cТип {type}&c не найден."
+        command-not-available-economy="&cЭта команда не доступна, пока сервер находится в режиме экономики."
+        command-option-exceeds-admin="&cЗначение опции &a'{value}&c' выходит за установленное администратором - '&a{admin-value}&c'. Применяю установленное администратором значение..."
+        command-pet-confirmation="&aПитомец передан."
+        command-pet-invalid="&cПередача питомца вида {type} не поддерживается."
+        command-pet-transfer-cancel="&aПередача питомца отменена."
+        command-pet-transfer-ready="&aГотов к передаче!  Нажмите на питомца, которого хотите передать, или нажмите ЛКМ для отмены."
+        command-player-not-found="&cИгрок '&6{player}&c' не найден."
+        command-world-not-found="&cМир '&6{world}&c' не найден."
+        command-worldedit-missing="&cДля использования этой команды на сервер должен быть установлен WorldEdit."
+        create-cancel="&cСоздание региона отменено."
+        create-cuboid-disabled="&cВозможность создавать регионы в форме &d3D&c-кубоида отключено администратором.\nВы можете создавать регионы в форме &d3D&c-кубоида, только если вы являетесь администратором или поверх вашего &d2D&c-региона."
+        create-failed-claim-limit="&cВы достигли ограничения в &a{limit}&c по количеству регионов вида &a{type}&c. Используйте &f/abandon&c, чтобы удалить существующий регион."
+        create-failed-result="&aНе удалось создать регион: &6{reason}&a."
+        create-insufficient-blocks-2d="&cУ вас недостаточно блоков для создания региона.\nВам нужно ещё &a{block-amount}&c блоков."
+        create-insufficient-blocks-3d="&cУ вас недостаточно блоков для создания региона.\nВам нужно ещё &a{chunk-amount}&c чанков. &f({block-amount} блоков)"
+        create-overlap="&cВы не можете создать здесь регион, потому что он будет пересекаться с другим вашим регионом. Воспользуйтесь &f/abandonclaim&c, чтобы удалить его, или используйте лопату на его угол, чтобы изменить его размер."
+        create-overlap-player="&cВы не можете создать здесь регион, потому что он будет пересекаться с регионом, принадлежащим игроку &6{player}&c."
+        create-overlap-short="&cВыбранная область пересекается с существующим регионом."
+        create-subdivision-fail="&cВ выбранной точке нет ни одного региона. Пожалуйста, кликните по блоку, находящемуся в родительском регионе, чтобы создать суб-регион."
+        create-subdivision-only="&cНе удалось создать регион. Только суб-регионы могут быть объёмом в один блок."
+        create-success="{type}&a регион создан!  Используйте &f/trust&a, чтобы впустить в него друзей."
+        debug-error-upload="&cОшибка загрузки контента {content}&c."
+        debug-no-records="&cНет отладочной записи!"
+        debug-paste-success="&aВставка успешна!"
+        debug-record-end="Запись закончена"
+        debug-record-start="Запись начата"
+        debug-time-elapsed="Затраченное время"
+        delete-all-player-failed="&aНе удалось удалить все регионы, принадлежащие игроку &6{player}&a. Результат действия: &f{result}&a."
+        delete-all-player-success="&aУдалены все регионы, принадлежавшие игроку &6{player}&a."
+        delete-all-player-warning="&6Вы уверенны, что хотите удалить все регионы, принадлежащие игроку &a{player}&6?"
+        delete-all-type-deny="&cНе удалось удалить все регионы вида &6{type}&c. Плагин не разрешил."
+        delete-all-type-success="&cВсе регионы вида &6{type}&c удалены."
+        delete-all-type-warning="&6Вы уверены, что хотите удалить все регионы вида &c{type}&6?"
+        delete-claim-failed="&aНе удалось удалить регион. Результат действия: &f{result}&a."
+        delete-claim-success="&aУспешно удалён регион, принадлежавший игроку &f{player}&a."
+        delete-claim-warning="&6Вы уверены, что хотите удалить регион, принадлежащий игроку &f{player}&6?"
+        economy-balance="&aВаш новый баланс: &6{balance}&a."
+        economy-block-available-purchase-2d="&aУ вас достаточно средств, чтобы создать регион ещё на &6{block-amount}&a блоков."
+        economy-block-available-purchase-3d="&aУ вас достаточно средств, чтобы создать регион ещё на &6{chunk-amount}&a чанков. &f({block-amount} блоков)"
+        economy-block-buy-invalid="&cКоличество блоков должно быть больше 0."
+        economy-block-buy-sell-disabled="&cПросим прощения, но покупка и продажа блоков региона отключена."
+        economy-block-cost="&aКаждый блок региона стоит &6{amount}&a."
+        economy-block-not-available="&cУ вас недостаточно блоков региона для продажи."
+        economy-block-only-buy="&cБлоки региона можно только купить, но не продать."
+        economy-block-only-sell="&cБлоки региона можно только продать, но не купить."
+        economy-block-purchase-confirmation="&aС вашего счёта списано &6{amount}&a. Теперь у вас доступно &6{balance}&a блоков региона."
+        economy-block-purchase-cost="&aКаждый блок региона стоит &6{amount}&a. Ваш баланс: &6{balance}&a."
+        economy-block-purchase-limit="&cНовое количество доступных блоков региона &a{total}&c превышает ограничение в &a{limit}&c. Операция отменена."
+        economy-block-sale-confirmation="&aНа ваш счёт начислено &6{deposit}&a. Теперь у вас доступно &6{amount}&a блоков региона."
+        economy-block-sell-error="&cНе удалось продать блоки. Причина: &f{reason}&c."
+        economy-claim-abandon-success="&aРегион(ы) удалены. Вам возмещены средства в размере '&6{amount}&a'."
+        economy-claim-buy-cancelled="&cПокупка отменена! Не удалось купить регион, принадлежащий &6{player}&c. Результат действия: &a{result}"
+        economy-claim-buy-confirmation="&6Вы уверены, что хотите купить этот регион за &a{amount}&6? Нажмите &aПодтвердить&6 для продолжения."
+        economy-claim-buy-confirmed="&aВы успешно приобрели регион за &6{amount}&a."
+        economy-claim-buy-not-enough-funds="&cУ вас недостаточно средств, чтобы приобрести регион за &a{amount}&c. На вашем счету сейчас &a{balance}&c и для покупки нужно ещё &a{amount_required}&c."
+        economy-claim-buy-transfer-cancelled="&cПередача региона отменена! Не удалось передать регион от &6{owner}&c игроку &6{player}&c. Результат действия: &a{result}"
+        economy-claim-not-for-sale="&cЭтот регион не продаётся."
+        economy-claim-sale-cancelled="&aВы отменили продажу вашего региона."
+        economy-claim-sale-confirmation="&6Вы уверены, что хотите продать свой регион за &a{amount}&6? Если регион купят, покупателю перейдут все находящиеся в нём блоки и предметы. Нажмите Подтвердить, если вы согласны с этим."
+        economy-claim-sale-confirmed="&aРегион выставлен на продажу по стоимости &6{amount}&a."
+        economy-claim-sale-invalid-price="&cСтоимость региона &a{amount}&c должна быть больше или равна &a0&c."
+        economy-claim-sold="&aВаш регион продан! &6{amount}&a было записано на ваш счёт. Ваш текущий баланс: &6{balance}&a."
+        economy-mode-block-sale-confirmation="&aНа ваш счёт зачислено &6{deposit}&a. Ваш текущий баланс: &6{balance}&a. Теперь у вас достаточно средств, чтобы создать регион ещё на &6{amount}&a блоков."
+        economy-mode-resize-success-2d="&aРазмер региона изменён. Ваш новый баланс: &6{balance}&a. У вас достаточно средств, чтобы создать регион ещё на &6{block-amount}&a блоков."
+        economy-mode-resize-success-3d="&aРазмер региона изменён. Ваш новый баланс: &6{balance}&a. У вас достаточно средств, чтобы создать регион ещё на &6{chunk-amount}&a чанков. &f({block-amount} блоков)"
+        economy-not-enough-funds="&cУ вас недостаточно средств на покупку этого региона. Ваш текущий баланс: '&a{balance}&c', а для покупки необходимо '&a{amount}&c'."
+        economy-not-installed="&cПлагин на экономику не установлен!"
+        economy-player-not-found="&cСчёт пользователя &6{player}&c не найден."
+        economy-remaining-funds="&aДля создания регионов вам доступно &6{amount}&a."
+        economy-virtual-not-supported="&cПлагин на экономику не поддерживает виртуальные счета. Воспользуйтесь другим плагином или свяжитесь с разработчиком вашего плагина, чтобы он добавил в него поддержку виртуальных счетов."
+        economy-withdraw-error="&cНе удалось списать средства: &f{reason}&c."
+        feature-not-available="&cДанный функционал не закончен и будет доступен в новой версии."
+        flag-description-custom-block-break="Управляет возможностью ломать блоки."
+        flag-description-custom-block-grow="Управляет возможностью блоков к росту."
+        flag-description-custom-block-place="Управляет возможностью установки блоков."
+        flag-description-custom-block-spread="Управляет возможностью блока распространяться."
+        flag-description-custom-build="Управляет возможностью совершения действий с блоками и сущностями, таких, как поломка, установка и взаимодействие."
+        flag-description-custom-enderpearl="Управляет возможностью пользоваться Жемчугом Края."
+        flag-description-custom-exit-player="Управляет возможностью игрока выйти из региона."
+        flag-description-custom-exp-drop="Управляет возможностью выпадения сфер опыта."
+        flag-description-custom-explosion-block="Управляет возможностью взрывов влиять на блоки."
+        flag-description-custom-explosion-entity="Управляет возможностью взрывов влиять на сущности."
+        flag-description-custom-fall-damage="Управляет возможностью игроков получать урон от падения."
+        flag-description-custom-interact-block="Управляет возможностью игроков взаимодействовать с блоками.\n&bПримечание&f: сюда не входят блоки с инвентарём, такие, как сундуки."
+        flag-description-custom-interact-entity="Управляет возможностью игроков взаимодействовать с сущностями.\n&bПримечание&f: сюда не входит доступ к сущностям с инвентарём, таким, как лошади."
+        flag-description-custom-interact-inventory="Управляет возможностью игроков взаимодействовать с инвентарями."
+        flag-description-custom-invincible="Управляет неуязвимостью игроков."
+        flag-description-custom-item-drop="Управляет возможностью игроков выбрасывать предметы."
+        flag-description-custom-item-pickup="Управляет возможностью игроков подбирать предметы."
+        flag-description-custom-monster-damage="Управляет возможностью монстров наносить урон."
+        flag-description-custom-pistons="Управляет возможностью использования поршней."
+        flag-description-custom-portal-use="Управляет возможностью использования порталов."
+        flag-description-custom-spawn-ambient="Управляет возможностью появления мобов окружения, таких, как летучие мыши."
+        flag-description-custom-spawn-animal="Управляет возможностью появления животных, таких, как коровы и свиньи."
+        flag-description-custom-spawn-aquatic="Управляет возможностью появления подводных мобов, таких, как спруты и стражи."
+        flag-description-custom-spawn-monster="Управляет возможностью появления враждебных мобов, таких, как криперы и скелеты."
+        flag-description-custom-teleport-from="Управляет возможностью игроков телепортироваться из региона."
+        flag-description-custom-teleport-to="Управляет возможностью игроков телепортироваться в регион."
+        flag-description-custom-use="Управляет возможностью игроков пользоваться блоками без инвентаря."
+        flag-description-custom-vehicle-destroy="Управляет возможностью поломки транспорта."
+        flag-description-custom-wither-damage="Управляет возможностью Иссушителя наносить урон."
+        flag-description-block-break="Управляет возможностью сломать блок.\n&dПример&f : чтобы запретить любому источнику ломать блоки земли, введите\n&a/cf block-break minecraft:dirt false\n&bПримечание&f : minecraft - это id мода, а dirt - id блока.\nЕсли id мода не указан - будет использоваться minecraft."
+        flag-description-block-grow="Управляет возможностью блока вырастать.\n&dПример&f : чтобы запретить кактусу расти, введите\n&a/cf block-grow minecraft:cactus false\n&bПримечание&f : minecraft - это id мода, а cactus - id блока.\nЕсли id мода не указан - будет использоваться minecraft."
+        flag-description-block-modify="Управляет возможностью изменения блоков.\n&dПример&f : чтобы запретить любому источнику поджигать блок, введите\n&a/cf block-modify minecraft:fire false\n&bПримечание&f : minecraft - это id мода, а fire - id блока.\nЕсли id мода не указан - будет использоваться minecraft."
+        flag-description-block-place="Управляет возможностью поставить блок.\n&dПример&f : чтобы запретить любому источнику ставить землю, введите\n&a/cf block-place minecraft:dirt false\n&bПримечание&f : minecraft - это id мода, а dirt - id блока.\nЕсли id мода не указан - будет использоваться minecraft."
+        flag-description-block-spread="Управляет возможностью блока распространяться на другие блоки.\n&dПример&f : чтобы запретить распространение огня, введите\n&a/cf block-spread any false context[source=fire]\n&bПримечание&f : 'any' обозначает любой целевой блок, а fire - id источника.\nЕсли id мода не указан - будет использоваться minecraft."
+        flag-description-collide-block="Управляет возможностью столкновения сущности с блоком.\n&dПример&f : чтобы запретить сущностям сталкиваться с каменными нажимными плитами, введите\n&a/cf collide-block minecraft:stone_pressure_plate false\n&bПримечание&f : minecraft - это id мода, а stone_pressure_plate - id блока.\nЕсли id мода не указан - будет использоваться minecraft."
+        flag-description-collide-entity="Управляет возможностью столкновения сущности с другой сущностью.\n&dПример&f : чтобы отключить столкновение сущностей с рамками, введите\n&a/cf collide-entity minecraft:item_frame false\n&bПримечание&f : minecraft - это id мода, а item_frame - id сущности.\nЕсли id мода не указан - будет использоваться minecraft."
+        flag-description-command-execute="Управляет возможностью выполнять команды.\n&dПример&f : чтобы запретить команду '/shop select' из Pixelmon, введите\n&a/cf command-execute pixelmon:shop[select] false\n&bПримечание&f : &o&6pixelmon&f - это id мода, &o&6shop&f - базовая команда, а &o&6select&f - аргумент.\nЕсли id мода не указан - будет использоваться minecraft."
+        flag-description-command-execute-pvp="Управляет возможностью выполнять команды во время сражения с другим игроком.\n&dПример&f : чтобы запретить команду '/shop select' из Pixelmon, введите \n&a/cf command-execute-pvp pixelmon:shop[select] false\n&bПримечание&f : &o&6pixelmon&f - это id модв, &o&6shop&f - базовая команда, а &o&6select&f - аргумент.\nЕсли id мода не указан - будет использоваться minecraft."
+        flag-description-enter-claim="Управляет возможностью входа сущности в регион.\n&dПример&f : чтобы запретить игрокам входить в регион, введите\n&a/cf enter-claim player false\n&bПримечание&f : если вы хотите изменить разрешение для групп, используйте команду /cfg."
+        flag-description-entity-chunk-spawn="Управляет возможностью появления сохранённой сущности при загрузке чанка.\n&dПример&f : чтобы запретить лошадям появляться при загрузке чанка, введите\n&a/cf entity-chunk-spawn minecraft:horse false\n&bПримечание&f : это удалит &cВСЕ&f сохранённые сущности, когда чанк будет загружен. Если чанк уже загружен - сущности будут удалены при его перезагрузке. Используйте с большой осторожностью."
+        flag-description-entity-damage="Управляет возможностью сущности получить урон.\n&dПример&f : чтобы запретить животным получать урон, введите\n&a/cf entity-damage minecraft:animal false."
+        flag-description-entity-riding="Управляет возможностью сесть на сущность верхом.\n&dПример&f : чтобы запретить садиться верхом на лошадей, введите\n&a/cf entity-riding minecraft:horse false."
+        flag-description-entity-spawn="Управляет возможностью сущности появиться в мире.\n&dПример&f : чтобы запретить генерацию свиней, введите\n&a/cf entity-spawn minecraft:pig false\n&bПримечание&f : сюда не входят предметы. См. флаг item-spawn."
+        flag-description-entity-teleport-from="Управляет возможностью сущности телепортироваться из региона.\n&dПример&f : чтобы запретить игрокам телепортироваться из этого региона, введите\n&a/cf entity-teleport-from player false\n&bПримечание&f : если вы хотите изменить разрешение для групп, используйте команду /cfg."
+        flag-description-entity-teleport-to="Управляет возможностью сущности телепортироваться в регион.\n&dПример&f : чтобы запретить игрокам телепортироваться в этот регион, введите\n&a/cf entity-teleport-to player false\n&bПримечание&f : если вы хотите изменить разрешение для групп, используйте команду /cfg."
+        flag-description-exit-claim="Управляет возможностью сущности выйти из региона.\n&dПример&f : чтобы запретить игрокам выход из региона, введите\n&a/cf exit-claim player false\n&bПримечание&f : если вы хотите изменить разрешение для групп, используйте команду /cfg."
+        flag-description-explosion-block="Управляет возможностью взрыва повреждать блоки в регионе.\n&dПример&f : чтобы запретить взрыву влиять на любые блоки, введите\n&a/cf explosion-block any false"
+        flag-description-explosion-entity="Управляет возможностью взрыва наносить урон сущностям в регионе.\n&dПример&f : чтобы запретить взрыву влиять на любые сущности, введите\n&a/cf explosion-entity any false"
+        flag-description-interact-block-primary="Управляет возможностью игрока нажимать ЛКМ (атаковать) по блоку.\n&dПример&f : чтобы запретить игрокам нажимать ЛКМ по сундукам, введите\n&a/cf interact-block-primary minecraft:chest false"
+        flag-description-interact-block-secondary="Управляет возможностью игрока нажимать ПКМ по блоку.\n&dПример&f : чтобы запретить игрокам нажимать ПКМ (открывать) сундуки, введите\n&a/cf interact-block-secondary minecraft:chest false"
+        flag-description-interact-entity-primary="Управляет возможностью игрока нажимать ЛКМ (атаковать) по сущности.\n&dПример&f : чтобы запретить игрокам нажимать ЛКМ по коровам, введите\n&a/cf interact-entity-primary minecraft:cow false"
+        flag-description-interact-entity-secondary="Управляет возможностью игрока нажимать ПКМ по сущности.\n&dПример&f : чтобы запретить игрокам взаимодействовать с жителями, введите\n&a/cf interact-entity-secondary minecraft:villager false"
+        flag-description-interact-inventory="Управляет возможностью игрока нажимать ПКМ (открывать) блок, содержащий инвентарь, например - сундук.\n&dПример&f : чтобы запретить игрокам открывать сундуки, введите\n&a/cf interact-inventory minecraft:chest false"
+        flag-description-interact-inventory-click="Управляет возможностью игрока кликать по слоту инвентаря.\n&dПример&f : чтобы запретить игрокам кликать по слоту инвентаря, в котором лежит алмаз, введите\n&a/cf interact-inventory-click minecraft:diamond false"
+        flag-description-interact-item-primary="Управляет возможностью игрока нажимать ЛКМ (атаковать) предметом.\n&dПример&f : чтобы запретить игрокам атаковать алмазным мечом, введите\n&a/cf interact-item-primary minecraft:diamond_sword false"
+        flag-description-interact-item-secondary="Управляет возможностью игрока нажимать ПКМ предметом.\n&dПример&f : чтобы запретить игрокам нажимать ПКМ огнивом, введите\n&a/cf interact-item-secondary minecraft:flint_and_steel false"
+        flag-description-item-drop="Управляет возможностью предмета быть выброшенным в регионе.\n&dПример&f : чтобы запретить игрокам выбрасывать алмазы в этом регионе, введите\n&a/cf item-drop minecraft:flint_and_steel false context[source=player]"
+        flag-description-item-pickup="Управляет возможностью предмета быть подобранным в регионе.\n&dПример&f : чтобы запретить игрокам поднимать алмазы, введите\n&a/cf item-pickup minecraft:diamond false"
+        flag-description-item-spawn="Управляет возможностью предмета появиться в регионе.\n&dПример&f : чтобы запретить перьям появляться в этом регионе, введите\n&a/cf item-spawn minecraft:feather false"
+        flag-description-item-use="Управляет возможностью воспользоваться предметом.\n&dПример&f : чтобы запретить есть яблоки в этом регионе, введите\n&a/cf item-use minecraft:apple false"
+        flag-description-leaf-decay="Управляет возможностью листьев опадать в этом регионе.\n&dПример&f : чтобы запретить листьям опадать в этом регионе, введите\n&a/cf leaf-decay any false"
+        flag-description-liquid-flow="Управляет возможностью жидкости, такой, как лава или вода, растекаться в этом регионе.\n&dПример&f : чтобы запретить всем видам жидкости растекаться в этом регионе, введите\n&a/cf liquid-flow any false"
+        flag-description-portal-use="Управляет возможностью пользоваться порталом.\n&dПример&f : чтобы запретить только игрокам проходить в порталы без влияния на не-игроков, введите\n&a/cf portal-use any false context[source=player]"
+        flag-description-projectile-impact-block="Управляет возможностью снаряда попадать (сталкиваться) с блоком.\n&dПример&f : чтобы запретить покеболам из Pixelmon сталкиваться с блоками, введите\n&a/cf projectile-impact-block any false[source=pixelmon:occupiedpokeball]\n&bПримечание&f : сюда входят такие вещи, как зелья, стрелы, снежки, покеболы из Pixelmon и т.д."
+        flag-description-projectile-impact-entity="Управляет возможностью снаряда попадать (сталкиваться) с сущностью.\n&dПример&f : чтобы запретить стрелам попадать в сущности, введите\n&a/cf projectile-impact-entity minecraft:arrow false[source=player]\n&bПримечание&f : сюда входят такие вещи, как зелья, стрелы, снежки, покеболы из Pixelmon и т.д."
+        flag-description-custom-chest-access="Управляет возможностью игрока взаимодействовать с инвентарями сундуков."
+        flag-description-custom-chorus-fruit-teleport="Управляет возможностью игрока телепортироваться при помощи плодов коруса."
+        flag-description-custom-crop-growth="Управляет возможностью культур расти."
+        flag-description-custom-damage-animals="Управляет возможностью животных получать урон."
+        flag-description-custom-enderman-grief="Управляет возможностью Странников Края ломать и ставить блоки."
+        flag-description-custom-enter-player="Управляет возможностью игрока входить в этот регион."
+        flag-description-custom-explosion-creeper="Управляет возможностью крипера взрываться."
+        flag-description-custom-explosion-tnt="Управляет возможностью динамита взрываться."
+        flag-description-custom-fire-damage="Управляет возможностью огня наносить урон."
+        flag-description-custom-fire-spread="Управляет возможностью огня распространяться."
+        flag-description-custom-grass-growth="Управляет возможностью травы расти."
+        flag-description-custom-ice-form="Управляет возможностью замерзания льда."
+        flag-description-custom-ice-melt="Управляет возможностью таяния льда."
+        flag-description-custom-lava-flow="Управляет возможностью лавы растекаться."
+        flag-description-custom-leaf-decay="Управляет возможностью листвы опадать."
+        flag-description-custom-lighter="Управляет возможностью игрока использовать огниво."
+        flag-description-custom-lightning="Управляет возможностью молнии наносить урон."
+        flag-description-custom-mushroom-growth="Управляет возможностью грибов вырастать."
+        flag-description-custom-mycelium-spread="Управляет возможностью мицелия распространяться."
+        flag-description-custom-pvp="Управляет возможностью сражений между игроками."
+        flag-description-custom-ride="Управляет возможностью садиться на транспорт (включая животных)."
+        flag-description-custom-sleep="Управляет возможностью игрока спать в кроватях."
+        flag-description-custom-snow-fall="Управляет возможностью появления снега."
+        flag-description-custom-snow-melt="Управляет возможностью таяния снега."
+        flag-description-custom-snowman-trail="Управляет возможностью снежных големов оставлять след из снега."
+        flag-description-custom-soil-dry="Управляет возможностью почвы высыхать."
+        flag-description-custom-vehicle-place="Управляет возможностью ставить транспорт (лодки, вагонетки и т.п.)."
+        flag-description-custom-vine-growth="Управляет возможностью роста лиан (и ламинарий)."
+        flag-description-custom-water-flow="Управляет возможностью воды растекаться."
+        flag-invalid-context="&cВведён неверный контекст '&f{context}&c' для базового флага &f{flag}&c."
+        flag-invalid-meta="&cВведены неверные метаданные цели '&f{value}&c' для базового флага &f{flag}&c."
+        flag-invalid-target="&cВведена неверная цель '&f{target}&c' для базового флага &f{flag}&c."
+        flag-not-found="&cФлаг &f{flag}&c не найден."
+        flag-not-set="&fФлаг &r{flag}&f не установлен.\nИспользуется стандартное значение &r{value}&f."
+        flag-overridden="&cНе удалось установить &f{flag}&c - он переопределён администратором."
+        flag-override-not-supported="&cРегион вида &f{type}&c не поддерживает переопределение флагов."
+        flag-reset-success="&aФлаги региона успешно откачены на стандартные значения."
+        flag-reset-warning="&6Вы уверены, что хотите откатить флаги этого региона на стандартные значения?"
+        flag-set-permission-target="&aРазрешение &b{permission}&a вида &f{type}&a с контекстами &7{contexts}&a установлено в значение &6{value}&a для &6{target}&a."
+        flag-ui-click-allow="Нажмите здесь, чтобы разрешить этот флаг."
+        flag-ui-click-deny="Нажмите здесь, чтобы запретить этот флаг."
+        flag-ui-click-remove="Нажмите здесь, чтобы удалить этот флаг."
+        flag-ui-click-toggle="&fНажмите здесь, чтобы переключить значение флага &r{flag}&f."
+        flag-ui-info-claim="Значения в регионе проверяются перед стандартными. Позволяет владельцам регионов определять значения флагов только для определённого региона."
+        flag-ui-info-default="Стандартные значения проверяются последними. Значения из переопределения и региона имеют высший приоритет."
+        flag-ui-info-inherit="Наследование - флаг, устанавливаемый родительским регионом, который не может быть изменён."
+        flag-ui-info-override="Переопределение имеет наивысший приоритет и проверяется до стандартных и регионных значений. Позволяет администраторам переопределять поведение всех базовых и администраторских регионов."
+        flag-ui-inherit-parent="Этот флаг унаследован от родителя {name}&f и &nне может&f быть изменён."
+        flag-ui-override-no-permission="Этот флаг переопределён администратором и &n&cНЕ&f может быть изменён."
+        flag-ui-override-permission="&fФлаг &r{flag}&f &cпереопределён&f администратором.\nНажмите здесь, чтобы удалить этот флаг."
+        flag-ui-return-flags="Вернуться к флагам"
+        label-accessors=Доступ
+        label-area=Площадь
+        label-blocks=Блоков
+        label-builders=Строительство
+        label-buy=Покупка
+        label-cancel=Отмена
+        label-children=суб-регионы
+        label-confirm=Подтвердить
+        label-containers=Контейнеры
+        label-context=Контекст
+        label-created=Создан
+        label-displaying=Отображение
+        label-expired=Истекло
+        label-farewell=Прощание
+        label-flag=Флаг
+        label-greeting=Приветствие
+        label-group=Группа
+        label-inherit=Наследование
+        label-location=Местоположение
+        label-managers=Управление
+        label-name=Имя
+        label-no=Нет
+        label-output=Вывод
+        label-owner=Владелец
+        label-permission=Разрешение
+        label-player=Игрок
+        label-price=Цена
+        label-raid=Рейды
+        label-resizable="Можно изменить размер"
+        label-result=Результат
+        label-schematic="Резервная копия"
+        label-source=Источник
+        label-spawn="Точка возрождения"
+        label-target=Цель
+        label-trust=Доступ
+        label-type=Тип
+        label-unknown=Неизвестно
+        label-user=Пользователь
+        label-world=Мир
+        label-yes=Да
+        mode-admin="&aВключён режим создания администраторских регионов. Все созданные регионы будут бесплатны и доступны для редактирования всеми администраторами."
+        mode-basic="&aВключён режим создания обычных областей."
+        mode-nature="&aГотов восстанавливать природу! ПКМ для начала, &f/modebasic&c - для выхода из режима."
+        mode-subdivision="&aВключён режим создания суб-регионов. Используйте лопату, чтобы создавать суб-регионы в существующих регионах.  Воспользуйтесь &f/modebasic&a, чтобы выйти из этого режима."
+        mode-town="&aВключён режим создания городов."
+        option-description-abandon-delay="&aКоличество дней до того, как свежесозданный регион может быть удалён."
+        option-description-abandon-return-ratio="&aДоля базовых блоков региона, возвращаемых игроку при удалении региона."
+        option-description-blocks-accrued-per-hour="&aЗарабатываемые блоки в час.\n&dПримечание&f: См. /playerinfo."
+        option-description-chest-expiration="&aКоличество дней, после которых автоматически созданный регион вокруг сундука будет удалён.\n&dПримечание&f: по истечении срока регион может быть просто удалён или откачен на состояние при создании. Это зависит от настроек сервера. Свяжитесь с администратором для дополнительной информации."
+        option-description-create-limit="&aКоличество базовых регионов на игрока.\n&dПримечание&f: Значение ниже 0 убирает ограничение."
+        option-description-create-mode="&aРежим создания региона (Площадь = 2D, Объём = 3D).\n&dПримечание&f: &bПлощадь&a действует только по осям x и z.\n&bОбъём&a действует по осям x, y и z."
+        option-description-economy-block-cost="&aСтоимость одного блока региона.\n&dПримечание&f: Формула для вычисления стоимости региона: цена * количество блоков региона."
+        option-description-economy-block-sell-return="&aВозвращаемые средства при продаже блоков региона.\n&dПримечание&f: Формула для вычисления возвращаемых средств: цена * количество блоков региона."
+        option-description-expiration="&aКоличество дней без активности, после которого регион будет удалён.\n&dПримечание&f: по истечении срока регион может быть просто удалён или откачен на состояние при создании. Это зависит от настроек сервера. Свяжитесь с администратором для дополнительной информации."
+        option-description-initial-blocks="&aКоличество блоков, которое игрок имеет изначально."
+        option-description-max-accrued-blocks="&aОграничение на накопленные блоки (с течением времени).\n&dПримечание&f: не влияет на купленные или подаренные администратором блоки региона."
+        option-description-max-level="&aМаксимальный уровень по оси y, на котором может быть создан регион."
+        option-description-max-size-x="&aМаксимальный размер региона по оси x."
+        option-description-max-size-y="&aМаксимальный размер региона по оси y."
+        option-description-max-size-z="&aМаксимальный размер региона по оси z."
+        option-description-min-level="&aМинимальный уровень по оси y, на котором может быть создан регион."
+        option-description-min-size-x="&aМинимальный размер региона по оси x."
+        option-description-min-size-y="&aМинимальный размер региона по оси y."
+        option-description-min-size-z="&aМинимальный размер региона по оси z."
+        option-description-player-command="&aИспользуется для выполнения команды в определённом контексте."
+        option-description-player-deny-flight="&aОпределяет, может ли игрок летать в регионе.\n&dПримечание&f: не даёт игрокам возможность летать, только удаляет её при необходимости. Даёт наибольшую совместимость с другими плагинами."
+        option-description-player-deny-godmode="&aОпределяет, может ли игрок быть в режиме бога в регионе.\n&dПримечание&f: не даёт игрокам возможность входить в режим бога, только удаляет её при необходимости. Даёт наибольшую совместимость с другими плагинами."
+        option-description-player-deny-hunger="&aОпределяет, может ли игрок терять сытость в регионе.\n&dПримечание&f: не даёт игрокам получать сытость в регионе, только удаляет возможность её терять при необходимости. Даёт наибольшую совместимость с другими плагинами."
+        option-description-player-gamemode="&aОпределяет игровой режим для игроков в регионе."
+        option-description-player-health-regen="&aОпределяет скорость восстановления здоровья игроков в регионе.\n&dПримечание&f: не имеет эффекта на игроков с полным запасом здоровья. \n&dПримечание&f: значение &6-1&f отключает эту опцию."
+        option-description-player-keep-inventory="&aОпределяет, сохранит ли игрок содержимое своего инвентаря при смерти в регионе."
+        option-description-player-keep-level="&aОпределяет, сохранит ли игрок свой уровень при смерти в регионе."
+        option-description-player-walk-speed="&aОпределяет скорость ходьбы игроков в регионе.\n&dПримечание&f: значение &6-1&f отключает эту опцию."
+        option-description-player-weather="&aОпределяет погоду для игрока в регионе."
+        option-description-radius-inspect="&aРадиус в блоках для поиска регионов поблизости при инспектировании."
+        option-description-radius-list="&aРадиус в блоках для составления списка регионов поблизости."
+        option-description-tax-expiration="&aКоличество дней без оплаты налогов, после которых регион будет заморожен.\n&dПримечание&f: заморозка означает отсутствие доступа к строительству и инвентарям в регионе до совершения оплаты."
+        option-description-tax-expiration-days-keep="&aКоличество дней после заморозки региона до его удаления.\n&dПримечание&f: по истечении срока регион может быть просто удалён или откачен на состояние при создании. Это зависит от настроек сервера. Свяжитесь с администратором для дополнительной информации."
+        option-description-tax-rate="&aВеличина налога на регион.\n&dПримечание&f: величина налога вычисляется по размеру региона в блоках."
+        option-invalid-context="&cВведён неверный контекст '&f{context}&c' для опции &f{option}&c."
+        option-invalid-target="&cВведена неверная цель '&f{target}&c' для опции &f{option}&c."
+        option-invalid-value="&cВведено неверное значение '&6{value}&c' для опции &f{option}&c. This option only accepts &f{type}&c values."
+        option-not-found="&cОпция &f{option}&c не найдена."
+        option-not-set="&a{option}&f не установлена.\nПрименяется стандартное значение &a{value}&f."
+        option-override-not-supported="&cРегионы вида &f{type}&c не поддерживают переопределение опций."
+        option-player-deny-flight="&cУ вас нет разрешения на полёт в этом регионе. Вы были телепортированы на безопасное место на земле."
+        option-reset-success="&aОпции региона успешно откачены на стандартные значения."
+        option-set-target="&aОпция &b{option}&a вида &b{type}&a с контекстами &7{contexts}&a установлена в значение {value}&a для &6{target}&a."
+        option-ui-click-toggle="Нажмите здесь, чтобы переключить опцию &f{option}&r."
+        option-ui-inherit-parent="Эта опция унаследована от родительского региона {name}&f и &nне может&f быть изменена."
+        option-ui-overridden="&cНе удалось установить опцию &f{option}&c. Значение опции переопределено администратором."
+        option-ui-override-no-permission="Значение опции было переопределено администратором, она &n&cНЕ&f может быть изменена."
+        owner-admin=Администратор
+        permission-access="&cУ вас нет разрешения от игрока &6{player}&c на доступ к этому."
+        permission-assign-without-having="&cВы не можете выдать разрешение на то, что не разрешено вам."
+        permission-ban-block="&cБлок {id}&c &l&nЗАБАНЕН&c и не может быть использован."
+        permission-ban-entity="&cСущность {id}&c &l&nЗАБАНЕНА&c и не может быть использована."
+        permission-ban-item="&cПредмет {id}&c &l&nЗАБАНЕН&c и не может быть использован."
+        permission-build="&cУ вас нет разрешения от игрока &6{player}&c на строительство."
+        permission-build-near-claim="&cУ вас нет разрешения от игрока &6{player}&c на строительство около его региона."
+        permission-claim-create="&cУ вас нет разрешения на создание региона."
+        permission-claim-delete="&cУ вас нет разрешения на удаление регионов вида {type}&c."
+        permission-claim-enter="&cУ вас нет разрешения на вход в этот регион."
+        permission-claim-exit="&cУ вас нет разрешения на выход из этого региона."
+        permission-claim-ignore="&cУ вас нет разрешения на игнорирование регионов вида {type}&c."
+        permission-claim-list="&cУ вас нет разрешения на просмотр информации о регионах, принадлежащих другому игроку."
+        permission-claim-manage="&cУ вас нет разрешения на редактирование регионов вида {type}&c."
+        permission-claim-reset-flags="&cУ вас нет разрешения на откат значений флагов для региона вида {type}&c."
+        permission-claim-reset-flags-self="&cУ вас нет разрешения на откат значений флагов для вашего региона."
+        permission-claim-resize="&cУ вас нет разрешения на изменение размера этого региона."
+        permission-claim-sale="&cУ вас нет разрешения на продажу этого региона."
+        permission-claim-transfer-admin="&cУ вас нет разрешения на передачу администраторского региона."
+        permission-clear="&cРазрешения в этом регионе очищены. Чтобы изменить разрешения для ВСЕХ ваших регионов, выйдите из них наружу."
+        permission-clear-all="&cТолько владелец области может очистить все разрешения."
+        permission-command-trust="&cУ вас нет разрешения на выдачу этого вида разрешения."
+        permission-cuboid="&cУ вас нет разрешения на создание/изменение размера базовых регионов в 3D-режиме."
+        permission-edit-claim="&cУ вас нет разрешения на изменение этого региона."
+        permission-fire-spread="&cУ вас нет разрешения на поджигание блоков в этом регионе."
+        permission-flag-defaults="&cУ вас нет разрешения на изменение стандартных значений флагов."
+        permission-flag-overrides="&cУ вас нет разрешения на редактирование переопределённых значений флагов."
+        permission-flag-use="&cУ вас нет разрешения на использование этого флага."
+        permission-flow-liquid="&cУ вас нет разрешения на разливание жидкостей в этом регионе."
+        permission-global-option="&cУ вас нет разрешения на редактирование глобальных настроек."
+        permission-grant="&cУ вас нет разрешения на выдачу разрешения, которого у вас нет."
+        permission-group-option="&cУ вас нет разрешения на изменение групповых настроек."
+        permission-interact-block="&cУ вас нет разрешения от игрока &6{player}&c на взаимодействие с блоком &d{block}&c."
+        permission-interact-entity="&cУ вас нет разрешения от игрока &6{player}&c на взаимодействие с сущностью &d{entity}&c."
+        permission-interact-item="&cУ вас нет разрешения от игрока &6{player}&c на взаимодействие с предметом &d{item}&c."
+        permission-interact-item-block="&cУ вас нет разрешения на использование предмета &d{item}&c на блок &b{block}&c."
+        permission-interact-item-entity="&cУ вас нет разрешения на использование предмета &d{item}&c на блок &b{entity}&c."
+        permission-inventory-open="&cУ вас нет разрешения от игрока &6{player}&c на открытие &d{block}&c."
+        permission-item-drop="&cУ вас нет разрешения от игрока &6{player}&c на выбрасывание предмета &d{item}&c."
+        permission-item-use="&cУ вас нет разрешения на использование предмета &d{item}&c."
+        permission-option-defaults="&cУ вас нет разрешения на изменение стандартных значений опций."
+        permission-option-overrides="&cУ вас нет разрешения на управление переопределением опций."
+        permission-option-use="&cУ вас нет разрешения на использование этой опции."
+        permission-override-deny="&cДействие, которое вы пытаетесь совершить, было отменено флагом, переопределённым администратором."
+        permission-player-admin-flags="&cУ вас нет разрешения на изменение флагов для членов администрации."
+        permission-player-option="&cУ вас нет разрешения на установку опции на игрока."
+        permission-player-view-others="&cУ вас нет разрешения на просмотр других игроков."
+        permission-portal-enter="&cВы не можете воспользоваться этим порталом, потому что у вас нет разрешения от игрока &6{player}&c на вход в регион в месте назначения."
+        permission-portal-exit="&cВы не можете воспользоваться этим порталом, потому что у вас нет разрешения от игрока &6{player}&c на выход из региона."
+        permission-protected-portal="&cУ вас нет разрешения от игрока &6{player}&c на использование портала в этом регионе."
+        permission-trust="&cУ вас нет разрешения от игрока &6{player}&c на изменение разрешений в этом регионе."
+        permission-visual-claims-nearby="&cУ вас нет разрешения на отображение регионов поблизости."
+        player-accrued-blocks-exceeded="&cУ игрока &6{player}&c есть &6{total}&c блоков и добавление ему ещё &6{amount}&c блоков превысит максимальное возможное количество.\n. Уменьшите добавляемое количество или попросите администратора выдать игроку нужное количество блоков."
+        player-remaining-blocks-2d="&aВы можете занять ещё &6{block-amount}&a блоков."
+        player-remaining-blocks-3d="&aВы можете занять ещё &6{chunk-amount}&a чанков. &f({block-amount} блоков)"
+        playerinfo-ui-abandon-return-ratio="&eДоля возврата при удалении региона&f : &a{ratio}"
+        playerinfo-ui-block-accrued="&eНакоплено блоков&f : &a{amount}&7(&d{block_amount}&f per hour&7)"
+        playerinfo-ui-block-bonus="&eБонусных блоков&f : &a{amount}"
+        playerinfo-ui-block-initial="&eНачальное количество блоков&f : &a{amount}"
+        playerinfo-ui-block-max-accrued="&eМаксимум накопления блоков&f : &a{amount}"
+        playerinfo-ui-block-remaining="&eОсталось блоков&f : &a{amount}"
+        playerinfo-ui-block-total="&eВсего блоков&f : &a{amount}"
+        playerinfo-ui-chunk-total="&eВсего чанков&f : &a{amount}"
+        playerinfo-ui-claim-level="&eМин./Макс. уровень региона&f : &a{level}"
+        playerinfo-ui-claim-size-limit="&eОграничения по размеру региона&f : &a{limit}"
+        playerinfo-ui-claim-total="&eВсего регионов&f : &a{amount}"
+        playerinfo-ui-economy-block-available-purchase="&eДоступно блоков региона для покупки&f : &a{amount}"
+        playerinfo-ui-economy-block-cost="&eЦена покупки блока региона&f : &a{amount} за блок"
+        playerinfo-ui-economy-block-sell-return="&eЦена продажи блока региона&f : &a{amount} за блок"
+        playerinfo-ui-last-active="&eВ последний раз активен&f : {date}"
+        playerinfo-ui-tax-current-rate="&eТекущий размер налога на регионы&f : &a{rate}"
+        playerinfo-ui-tax-global-claim-rate="&eОбщий размер налога на регионы&f : &a{rate}"
+        playerinfo-ui-tax-global-town-rate="&eОбщий размер налога на города&f : &a{rate}"
+        playerinfo-ui-tax-total="&eОбщий размер налогов&f : &a{amount}"
+        playerinfo-ui-title="&bИнформация об игроке"
+        playerinfo-ui-uuid="&eUUID&f : &7{id}"
+        playerinfo-ui-world="&eМир&f : &7{name}"
+        plugin-command-not-found="&cКоманда '&a{command}&c' для плагина &b{id}&c не найдена."
+        plugin-event-cancel="&cПлагин отменил это действие."
+        plugin-not-found="&cНе удалось найти плагин с идентификатором &b{id}&c."
+        plugin-reload="&aGriefDefender перезагружен."
+        pvp-claim-not-allowed="&aВ этом регионе сражения между игроками запрещены."
+        pvp-source-not-allowed="&aУ вас нет разрешения на сражение с другим игроком."
+        pvp-target-not-allowed="&aВы не можете атаковать игроков, которые не могут участвовать в сражениях."
+        registry-block-not-found="&cБлок {id}&c не найден в реестре."
+        registry-entity-not-found="&cСущность {id}&c не найдена в реестре."
+        registry-item-not-found="&cПредмет {id}&c не найден в реестре."
+        resize-overlap="&cНельзя изменить размер региона - в этом случае он будет пересекаться с другим регионом."
+        resize-overlap-subdivision="&cНельзя изменить размер суб-региона - в этом случае он будет пересекаться с другим суб-регионом.  Воспользуйтесь &f/abandon&c, чтобы удалить его, или используйте лопату на его угол, чтобы изменить его размер."
+        resize-same-location="&cУгол региона уже находится здесь. Нужно выбрать другое место для угла региона, чтобы изменить его размер."
+        resize-start="&aИзменение размера региона.  Используйте лопату на новой точке размещения этого угла."
+        resize-success-2d="&aРазмер региона изменён. У вас осталось ещё &6{block-amount}&a блоков."
+        resize-success-3d="&aРазмер региона изменён. У вас осталось ещё &6{chunk-amount}&a чанков. &f({block-amount} блоков)"
+        result-type-change-deny="&cВы не можете изменить вид региона на {type}."
+        result-type-change-not-admin="&cВы не имеете администраторских привилегий на изменение вида на {type}&c."
+        result-type-child-same="&cРегионы вида &f{type}&c не могут иметь прямые суб-регионы вида &f{type}&c."
+        result-type-create-deny="&cРегионы вида &f{type}&c не могут быть созданы в &f{target_type}&с."
+        result-type-no-children="&cРегионы вида &f{type}&c не могут содержать суб-регионы."
+        result-type-only-subdivision="&cРегионы вида &f{type}&c могут содержать только суб-регионы."
+        result-type-requires-owner="&cНе удалось изменить регион вида {type} на {target_type}. Требуется владелец."
+        schematic-abandon-all-restore-warning="&6Вы уверены, что хотите &nудалить&6 &cВСЕ&6 ваши регионы? &cВСЕ ДАННЫЕ БУДУТ УДАЛЕНЫ&f!!&6 При подтверждении все регионы будет восстановлены в состояние, в котором они были при создании."
+        schematic-abandon-restore-warning="&6Вы уверены, что хотите &nудалить&6 этот регион? &cВСЕ ДАННЫЕ БУДУТ УДАЛЕНЫ&f!!&6 При подтверждении этот регион будет восстановлен в состояние, в котором они были при создании."
+        schematic-create="&aСоздание резервной копии..."
+        schematic-create-complete="&aРезервная копия создана."
+        schematic-create-fail="&cНе удалось создать резервную копию."
+        schematic-deleted="&aРезервная копия {name} удалена."
+        schematic-none="&aДля этого региона нет резервных копий."
+        schematic-restore-click="&aНажмите, чтобы восстановить регион из резервной копии.\nИмя: {name}\nСоздана: {date}"
+        schematic-restore-confirmation="&6Вы уверены, что хотите восстановить регион? Нажатие Подтвердить восстановит &cВСЕ&6 данные региона из резервной копии. Используйте аккуратно!"
+        schematic-restore-confirmed="&aРегион успешно восстановлен из резервной копии &b{name}&a."
+        spawn-not-set="&cТочка возрождения в регионе не задана."
+        spawn-set-success="&aТочка возрождения в регионе успешно установлена в &b{location}&a."
+        spawn-teleport="&aВы телепортированы в точку возрождения в регионе в &b{location}&a."
+        tax-claim-expired="&cЭтот регион заморожен за неуплату налогов. Текущая задолженность: '&a{amount}&c'.\nУ вас осталось '&a{days}&c' дней на то, чтобы зачислить оплату на счёт банка региона, чтобы разморозить его.\nПри неуплате налога регион будет удалён.\nПримечание: чтобы зачислить средства на счёт банка региона, воспользуйтесь &f/claimbank&c deposit <amount>."
+        tax-claim-paid-balance="&aЗадолженность по налогам в размере '&6{amount}&a' оплачена. Ваш регион разморожен и снова может быть использован."
+        tax-claim-paid-partial="&aЗадолженность по налогам в размере '&6{amount}&a' частично оплачена. Чтобы разморозить регион, остаток долга в размере '&6{balance}&a' должен быть уплачен."
+        tax-info="&aСледующая выплата по налогам в размере &6{amount}&a будет списана с вашего счёта &b{date}&a."
+        tax-past-due="&cУ вас есть задолженность по налогам в размере &a{balance}&c, которая должна быть выплачена до &b{date}&c. В случае неуплаты ваша собственность будет потеряна."
+        teleport-confirm="&aВы уверены, что хотите телепортироваться на {pos}? Нажмите Подтвердить, чтобы продолжить."
+        teleport-delay-notice="&aВы будете телепортированы через {delay}&a секунд."
+        teleport-move-cancel="&cТелепортация отменена! Вы не можете двигаться, пока идёт отсчёт."
+        teleport-no-safe-location="&cНе удалось найти безопасную точку для телепортации в этот регион!\n&aВоспользуйтесь '&f/claiminfo&a', чтобы установить безопасную точку телепортации/возрождения."
+        teleport-success="&aВы были телепортированы в регион {name}&a."
+        title-accessor=ДОСТУП
+        title-all=ВСЕ
+        title-builder=СТРОИТЕЛЬСТВО
+        title-claim=РЕГИОН
+        title-container=КОНТЕЙНЕРЫ
+        title-default=СТАНДАРТ
+        title-inherit=НАСЛЕДОВАНИЕ
+        title-manager=УПРАВЛЕНИЕ
+        title-override=ПЕРЕОПРЕДЕЛЕНИЕ
+        title-own=ВЛАДЕЛЕЦ
+        tool-not-equipped="&cУ вас нет &f{tool}&c."
+        town-chat-disabled="&aЧат города отключён."
+        town-chat-enabled="&aЧат города включён."
+        town-create-not-enough-funds="&cУ вас недостаточно средств для создания города за &a{amount}&c. На вашем счёте сейчас &a{balance}&c и для создания нужно ещё &a{amount-needed}&c."
+        town-name="&aИмя города установлено: &f{name}&a."
+        town-not-found="&cГород не найден."
+        town-not-in="&cВы не находитесь в городе."
+        town-owner="&cЭто принадлежит городу."
+        town-tag="&aТег города установлен: &f{tag}&a."
+        town-tag-clear="&aТег города удалён."
+        town-tax-no-claims="&cЧтобы попасть под налогообложение этим городом, нужно иметь в нём собственность."
+        trust-already-has="&cУ пользователя &f{target}&c уже есть разрешение вида &f{type}&c."
+        trust-click-show-list="Нажмите здесь, чтобы вывести список всех игроков и групп, вписанных в регион."
+        trust-grant="&aИгроку &6{target}&a выдано разрешение вида &6{type}&a в текущем регионе."
+        trust-individual-all-claims="&aИгроку &6{player}&a выданы полные права во всех ваших регионах.  Чтобы отменить разрешения во ВСЕХ ваших регионах, воспользуйтесь &f/untrustall&a."
+        trust-invalid="&cНеверный вид разрешения.\nДоступные виды: accessor, builder, container и manager."
+        trust-list-header="Разрешения в этом регионе:"
+        trust-no-claims="&cУ вас нет регионов, в которые можно было бы вписать игрока."
+        trust-plugin-cancel="&cНе удалось выдать разрешения игроку &f{target}&c. Плагин не разрешил."
+        trust-self="&cНикому нельзя доверять. Особенно себе."
+        tutorial-claim-basic="&eНажмите для получения по созданию регионов: &ahttp://bit.ly/mcgpuser"
+        ui-click-confirm="Нажмите для подтверждения."
+        ui-click-filter-type="&7Нажмите здесь, чтобы вывести только &f{type}&7."
+        untrust-individual-all-claims="&aОтозваны все разрешения игрока &6{target}&a во ВСЕХ ваших регионах.  Чтобы настраивать разрешения в одном регионе, войдите в него и используйте &f/untrust&a."
+        untrust-individual-single-claim="&aОтозваны все разрешения игрока &6{target}&a в этом регионе.  Чтобы настроаивать разрешения во ВСЕХ ваших регионах, воспользуйтесь &f/untrustall&a."
+        untrust-no-claims="&cУ вас нет регионов, из которых можно было бы выписать игрока."
+        untrust-owner="&6{owner}&a является владельцем региона и не может быть выписан из него."
+        untrust-self="&cНельзя не доверять себе."
+    }
+}
diff --git a/sponge/src/main/resources/com/griefdefender/internal/pagination/font-sizes.json b/sponge/src/main/resources/com/griefdefender/internal/pagination/font-sizes.json
new file mode 100644
index 0000000..b6954cb
--- /dev/null
+++ b/sponge/src/main/resources/com/griefdefender/internal/pagination/font-sizes.json
@@ -0,0 +1,50 @@
+# To create/update this file: Use the following code (accurate as of MC 1.8), run on the client. The nonUnicode string constant is extracted from FontRenderer -- it's inlined though.
+# This must be run while a game is active -- I've stuck this in a command
+# ```java
+#     public void printCharSizes() {
+#         ConfigurationNode node = SimpleConfigurationNode.root();
+#         String nonUnicode = "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130\u0131\u0152\u0153"
+#                 + "\u015e"
+#                 + "\u015f\u0174"
+#                 + "\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&\'()*+,-./0123456789:;"
+#                 + "<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000";
+#         List<Integer> nonUnicodeCodePoints = new ArrayList<Integer>(nonUnicode.length());
+#         for (int i = 0; i < nonUnicode.length(); ++i) {
+#             nonUnicodeCodePoints.add(nonUnicode.codePointAt(i));
+#         }
+#         node.getNode("non-unicode").setValue(nonUnicode);
+#         int[] charWidths = Minecraft.getMinecraft().getRenderManager().getFontRenderer().charWidth;
+#         final List<Integer> charWidthsList = new ArrayList<Integer>(charWidths.length);
+#         for (int i : charWidths) {
+#             charWidthsList.add(i);
+#         }
+#         charWidthsList.set(32, 4); // Space is handled weirdly
+#         node.getNode("char-widths").setValue(charWidthsList);
+#         InputStream var1 = null;
+# 
+#         final List<Byte> glyphSizezList = new ArrayList<Byte>(65536);
+#         try {
+#             var1 = Minecraft.getMinecraft().getResourceManager().getResource(new ResourceLocation("font/glyph_sizes.bin")).getInputStream();
+#             int b;
+#             while ((b = var1.read()) != -1) {
+#                 glyphSizezList.add((byte) b);
+#             }
+#         } catch (IOException var6) {
+#             throw new RuntimeException(var6);
+#         } finally {
+#             IOUtils.closeQuietly(var1);
+#         }
+#         node.getNode("glyph-widths").setValue(glyphSizezList);
+# 
+#         try {
+#             HoconConfigurationLoader.builder()
+#                     .setRenderOptions(ConfigRenderOptions.concise())
+#                     .setFile(new File("font-sizes.conf"))
+#                     .build().save(node);
+#         } catch (IOException e) {
+#             logger.error("Unable to write character size information", e);
+#         }
+#     }
+# ```
+
+{"char-widths":[6,6,6,6,6,6,4,6,6,6,6,6,6,6,6,4,4,6,7,6,6,6,6,6,6,1,1,1,1,1,1,1,4,2,5,6,6,6,6,3,5,5,5,6,2,6,2,6,6,6,6,6,6,6,6,6,6,6,2,2,5,6,5,6,7,6,6,6,6,6,6,6,6,4,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,4,6,4,6,6,3,6,6,6,6,6,5,6,6,2,6,5,3,6,6,6,6,6,6,6,4,6,6,6,6,6,6,5,2,5,7,6,6,6,6,6,6,6,6,6,6,6,6,4,6,3,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,4,6,6,3,6,6,6,6,6,6,6,7,6,6,6,2,6,6,8,9,9,6,6,6,8,8,6,8,8,8,8,8,6,6,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,6,9,9,9,5,9,9,8,7,7,8,7,8,8,8,7,8,8,7,9,9,6,7,7,7,7,7,9,6,7,8,7,6,6,9,7,6,7,1],"glyph-widths":[15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,68,38,22,23,23,23,68,53,36,23,23,52,22,52,22,22,38,22,22,22,22,22,22,22,22,52,52,38,22,21,22,22,22,22,22,22,22,22,22,22,38,23,22,22,22,22,22,22,23,22,22,23,22,23,22,22,23,22,70,22,19,22,23,36,22,22,22,22,22,21,22,22,38,21,22,38,23,22,22,22,22,22,22,21,22,22,23,22,22,22,53,68,36,23,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,68,23,23,22,23,68,22,37,7,38,22,22,15,7,22,36,23,38,38,53,38,22,52,36,36,38,22,22,22,22,22,22,22,22,22,22,22,23,22,22,22,22,22,38,38,38,38,6,22,22,22,22,22,22,22,22,22,22,22,22,23,22,22,22,22,22,22,22,22,23,22,22,22,22,22,38,38,38,38,22,22,22,22,22,22,22,22,22,22,22,22,22,22,38,22,22,22,22,22,23,23,22,22,22,22,22,22,22,22,22,22,6,23,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,7,6,22,22,38,38,22,22,38,38,38,38,22,38,23,22,6,6,22,22,38,22,38,22,38,22,21,6,38,22,22,6,6,22,22,22,22,22,22,22,22,22,23,23,23,23,22,22,6,6,22,22,22,22,22,22,22,22,22,22,23,21,23,21,23,21,22,22,22,22,22,22,22,22,23,23,22,23,22,23,23,22,23,22,22,22,22,22,22,37,6,6,22,22,22,22,22,23,23,6,6,22,22,22,22,22,22,23,21,23,22,22,21,38,22,22,38,22,23,6,22,22,22,22,22,22,23,23,23,22,22,22,21,21,23,21,23,23,23,22,22,23,23,22,22,22,22,22,22,22,22,22,21,22,51,36,21,68,23,23,23,23,23,7,23,23,23,22,22,38,38,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,22,22,22,22,22,22,22,22,22,22,22,23,23,23,22,22,22,22,22,22,22,22,23,23,22,22,6,6,22,22,6,6,22,22,6,6,22,22,6,6,22,22,6,6,22,22,6,6,22,22,22,22,23,21,22,22,22,22,22,23,22,38,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,22,37,23,22,21,6,6,6,6,6,22,23,23,22,22,22,22,6,22,22,6,23,23,23,23,22,22,23,23,22,22,22,22,22,22,23,23,22,22,23,22,22,23,22,23,23,22,22,22,22,22,22,22,38,53,38,22,22,54,23,23,23,23,23,23,22,22,23,23,23,22,22,23,22,22,22,22,22,22,22,21,21,21,22,21,22,23,22,22,22,23,22,23,23,23,22,22,22,22,22,22,22,22,22,23,38,22,22,22,23,22,22,23,23,23,23,23,23,23,39,22,23,22,21,23,38,38,37,38,21,23,21,21,21,36,22,52,52,52,37,37,22,22,37,37,23,23,37,37,68,37,37,37,68,37,37,37,36,36,37,37,21,21,21,21,22,52,37,70,22,23,6,22,21,36,21,21,21,21,21,21,21,21,21,20,21,22,22,21,21,53,53,37,36,22,22,22,52,36,36,36,36,22,22,22,22,22,22,22,22,7,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,22,22,22,22,22,22,22,22,23,22,22,22,22,22,22,7,7,22,22,7,22,7,22,22,22,6,22,22,7,22,22,22,22,22,22,22,22,22,22,22,22,22,23,22,15,22,22,22,22,22,22,7,22,22,22,22,22,7,7,7,7,7,7,7,22,22,22,22,22,22,22,22,6,22,22,22,22,21,22,6,21,37,37,22,6,0,0,52,22,22,22,52,0,0,0,0,0,51,21,22,53,22,22,22,0,22,0,23,23,21,22,22,22,23,22,22,22,22,38,22,23,22,22,22,22,23,22,0,22,23,23,23,22,23,23,38,23,22,22,22,53,22,22,22,23,22,22,38,22,22,53,38,22,22,22,22,22,22,22,22,23,22,22,23,22,23,23,37,22,22,22,23,22,22,23,23,23,23,23,23,23,22,22,22,22,22,38,22,21,22,21,7,23,22,38,22,22,22,22,7,22,22,22,23,23,22,22,22,38,22,21,21,22,22,22,22,23,22,22,22,22,22,22,23,22,22,22,38,38,22,7,23,23,22,22,23,23,22,22,22,22,7,22,23,22,22,22,22,22,22,22,22,22,22,22,23,23,23,22,23,22,23,7,23,22,22,22,22,22,22,22,22,22,23,22,23,22,22,22,22,22,22,22,22,22,22,22,23,22,23,22,23,22,23,7,23,22,22,22,22,22,22,22,22,22,22,22,38,38,21,7,23,22,22,22,22,38,23,23,23,22,23,23,23,23,23,23,23,23,23,23,22,22,23,23,22,22,23,22,23,23,23,23,23,23,23,23,23,23,22,22,23,22,6,22,22,7,13,30,23,23,22,22,22,22,22,22,23,23,22,22,23,23,22,22,22,22,23,23,23,23,7,7,23,23,23,23,23,23,22,22,22,22,23,23,23,38,23,38,23,23,7,7,23,23,22,22,22,22,22,22,22,22,38,23,23,22,22,23,23,22,22,23,23,22,22,23,23,53,22,22,22,22,23,23,22,22,22,22,22,22,23,23,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,22,23,22,23,23,22,22,22,22,22,22,22,21,22,22,22,22,22,22,23,23,23,23,23,23,7,7,23,23,22,22,23,23,22,22,22,22,7,7,7,7,7,7,23,22,22,23,22,22,7,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,23,22,23,23,22,22,22,22,23,23,22,22,23,22,22,22,22,23,22,23,22,23,22,22,22,23,22,23,22,23,22,22,22,23,21,22,22,23,0,0,35,35,36,37,36,21,21,0,23,22,23,23,22,23,21,22,23,23,22,21,23,23,22,22,22,23,23,23,21,22,23,22,39,23,23,23,22,23,23,22,22,54,23,23,22,23,23,0,52,22,0,0,0,0,0,0,22,22,22,22,22,22,22,22,6,22,22,22,23,23,7,23,6,22,22,22,22,22,22,22,22,22,22,22,23,6,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,68,22,22,53,22,22,37,22,0,0,0,0,0,0,0,0,22,23,22,22,22,36,38,22,22,36,22,22,38,6,23,36,37,22,6,22,22,38,22,22,22,23,22,0,0,0,0,0,23,23,23,70,22,0,0,0,0,0,0,0,0,0,0,0,15,15,15,15,0,0,28,27,45,21,23,60,52,21,62,40,23,76,60,44,60,74,58,90,90,90,90,52,0,0,121,23,0,37,23,36,22,53,22,68,22,38,22,22,22,22,22,22,22,22,22,23,23,23,23,22,22,22,22,6,6,22,22,22,7,22,22,22,22,38,22,26,22,22,22,22,22,22,22,22,22,22,22,23,22,22,90,90,90,90,90,90,90,90,90,0,36,37,38,23,22,22,23,23,23,23,21,52,52,23,22,24,22,38,22,22,36,38,23,23,23,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,22,22,23,22,22,22,22,23,23,23,23,23,22,22,22,22,22,22,22,22,22,22,6,30,6,22,22,22,6,6,6,6,6,6,23,22,23,22,22,22,22,22,22,22,22,37,22,22,22,22,22,22,22,22,22,22,22,22,6,22,22,22,22,23,23,37,37,61,60,90,74,58,74,76,15,31,90,90,90,90,76,90,38,23,58,90,31,90,90,90,90,22,23,36,37,38,23,22,23,22,23,23,23,23,23,22,37,22,26,59,52,52,52,52,22,22,22,22,22,22,30,29,30,0,15,15,22,28,30,30,39,39,30,60,70,30,30,30,37,30,63,43,29,46,29,29,30,30,13,13,61,39,30,29,30,30,23,22,22,22,22,22,22,22,22,22,22,22,22,22,59,59,22,22,22,22,22,22,22,22,22,22,126,22,0,0,60,30,30,22,22,22,22,22,22,22,22,22,22,22,23,23,22,22,22,22,22,6,6,6,38,38,22,22,22,22,22,22,23,22,22,23,22,22,36,38,22,23,22,22,22,23,23,23,23,23,22,19,22,6,22,21,37,37,37,37,37,54,22,22,38,37,21,44,54,38,38,22,54,21,7,6,20,19,22,22,44,44,44,22,22,37,37,37,37,22,22,22,22,22,23,22,6,22,22,22,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,22,38,37,37,36,21,22,22,21,51,22,6,21,21,21,21,22,38,37,37,21,38,20,22,38,22,22,21,22,37,22,21,21,21,38,37,37,21,21,22,22,38,22,22,22,22,22,22,22,22,22,36,36,38,23,36,36,37,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,75,90,93,47,31,31,15,15,15,15,15,15,15,15,15,15,31,31,31,31,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,63,15,63,15,15,15,15,15,47,15,15,15,15,15,15,15,15,15,15,15,15,0,0,90,127,95,10,95,74,91,90,90,90,74,74,58,95,95,95,95,90,0,0,30,90,90,90,90,0,0,0,15,15,15,15,15,15,15,15,15,15,75,75,120,106,75,90,76,75,45,75,75,61,60,75,106,-119,31,0,0,0,0,0,0,0,0,15,15,75,15,15,0,90,95,94,0,15,15,15,15,15,15,31,30,0,0,30,30,0,0,30,30,15,47,47,15,45,15,15,15,15,30,15,15,15,15,63,15,31,15,79,15,0,31,15,15,15,15,15,15,0,15,0,0,0,31,15,15,15,0,0,90,13,95,12,79,90,91,90,90,0,0,10,10,0,0,15,15,90,75,0,0,0,0,0,0,0,0,95,0,0,0,0,15,15,0,15,31,75,91,94,0,0,60,76,60,45,44,44,45,76,29,45,15,15,59,28,59,45,45,-119,44,61,30,0,0,0,0,0,0,90,90,95,0,15,15,15,14,15,15,0,0,0,0,15,15,0,0,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,0,15,15,15,15,15,15,15,0,15,15,0,15,15,0,15,15,0,0,90,0,79,9,95,74,74,0,0,0,0,92,92,0,0,92,92,94,0,0,0,74,0,0,0,0,0,0,0,15,15,15,15,0,15,0,0,0,0,0,0,0,43,75,75,42,74,45,76,75,43,43,90,90,15,15,46,90,0,0,0,0,0,0,0,0,0,0,0,90,90,95,0,30,15,92,76,75,45,46,45,30,0,30,30,15,0,15,15,73,27,60,93,42,45,42,27,59,60,21,72,39,42,27,75,59,89,75,58,0,43,57,46,62,76,76,57,0,42,43,0,75,60,75,44,42,0,0,90,21,95,10,95,91,91,90,90,94,0,94,94,95,0,95,95,91,0,0,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,45,92,94,0,0,43,75,75,59,60,43,60,43,57,41,0,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,90,95,95,0,27,29,26,26,26,27,42,90,0,0,22,43,0,0,74,44,27,75,75,59,28,75,59,75,59,43,75,57,59,59,59,26,59,26,91,42,0,59,28,42,26,27,27,75,0,27,27,0,42,59,59,43,26,0,0,90,21,92,76,92,75,91,90,74,0,0,10,13,0,0,12,13,90,0,0,0,0,0,0,0,0,93,93,0,0,0,0,59,59,0,29,43,90,93,93,0,0,43,75,75,43,92,60,75,75,74,74,27,59,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,90,45,0,43,11,43,26,27,12,0,0,0,45,45,27,0,27,27,13,27,0,0,0,27,26,0,27,0,11,59,0,0,0,15,27,0,0,0,27,29,72,0,0,0,58,59,75,60,27,29,42,27,12,28,28,14,0,0,0,0,95,94,90,95,75,0,0,0,10,10,14,0,15,15,15,90,0,0,15,0,0,0,0,0,0,95,0,0,0,0,0,0,0,0,0,0,0,0,0,0,90,27,29,45,29,29,30,45,27,30,42,44,29,27,46,15,75,13,15,28,47,0,0,0,0,0,0,95,95,95,0,44,44,25,27,28,13,12,43,0,60,60,60,0,59,59,43,74,26,72,28,59,28,28,59,44,43,27,57,27,27,44,44,57,57,57,43,0,60,60,28,29,28,28,57,44,27,44,0,60,58,61,60,28,0,0,0,92,95,90,74,95,95,95,95,0,74,74,75,0,43,28,43,58,0,0,0,0,0,0,0,92,75,0,28,59,0,0,0,0,0,0,15,47,76,45,0,0,90,75,44,107,75,93,28,92,29,28,0,0,0,0,0,0,0,0,91,-120,106,74,60,92,92,60,0,0,95,95,0,44,44,25,11,11,14,13,43,0,28,28,28,0,28,28,28,58,45,43,12,28,12,12,26,44,43,27,57,27,27,28,59,57,57,57,43,0,28,28,28,28,12,12,57,44,26,27,0,28,26,29,43,76,0,0,90,22,95,90,95,95,95,95,95,0,77,79,79,0,79,79,62,92,0,0,0,0,0,0,0,95,95,0,0,0,0,0,0,0,44,0,15,31,76,61,0,0,90,75,91,60,75,45,28,92,26,28,0,74,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,95,95,0,13,15,27,12,57,44,59,44,0,12,12,15,0,43,45,29,28,29,29,30,28,44,45,28,14,29,74,73,45,44,28,45,28,58,44,44,0,28,28,28,58,60,45,44,59,44,43,59,28,45,28,12,12,0,0,0,108,95,95,95,95,95,95,95,0,10,10,10,0,15,15,15,95,0,0,0,0,0,0,0,0,0,95,0,0,0,0,0,0,0,0,28,29,44,44,0,0,90,44,28,29,58,60,30,74,42,27,45,44,30,60,43,44,0,0,0,30,29,44,59,28,44,29,0,0,95,95,0,60,14,31,31,59,59,59,30,15,15,30,15,42,45,15,29,29,15,0,0,0,12,44,10,26,27,30,42,46,44,14,13,14,29,42,27,44,27,14,44,12,27,92,44,13,0,74,61,61,44,29,44,44,42,57,0,44,0,0,27,11,43,26,29,62,44,0,0,0,95,0,0,0,0,95,95,95,76,76,59,0,59,0,95,10,15,10,15,15,15,95,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,95,95,14,0,0,0,0,0,0,0,0,0,0,0,0,22,21,22,22,22,22,21,22,22,38,22,23,22,22,22,38,22,23,23,22,22,22,22,21,22,38,38,23,23,23,23,22,22,22,21,22,22,22,21,22,23,23,22,23,22,23,21,37,23,21,22,22,22,22,22,22,22,22,0,0,0,0,22,69,55,23,22,22,21,22,22,22,23,23,22,23,22,23,22,22,22,22,22,22,22,22,22,23,22,38,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,22,0,22,0,0,21,22,0,22,0,0,22,0,0,0,0,0,0,22,22,22,6,0,22,22,22,7,6,6,6,0,22,22,22,0,22,0,22,0,0,22,7,0,22,22,38,37,23,37,6,22,22,22,22,22,22,0,23,22,22,0,0,69,38,22,22,6,0,22,0,22,22,23,22,22,22,0,0,22,22,21,22,22,22,22,22,22,22,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,23,12,12,7,22,7,7,22,39,23,52,15,52,38,52,52,22,23,7,37,7,30,31,31,22,37,23,23,53,23,23,39,55,39,39,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,53,23,22,22,23,23,12,46,22,39,23,6,23,23,23,23,23,23,23,23,0,23,23,39,23,23,39,23,39,23,23,39,23,23,23,23,23,23,23,23,23,23,23,39,23,23,39,23,23,39,23,23,23,23,23,23,23,0,0,0,0,22,22,23,22,22,22,23,22,23,6,6,7,7,22,22,22,23,23,23,87,23,22,22,40,23,6,23,0,0,0,0,23,23,23,23,23,23,23,23,0,23,23,22,23,23,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,0,23,23,23,38,22,23,23,44,29,14,44,44,14,14,14,0,44,44,29,22,89,29,75,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,41,41,13,40,41,12,42,9,25,28,8,6,24,26,12,10,10,40,57,25,41,57,57,26,57,10,25,10,40,26,10,6,11,27,44,11,25,25,57,58,14,15,94,95,90,90,90,91,95,90,90,90,90,90,91,95,90,90,94,30,90,90,31,42,57,55,39,73,89,89,72,57,89,68,37,22,92,78,12,57,57,57,57,39,41,95,95,90,91,91,93,75,91,90,90,90,74,94,94,94,90,91,94,94,94,94,94,94,94,29,29,27,91,90,91,90,58,58,58,11,11,11,11,27,27,27,57,28,57,91,95,10,90,90,94,94,94,95,94,95,91,74,94,90,89,73,89,90,74,74,90,90,90,0,0,0,0,123,76,6,6,6,6,21,6,6,6,21,21,6,6,21,6,6,22,6,21,22,6,6,21,22,21,21,21,22,22,22,22,21,6,6,22,22,21,6,6,0,0,0,0,0,0,0,0,0,0,38,38,38,23,38,38,22,23,38,38,23,38,38,23,38,22,23,38,22,22,23,38,23,38,22,38,38,38,39,22,38,38,38,38,38,38,38,22,21,38,38,39,38,38,38,0,0,0,40,40,40,40,40,40,40,40,40,55,25,55,40,41,40,40,40,24,40,45,45,45,45,45,45,45,45,45,45,61,45,45,45,45,30,30,29,30,30,45,45,45,45,76,45,61,61,61,61,61,61,30,46,61,61,61,61,61,61,61,60,60,58,61,42,61,61,62,61,62,29,61,61,61,61,61,55,45,42,44,45,45,45,45,42,43,45,60,45,44,0,0,0,0,0,15,15,-66,-66,-66,-66,-101,-98,-101,-98,46,46,46,43,46,46,43,46,43,46,46,43,-69,44,46,46,46,46,46,43,46,46,46,46,46,46,46,46,45,46,46,46,46,46,46,46,46,46,47,46,46,46,46,47,46,46,46,-98,-98,46,46,46,-117,-117,75,46,59,60,0,0,0,0,0,91,76,77,91,76,77,91,91,76,59,59,60,59,60,60,91,91,60,59,44,106,91,76,91,91,75,91,29,30,45,45,44,30,45,45,45,31,45,45,15,45,30,31,31,15,44,30,45,45,46,45,45,45,61,31,46,46,46,74,45,45,46,91,60,61,44,61,59,61,29,45,45,89,44,29,45,75,29,29,29,29,74,0,0,0,0,0,0,41,45,75,74,74,59,44,30,74,44,44,74,44,58,45,78,60,30,30,60,29,60,60,47,45,14,30,45,30,44,44,29,60,29,30,60,45,60,60,30,58,43,59,44,60,58,60,44,74,44,44,74,60,91,74,77,42,45,45,59,45,59,59,44,60,43,43,60,43,60,43,43,61,0,31,60,60,30,0,0,60,43,43,60,43,44,60,0,44,0,31,60,60,30,0,0,74,44,44,74,61,44,74,77,42,45,45,42,28,28,42,44,28,28,28,28,28,45,45,45,45,45,45,28,28,29,45,45,58,45,45,75,45,75,45,45,29,0,30,61,45,29,0,0,89,91,74,42,90,41,44,42,60,44,44,44,43,26,44,44,74,44,44,74,44,74,74,42,59,29,29,59,29,59,59,44,29,0,30,62,29,29,0,0,27,14,14,27,31,28,27,0,15,0,15,30,31,15,0,0,44,14,44,44,44,14,44,30,75,45,28,59,28,75,75,0,74,44,44,74,61,43,74,77,45,29,29,45,29,29,45,45,74,75,60,91,60,27,44,30,60,45,44,75,44,44,59,78,75,45,44,58,44,60,59,78,43,29,28,43,28,45,43,46,57,44,60,74,60,59,74,74,45,0,45,74,60,45,0,0,75,44,60,75,60,59,74,59,59,29,29,59,29,44,59,62,14,15,15,30,15,13,30,30,75,45,45,75,45,44,75,61,75,45,45,58,44,44,58,61,75,45,11,7,11,75,75,44,60,59,43,43,44,41,43,43,60,60,60,60,60,60,60,60,60,45,44,0,0,0,0,75,60,103,90,90,90,56,89,120,75,59,76,75,75,59,59,59,60,75,75,60,60,60,75,60,75,44,59,75,45,0,0,0,15,15,29,14,44,45,42,30,61,45,60,43,61,44,60,45,52,37,52,52,37,28,22,30,30,22,0,0,0,0,0,0,22,23,23,23,23,38,23,22,22,22,22,23,22,22,22,23,22,22,23,22,22,22,22,22,22,23,22,22,22,23,22,23,22,23,22,22,22,23,23,22,22,23,23,22,23,23,22,22,23,23,22,22,23,23,23,23,23,23,22,23,23,22,22,22,23,23,22,23,23,23,22,23,23,23,23,22,23,22,22,23,22,22,22,23,22,0,0,0,0,0,0,0,0,0,0,0,0,23,23,23,23,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,22,23,23,23,23,22,23,22,21,37,37,23,23,37,37,23,37,36,21,21,21,23,23,22,22,23,23,23,23,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,22,37,51,20,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,21,23,23,23,23,21,21,21,21,21,21,22,21,21,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,37,37,23,23,23,23,21,21,21,21,21,21,22,21,21,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,37,37,21,21,21,21,21,21,22,21,21,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,37,37,37,37,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,37,22,21,22,21,21,21,21,22,21,21,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,36,22,36,22,36,36,36,44,45,44,44,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,37,21,21,21,23,23,23,23,23,23,21,21,21,21,21,21,23,23,36,37,22,23,23,23,23,23,23,23,23,7,23,21,23,23,23,23,23,23,23,23,23,23,23,23,21,23,23,23,23,44,23,23,23,23,23,23,23,23,23,23,23,21,22,21,44,44,44,44,44,44,44,23,21,21,21,21,23,23,23,23,31,31,31,31,31,31,31,23,31,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,21,23,23,23,23,23,21,21,21,21,22,22,22,22,23,23,23,23,23,23,23,23,23,23,22,22,22,22,23,23,22,22,22,22,23,23,22,22,22,22,23,23,23,6,6,23,22,22,22,22,22,22,22,22,22,22,6,22,22,22,6,6,6,6,21,23,23,22,22,22,22,23,23,22,22,6,22,23,23,22,22,6,22,21,22,22,6,6,6,6,23,23,22,22,6,22,22,22,22,22,22,22,6,6,6,44,44,44,44,44,75,75,46,46,46,29,60,60,21,21,21,21,60,60,6,6,6,23,6,6,6,6,6,6,76,59,7,7,7,7,45,28,12,12,12,46,6,6,60,60,60,60,6,6,6,6,60,60,60,60,44,44,44,44,44,44,44,44,44,44,44,44,21,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,23,36,44,31,31,31,31,31,31,31,0,0,0,0,0,0,0,0,0,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,47,13,0,0,0,6,6,5,6,5,6,4,4,4,6,6,4,6,54,20,6,6,6,21,6,6,6,6,6,6,4,6,6,6,36,6,38,6,51,36,6,6,6,4,6,6,6,20,21,51,36,6,6,4,5,21,21,4,6,6,6,6,36,22,6,6,6,6,6,12,6,12,6,6,6,6,51,6,21,6,36,19,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,30,75,30,45,30,30,30,59,30,28,30,30,0,30,76,46,46,93,92,90,0,0,0,0,0,0,0,0,0,0,0,29,29,91,59,59,59,60,44,28,29,41,45,28,60,43,30,30,60,93,92,62,89,60,0,0,0,0,0,0,0,0,0,44,44,75,59,91,61,30,44,92,45,74,60,45,60,43,60,44,74,93,92,0,0,0,0,0,0,0,0,0,0,0,0,30,30,36,44,29,30,13,30,29,30,45,30,30,0,30,30,29,0,93,92,0,0,0,0,0,0,0,0,0,0,0,0,75,90,75,44,74,43,75,46,61,90,91,59,46,45,45,75,75,77,59,90,76,75,76,59,59,61,89,61,90,76,60,60,61,60,44,44,29,91,62,91,75,59,75,76,78,76,77,75,76,91,75,92,15,15,94,92,91,92,92,90,90,90,11,15,15,10,10,10,14,14,90,95,94,90,75,90,92,92,90,94,93,43,15,90,56,24,20,30,15,74,15,90,38,75,0,0,74,59,44,44,77,75,59,46,60,44,0,0,0,0,0,0,20,21,34,7,21,21,36,6,36,21,0,0,0,0,0,0,29,120,74,74,75,14,120,72,60,60,120,15,15,15,15,0,45,45,45,45,45,44,45,45,44,45,0,0,0,0,0,0,60,28,44,60,60,60,60,60,28,61,60,44,60,12,78,78,76,78,75,43,40,24,40,24,56,28,44,44,40,40,44,45,45,40,56,123,60,77,60,28,60,60,24,24,60,28,28,77,75,56,24,24,40,56,40,62,78,40,29,44,24,46,40,75,61,56,24,30,61,62,60,60,77,76,24,45,62,62,78,77,60,44,28,24,46,40,45,60,0,0,0,0,0,0,0,0,60,105,75,44,44,90,14,60,61,45,28,24,40,56,40,40,40,40,61,29,30,46,45,60,56,43,62,60,78,76,45,44,44,77,28,45,62,62,72,24,61,10,62,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,56,9,25,8,27,25,24,24,39,42,24,25,25,24,56,10,8,25,24,24,23,57,40,8,26,38,41,25,0,0,0,75,92,75,94,95,78,79,58,42,95,94,94,0,0,0,0,95,95,90,93,95,95,95,93,95,92,90,74,0,0,0,0,25,0,0,0,20,25,74,75,59,74,44,74,74,58,59,58,21,21,21,6,6,22,6,6,22,37,6,37,6,37,6,7,21,6,22,85,37,37,37,37,37,37,37,37,37,6,0,0,23,21,21,22,21,0,0,0,0,0,0,0,0,0,0,0,75,58,59,90,59,59,58,58,90,75,93,73,74,42,42,90,59,74,59,74,60,60,59,59,75,59,57,59,43,59,75,59,59,73,59,74,73,59,59,74,59,59,0,0,0,0,0,0,95,95,95,95,95,10,10,10,95,95,10,95,95,95,95,95,95,89,58,89,59,59,73,59,95,95,0,0,0,0,0,0,75,76,90,106,75,59,90,44,30,74,0,0,0,0,45,45,76,89,59,59,73,73,89,76,74,89,45,45,30,30,45,45,74,73,59,59,72,74,73,76,74,73,45,45,30,30,45,45,23,60,38,7,60,6,23,43,23,23,23,60,60,60,29,30,29,21,29,29,108,29,29,94,95,10,95,10,0,0,7,21,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,59,74,90,91,95,45,14,44,15,75,61,60,14,46,15,58,15,10,15,30,30,29,29,27,60,28,44,15,15,27,27,28,43,15,30,43,60,45,60,43,44,14,28,44,29,43,14,60,15,27,43,14,90,95,58,58,95,95,75,79,28,31,26,10,31,15,58,63,95,30,15,30,60,60,43,15,0,0,0,0,105,31,46,10,72,75,58,14,44,30,15,15,105,91,90,45,89,-49,89,89,89,107,58,75,36,14,53,90,90,90,90,90,74,91,90,90,21,20,5,5,20,20,20,5,35,0,0,0,90,75,94,58,73,91,61,92,57,58,45,29,92,91,61,44,93,62,60,43,106,45,77,77,46,75,61,90,75,91,45,60,79,94,92,91,90,90,10,95,90,90,95,0,0,0,31,31,75,92,91,92,91,30,91,75,60,91,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,59,57,75,60,45,108,58,60,77,44,107,75,58,59,76,59,60,60,59,74,75,59,75,60,61,44,90,76,59,60,74,74,59,74,61,59,95,95,95,10,10,13,95,95,90,90,90,90,74,90,42,42,10,10,93,90,0,0,0,-120,105,106,91,60,89,105,75,75,75,75,59,106,90,106,0,0,0,59,59,44,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,6,22,22,22,6,22,22,6,22,6,22,6,6,6,6,22,7,6,6,7,22,22,6,6,6,52,52,52,6,36,21,51,37,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,21,22,6,6,22,21,5,21,22,21,21,38,22,38,21,22,22,23,23,23,23,21,38,21,22,22,22,23,22,39,23,23,23,22,22,22,22,6,22,23,23,22,23,22,21,22,21,6,21,21,21,22,22,21,21,38,38,38,21,21,22,21,22,22,23,22,22,22,22,22,6,21,21,22,22,22,22,22,21,20,23,22,22,22,38,38,22,21,22,22,23,22,6,22,23,22,23,22,21,22,22,22,22,23,21,23,22,7,6,23,21,7,23,23,23,23,7,22,6,22,22,22,45,21,21,23,23,23,23,23,21,23,22,38,23,23,22,22,23,22,22,22,22,22,23,23,23,23,22,60,39,22,21,7,22,22,21,21,38,38,21,38,20,21,21,37,21,21,22,21,36,54,23,23,23,23,38,22,21,37,21,22,23,23,38,22,38,38,23,23,22,38,22,22,22,22,22,22,22,6,6,6,22,6,6,7,22,22,23,6,22,6,6,6,7,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,22,6,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,21,22,22,21,22,21,22,22,22,22,22,6,23,22,22,38,38,22,22,22,22,22,22,22,38,22,38,22,38,22,38,22,23,22,23,22,23,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,21,23,21,23,21,23,22,22,22,22,22,22,22,22,22,22,22,23,22,23,21,22,23,22,23,22,23,21,23,21,23,21,21,22,22,23,22,22,22,22,22,22,22,22,21,23,22,22,37,21,21,22,22,21,22,22,22,22,22,22,22,22,23,39,23,38,22,21,22,21,22,6,22,6,22,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,38,38,38,38,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,22,23,22,23,22,23,22,6,7,22,21,22,22,22,22,22,22,22,22,6,6,6,6,6,6,6,6,6,6,22,22,22,22,22,22,0,0,6,6,6,6,6,6,0,0,22,22,22,22,22,22,22,22,6,6,6,6,6,6,6,6,53,53,21,21,21,21,22,22,6,6,6,6,6,6,6,6,22,22,22,22,22,22,0,0,6,6,6,6,6,6,0,0,22,22,22,22,22,22,22,22,0,7,0,7,0,7,0,7,23,23,23,23,23,23,23,23,7,7,7,7,7,7,23,23,22,22,22,22,22,22,53,53,22,22,22,22,23,23,0,0,22,22,22,22,22,22,22,22,6,6,6,6,6,6,6,6,22,22,22,22,22,22,22,22,6,6,6,6,6,6,6,6,23,23,23,23,23,23,23,23,7,7,7,7,7,7,7,7,22,22,22,22,22,0,22,22,22,22,6,6,22,52,53,52,22,22,22,22,22,0,22,22,6,6,6,6,22,21,21,22,21,37,22,22,0,0,22,22,38,38,6,6,0,21,21,22,22,22,22,22,22,22,22,22,23,23,7,7,6,22,22,52,0,0,23,23,23,0,23,23,6,6,7,7,23,52,52,0,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,37,37,22,22,23,7,53,7,52,52,52,52,22,22,22,22,23,23,21,37,52,38,23,52,15,15,15,15,15,15,15,15,7,7,37,7,7,37,7,7,21,70,19,23,37,22,22,22,22,22,23,37,22,70,19,22,22,22,21,6,38,21,23,52,22,23,22,7,22,6,37,15,21,21,51,6,21,51,51,15,15,15,15,15,15,0,0,0,0,0,15,15,15,15,15,15,38,68,0,0,38,38,38,38,38,38,38,38,38,69,52,38,38,36,38,38,38,38,38,38,38,38,38,38,38,69,52,0,21,21,21,22,21,0,0,0,0,0,0,0,0,0,0,0,22,22,22,7,23,23,23,23,23,23,22,22,22,6,23,23,39,7,22,7,7,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,22,6,6,22,22,22,22,7,7,7,22,23,14,30,14,14,7,30,30,46,22,22,46,22,7,61,22,22,22,22,22,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,23,23,6,22,23,23,22,22,6,23,7,7,23,22,6,38,23,7,22,7,23,7,7,23,22,23,23,7,23,22,22,7,7,7,7,22,22,23,23,22,22,21,22,23,22,22,21,22,22,22,7,23,22,23,22,23,22,44,30,61,61,23,44,60,22,22,22,6,60,60,6,22,6,22,23,29,7,21,29,0,0,0,22,22,22,22,22,6,22,22,22,22,22,22,22,53,22,23,23,23,7,7,7,23,23,23,23,22,22,22,23,53,22,23,23,23,23,23,7,23,23,23,23,53,22,23,23,22,22,59,22,22,6,6,7,29,0,0,0,0,0,0,0,22,38,22,38,7,38,22,22,22,22,23,23,23,23,7,38,7,38,23,23,22,38,22,38,38,23,23,23,23,7,7,22,21,38,21,38,23,21,7,7,22,22,22,22,22,22,70,36,22,22,70,36,22,7,22,22,7,22,7,22,22,6,7,23,22,38,22,38,7,38,22,22,22,22,6,23,7,7,38,38,23,38,23,38,22,22,7,23,7,23,23,23,23,23,23,23,23,23,23,38,29,7,7,7,7,44,28,45,29,7,7,45,23,37,22,22,22,23,23,23,22,22,22,22,22,22,22,23,23,22,22,23,23,22,22,38,37,37,23,23,23,22,23,22,22,22,22,52,22,37,22,23,23,22,22,38,23,7,38,23,7,39,39,22,22,22,52,22,22,22,22,22,22,22,23,23,52,22,22,22,22,22,22,22,22,22,22,22,23,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,7,7,37,22,38,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,22,22,23,23,37,37,22,22,23,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,38,38,38,38,22,22,23,23,22,22,38,52,38,38,38,38,38,38,38,22,23,23,22,22,23,23,38,22,38,21,23,23,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,52,7,7,7,27,59,22,59,59,22,25,25,44,42,22,42,22,76,23,6,22,38,38,38,38,53,54,20,54,20,71,20,71,20,22,23,23,23,23,23,23,7,23,22,7,22,20,54,20,54,70,36,22,22,23,23,7,23,22,20,54,7,23,7,23,22,23,7,23,23,22,22,23,38,23,23,23,23,23,23,23,7,7,23,23,23,23,23,23,23,23,23,23,38,23,23,23,23,23,38,38,23,23,23,23,23,38,38,23,38,23,23,23,23,23,23,38,38,38,23,23,23,22,22,37,23,23,22,38,23,23,22,22,54,22,23,22,22,54,23,22,30,30,29,60,38,6,29,29,29,45,59,30,30,44,12,12,12,30,45,45,59,59,59,59,29,30,23,21,29,29,14,29,38,35,38,21,69,21,38,35,38,21,69,21,54,20,54,52,20,54,20,68,7,22,22,30,30,30,30,30,23,0,119,7,7,7,7,38,38,44,44,44,44,44,44,44,44,44,44,44,39,39,29,30,6,68,6,6,6,14,14,14,30,30,30,6,15,15,15,14,14,30,30,29,76,30,29,29,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,30,29,29,30,30,29,30,26,27,27,27,27,27,27,27,28,29,29,29,29,30,30,29,29,27,30,30,25,25,25,25,25,30,22,22,22,23,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,38,38,38,38,38,38,22,7,23,23,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,14,14,14,14,14,14,14,14,14,30,30,30,30,30,30,30,30,30,30,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,73,59,59,60,59,59,60,59,59,12,11,13,13,12,13,13,13,13,13,29,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,15,30,30,30,30,30,30,30,30,30,30,15,15,15,15,15,15,15,15,15,15,30,7,7,68,52,7,7,68,52,6,6,68,52,71,71,55,55,4,4,4,4,71,71,55,55,4,4,4,4,71,71,55,55,55,55,55,55,4,4,4,4,4,4,4,4,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,6,6,68,52,7,53,71,55,55,4,5,5,71,55,55,4,5,5,71,55,55,4,5,5,7,7,7,7,7,7,7,7,7,71,4,4,71,7,7,7,3,68,71,68,3,52,71,52,7,52,7,52,15,15,15,15,15,15,15,15,15,13,11,9,7,5,3,1,-113,14,15,15,15,-17,7,-113,7,15,15,15,15,-113,15,15,23,23,23,23,23,23,23,23,23,23,37,37,23,23,37,37,7,7,22,22,22,22,22,22,37,37,22,22,22,22,22,22,22,22,37,37,22,22,23,23,23,23,22,23,23,23,23,23,23,23,23,23,23,23,20,71,7,7,7,7,20,71,71,20,23,23,22,22,22,22,21,22,22,22,22,23,23,22,22,7,22,22,22,22,7,7,7,7,22,22,22,22,22,37,37,22,23,7,23,15,23,23,23,38,22,7,7,7,21,23,23,23,23,23,23,38,23,45,44,44,29,30,23,23,23,37,23,37,7,37,29,14,29,38,38,22,38,23,7,44,61,7,23,29,29,29,29,29,29,29,29,29,23,7,7,7,23,39,22,38,38,38,23,22,22,23,7,22,23,23,23,23,7,23,23,23,23,22,22,22,23,23,23,23,22,22,23,23,23,23,22,22,23,23,38,23,23,23,38,23,7,20,22,7,7,22,22,22,23,23,15,14,14,14,14,14,14,14,14,15,14,14,12,45,59,59,59,59,59,59,12,12,12,12,29,29,29,29,29,29,22,22,15,30,29,59,14,44,44,29,14,29,14,14,0,0,29,22,44,44,60,75,44,14,6,13,6,6,38,44,61,29,47,44,6,5,6,6,29,22,22,7,6,6,23,0,0,0,45,45,30,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,13,14,14,0,14,14,14,30,0,0,14,31,28,14,28,14,14,29,14,44,14,28,28,14,14,14,14,59,29,29,14,14,14,14,14,15,14,14,0,14,14,14,14,14,14,14,15,14,14,14,14,14,29,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,30,14,14,14,14,0,46,0,62,62,62,62,0,0,0,29,0,120,105,75,74,74,14,14,0,0,30,59,59,14,29,14,30,54,37,38,38,38,38,39,22,23,6,53,36,22,22,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,0,0,0,44,14,44,14,14,14,14,14,14,14,30,30,30,14,14,91,14,30,30,14,14,14,14,15,0,15,30,14,44,30,44,44,14,44,14,14,14,14,14,0,22,44,21,60,60,38,38,6,30,30,36,0,45,0,0,0,76,6,59,22,22,45,45,30,59,59,29,29,30,30,30,121,6,29,14,31,14,31,38,21,37,37,22,22,54,20,70,19,44,44,45,45,14,14,14,13,14,14,13,13,30,13,30,14,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,14,14,31,14,46,30,31,30,89,89,59,59,31,14,31,14,13,14,23,23,30,30,30,30,30,30,30,30,30,31,31,30,30,46,46,45,28,28,28,30,46,30,29,45,45,46,46,46,46,30,30,30,29,29,60,60,37,37,45,45,45,45,45,45,44,44,30,30,30,14,14,14,14,21,30,30,21,21,30,36,30,36,31,14,21,21,31,14,21,21,46,29,21,21,46,29,21,21,30,76,30,76,30,30,30,30,30,30,30,30,76,76,30,14,14,31,14,14,76,30,76,76,30,76,23,23,59,59,38,105,37,22,22,37,37,37,37,37,37,70,19,54,20,54,20,38,21,61,61,61,61,37,37,119,104,46,45,45,46,46,61,61,46,46,46,46,22,22,46,46,46,46,45,45,45,45,59,59,59,59,59,44,59,59,59,59,59,59,61,59,61,61,61,61,29,45,27,27,27,27,27,45,29,29,31,14,59,44,44,6,6,6,6,6,6,6,36,36,91,91,44,59,44,30,45,46,44,30,30,30,30,59,29,29,59,6,91,91,57,57,57,57,57,57,30,21,21,21,23,6,6,6,6,6,29,29,61,61,61,58,59,59,59,30,30,44,59,59,13,74,74,74,74,61,74,74,75,74,74,59,74,74,74,74,74,44,22,37,60,37,21,21,5,21,22,74,21,21,21,21,21,74,74,38,21,21,21,59,75,75,44,14,8,29,29,29,22,22,53,60,75,75,75,75,59,59,37,37,37,37,6,6,75,75,59,59,46,59,59,44,44,45,45,60,77,29,44,44,44,44,59,59,59,59,59,59,44,44,23,23,44,44,5,5,5,5,44,5,5,5,5,5,44,29,30,5,45,59,59,43,60,74,74,74,74,74,74,74,74,75,58,74,74,75,58,5,5,75,58,5,5,21,21,22,22,74,74,74,74,74,74,74,74,75,75,75,75,59,59,45,93,29,59,59,59,59,59,59,59,59,44,59,76,59,59,59,59,59,59,59,59,59,59,30,30,44,44,22,22,22,22,22,22,22,22,44,44,22,22,22,22,30,30,59,59,59,59,22,22,22,22,29,29,59,59,59,59,59,37,59,59,44,43,45,60,45,45,59,59,59,59,59,60,60,21,21,21,21,6,75,74,44,51,44,44,74,74,44,74,75,36,36,44,44,44,44,30,29,6,6,44,44,44,44,30,6,30,30,30,30,60,60,60,60,60,60,60,60,44,30,30,37,37,45,45,44,44,30,30,6,6,6,6,21,21,21,14,14,22,22,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,14,14,30,30,30,30,30,30,0,0,0,29,44,44,61,61,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,21,6,6,6,6,21,6,6,7,6,6,6,7,37,6,61,6,38,6,22,6,6,7,6,6,6,6,6,7,6,6,60,6,6,6,22,6,6,6,45,28,13,7,6,53,6,22,0,21,38,6,6,6,21,6,23,7,6,6,6,6,37,22,44,22,38,22,21,6,6,6,6,6,6,22,38,6,6,21,60,22,6,6,21,22,22,6,45,45,30,6,22,53,22,22,0,22,21,23,22,22,7,7,22,22,23,23,22,22,22,22,22,0,7,6,23,6,21,21,23,6,22,6,38,53,21,0,0,7,7,22,21,22,21,7,6,6,6,22,22,6,6,22,22,7,7,36,36,22,22,7,6,6,21,6,21,7,6,6,6,7,22,22,22,22,22,6,6,7,7,6,6,23,23,7,7,6,6,6,21,22,21,6,22,21,37,6,22,7,7,7,7,6,6,6,6,7,5,37,37,7,7,22,22,6,6,76,75,22,21,22,22,37,37,6,22,7,22,7,23,6,6,22,22,7,7,6,6,22,7,7,14,23,23,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,21,21,6,37,22,37,22,6,6,21,6,43,22,60,22,37,60,38,21,44,7,7,59,21,5,60,60,6,6,22,22,37,5,59,60,21,22,7,44,21,21,21,22,59,0,0,0,0,0,0,0,0,0,0,37,6,6,6,6,6,6,21,21,22,22,22,6,6,21,6,6,6,52,7,21,6,22,6,60,36,21,6,6,37,22,51,6,51,37,37,6,6,21,52,7,6,6,23,21,6,23,22,21,21,36,6,37,6,0,0,0,0,0,0,0,0,0,37,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,46,29,9,10,9,43,43,61,61,43,8,44,44,44,45,30,28,44,29,13,27,30,0,0,0,0,0,0,0,0,0,44,29,29,44,29,26,45,0,60,60,60,60,60,44,28,0,30,30,30,30,30,30,30,0,15,15,15,14,15,14,14,0,60,60,60,43,60,44,60,0,76,60,60,76,60,60,76,0,11,14,14,11,14,11,11,0,42,27,27,25,27,41,25,0,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,22,22,22,22,22,22,22,22,22,6,37,37,37,37,37,37,21,21,5,5,5,21,21,21,29,30,30,31,18,44,44,44,37,22,22,7,22,23,21,21,23,23,37,37,37,37,37,37,22,22,22,5,22,22,22,7,22,52,37,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,59,13,44,59,14,73,27,14,43,-99,91,45,59,44,14,30,14,30,45,28,14,28,5,29,5,14,0,13,44,59,30,14,45,4,13,13,29,30,22,14,5,14,5,44,31,5,14,7,5,44,44,29,14,45,14,14,14,14,14,14,28,29,14,14,14,13,5,29,14,14,12,28,5,44,14,14,14,14,14,-83,6,14,14,14,29,60,21,14,14,30,29,14,14,14,6,7,6,14,13,14,14,14,29,46,14,14,46,14,14,14,14,14,14,14,14,14,0,0,0,0,0,0,0,0,0,0,0,0,15,119,90,25,14,87,15,14,30,30,14,14,45,14,5,30,29,28,29,29,60,29,45,14,108,90,14,30,14,44,29,14,14,14,14,59,14,14,14,30,14,14,14,13,44,29,29,14,46,44,14,29,14,14,30,13,44,28,29,57,14,29,13,14,30,14,14,14,14,14,14,59,46,27,30,14,14,14,30,14,46,14,45,30,14,29,14,14,14,28,14,30,30,14,30,14,14,14,14,14,13,44,14,14,14,44,14,14,60,14,14,14,14,45,14,30,14,14,14,29,30,29,30,29,14,30,30,14,30,45,46,60,14,45,14,14,14,46,29,30,14,29,14,14,14,14,46,29,30,14,14,14,29,44,14,14,14,30,14,14,14,14,46,14,14,14,14,14,45,14,30,14,14,30,14,30,14,14,14,14,30,14,14,13,14,14,29,30,14,14,29,45,29,30,29,30,46,14,14,14,14,14,14,14,30,29,14,46,14,14,14,14,14,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,14,14,14,14,14,14,14,14,14,14,14,0,0,0,0,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,0,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,0,0,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,0,0,0,0,0,44,44,44,45,44,29,44,44,58,29,13,74,89,29,29,57,12,74,43,29,44,60,29,29,29,29,60,59,59,44,60,29,44,29,119,27,44,29,29,30,29,0,0,0,45,29,15,45,14,15,29,30,44,14,30,29,14,30,15,15,44,59,45,30,45,14,59,28,14,45,45,44,29,29,-97,124,-97,124,107,109,107,109,31,31,29,28,31,31,28,28,28,31,31,28,-101,15,14,15,14,15,15,30,15,14,14,29,30,31,45,15,14,15,15,14,14,44,44,14,30,14,30,15,44,30,44,14,14,29,15,29,31,28,28,28,12,28,104,92,0,-120,106,41,41,43,42,41,41,41,41,41,42,42,42,44,41,44,44,74,45,29,29,29,29,29,60,30,30,29,30,29,60,29,29,29,55,39,23,22,23,0,0,0,0,0,0,0,0,41,87,59,14,75,43,43,43,30,44,41,45,106,30,43,77,30,119,90,73,89,60,29,61,43,90,104,90,60,46,44,76,30,45,43,29,0,0,0,0,0,0,0,0,0,0,0,0,22,23,6,37,6,59,22,6,75,6,60,6,5,77,5,59,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,0,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,0,0,0,0,0,0,0,0,0,0,0,0,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,0,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,0,0,0,0,0,0,0,0,0,0,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,27,27,27,44,59,74,74,75,44,74,74,59,59,59,75,59,28,28,28,44,44,59,91,59,59,91,44,59,59,76,74,59,59,59,75,75,74,59,74,74,59,59,59,74,59,74,74,90,74,74,59,75,75,43,29,29,44,58,58,59,59,59,59,59,59,59,75,75,75,75,59,74,74,75,90,58,75,75,59,59,59,44,44,43,59,59,59,59,59,44,59,28,45,60,45,30,30,59,74,74,59,59,74,74,59,43,60,45,45,43,60,60,58,58,59,59,59,74,75,75,75,60,59,59,74,45,59,59,59,44,59,59,59,75,58,58,59,29,29,75,59,59,30,44,44,74,59,59,59,59,59,75,44,45,45,45,60,60,44,60,44,44,74,74,74,74,59,59,59,75,75,75,59,59,59,59,59,60,44,44,60,60,59,59,59,59,59,59,45,59,76,76,75,45,45,60,42,59,60,59,59,75,59,59,75,59,91,91,75,29,60,60,60,58,75,59,59,44,44,59,44,44,59,43,43,43,59,44,44,59,43,59,59,44,59,59,59,44,60,60,60,45,59,59,45,43,43,59,58,58,29,59,59,45,59,59,45,59,59,59,45,45,30,60,60,60,29,74,74,74,44,44,44,59,59,60,75,44,44,59,60,60,44,43,60,60,43,43,75,75,59,59,75,29,75,75,29,74,44,44,29,60,60,59,59,59,59,60,44,44,60,59,59,60,44,44,44,59,45,45,44,74,74,74,74,59,59,59,74,74,44,59,59,59,60,59,59,60,74,74,74,44,44,60,60,60,44,75,59,59,59,45,45,44,74,59,59,59,59,59,60,60,60,44,44,59,59,30,60,44,44,74,44,44,29,45,45,74,74,45,30,44,75,75,59,59,59,44,60,60,60,44,44,75,75,30,30,45,74,74,44,75,59,59,60,59,59,60,59,59,59,43,60,75,75,59,59,60,74,74,44,45,45,44,59,59,59,60,75,75,59,44,44,75,43,60,60,29,29,44,73,73,59,74,73,73,74,60,74,74,45,75,60,60,44,59,74,74,74,74,74,60,59,59,59,29,44,44,29,60,60,60,90,90,44,75,75,44,59,59,59,44,59,59,59,59,29,29,29,59,29,44,44,59,44,74,74,44,59,59,59,59,44,44,90,59,75,59,44,44,44,60,60,60,44,60,60,74,59,59,59,59,59,74,74,59,59,59,59,59,57,74,59,74,74,60,60,60,60,75,75,44,75,44,44,59,44,74,74,73,59,59,59,44,60,59,59,59,59,59,30,30,59,60,60,45,44,44,74,74,59,59,60,59,59,44,44,74,74,59,59,59,75,44,44,59,73,59,59,75,45,45,45,45,44,90,90,59,74,74,74,44,59,59,44,60,60,30,44,60,60,44,44,89,89,59,74,74,60,74,74,59,60,60,60,59,43,60,60,60,44,44,59,60,59,59,44,45,45,74,45,60,60,60,75,75,75,75,75,74,60,60,60,59,59,59,75,60,60,60,60,60,60,59,73,73,45,74,29,29,59,60,60,74,74,89,89,59,29,29,60,73,73,75,45,45,60,59,59,74,30,91,91,59,59,59,59,60,59,59,74,44,44,44,59,59,59,59,45,29,29,59,75,75,74,90,90,74,59,59,60,44,44,90,59,59,59,60,60,59,59,59,75,75,59,75,75,59,59,59,60,90,90,59,75,75,74,57,74,75,74,74,75,59,59,59,74,74,74,60,59,59,59,59,90,90,59,59,29,59,59,59,90,106,59,59,59,44,59,59,59,29,29,59,60,59,59,59,90,90,74,90,59,59,74,75,75,59,59,58,75,43,59,59,59,59,59,59,59,59,60,91,91,60,60,60,60,59,44,44,59,60,59,59,59,74,74,59,60,59,59,59,59,44,44,44,59,59,59,59,59,44,44,44,74,44,59,59,59,44,44,44,59,45,60,60,74,60,60,74,59,59,75,60,60,74,74,74,74,44,44,44,45,14,60,60,29,59,59,59,44,60,60,75,29,29,60,60,60,75,60,60,45,45,59,59,44,74,74,59,28,74,74,60,59,59,59,44,74,74,60,75,75,75,60,60,59,59,59,75,75,75,75,60,45,45,59,74,45,45,29,59,59,75,43,43,76,60,60,60,44,44,29,74,74,60,59,59,59,45,59,60,60,60,74,44,44,75,75,75,60,90,90,59,58,75,60,60,60,74,74,74,59,44,74,74,44,75,75,59,74,43,60,59,74,74,60,91,91,45,60,60,74,43,60,60,45,29,29,75,59,59,59,60,59,59,59,59,59,90,90,75,59,59,60,74,74,74,60,60,45,74,74,59,45,44,44,60,75,59,59,59,59,59,59,59,75,59,59,59,59,59,60,74,74,74,59,59,74,59,59,59,58,44,44,74,59,59,60,45,29,29,59,59,75,75,74,75,75,44,74,74,75,59,59,59,45,59,59,59,59,59,59,29,30,30,74,90,90,75,28,45,59,59,59,74,59,59,59,60,60,73,30,30,30,29,75,75,44,44,58,58,74,59,75,75,75,59,60,60,74,59,59,59,44,74,74,59,60,60,14,74,43,60,60,43,60,44,45,45,74,45,59,59,59,59,59,59,58,59,75,75,75,74,59,59,74,74,74,74,73,73,59,44,44,0,0,0,119,73,90,90,74,90,73,44,60,60,90,105,75,75,74,90,74,74,74,60,59,74,44,74,59,89,59,74,59,59,60,90,89,89,90,59,29,59,29,44,75,58,75,90,89,74,74,44,60,44,45,74,74,59,89,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,44,44,44,44,29,29,76,76,59,59,59,89,89,60,60,29,29,60,60,44,60,74,74,59,59,29,29,44,44,14,44,45,76,45,29,60,61,61,90,60,62,74,30,59,59,44,44,44,59,59,61,61,75,75,90,75,60,30,44,44,43,45,74,29,29,29,74,74,44,44,91,76,89,44,45,30,29,61,44,44,30,45,44,59,44,59,75,74,60,59,59,60,45,75,75,44,45,59,59,59,29,60,44,59,29,74,45,44,30,29,74,29,60,60,60,60,60,75,75,59,30,74,44,74,44,44,58,58,30,30,44,44,29,44,44,44,89,59,74,74,60,88,29,29,44,29,29,74,45,28,28,14,44,28,45,29,60,60,44,44,44,29,30,74,58,59,29,60,60,60,60,59,59,59,59,60,60,60,60,44,89,89,74,59,74,59,44,44,45,29,59,45,30,30,60,60,45,45,29,29,75,75,59,60,59,60,90,75,44,44,75,77,75,77,75,60,60,91,76,29,59,59,75,30,60,30,45,44,30,44,44,74,77,58,59,30,30,29,29,29,76,76,29,29,60,30,59,59,59,59,29,29,29,29,44,44,44,44,44,44,44,44,44,60,59,44,61,45,89,75,29,30,59,30,90,45,29,74,44,44,76,44,61,44,43,30,43,59,-120,93,25,74,75,58,59,90,60,29,91,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,22,22,22,22,22,37,37,6,21,22,22,29,29,23,23,43,43,22,22,22,22,6,22,22,22,23,23,6,23,43,7,0,0,45,60,45,44,45,44,22,22,6,23,45,45,31,22,13,22,14,6,0,0,0,0,0,0,0,0,22,22,22,70,7,23,22,37,30,30,6,6,7,23,7,7,6,6,23,23,23,23,43,60,22,21,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,37,37,37,37,37,37,37,19,19,19,19,19,53,53,53,53,53,54,54,54,54,54,19,54,21,21,21,21,51,51,51,22,22,21,37,22,21,22,22,44,45,22,38,21,21,7,7,21,22,45,45,45,45,45,45,44,44,44,45,44,44,22,22,6,6,22,22,6,6,22,55,6,38,7,7,7,7,45,45,6,6,7,7,7,7,23,23,60,60,22,22,6,22,7,6,22,23,22,21,6,22,6,22,21,38,21,21,22,22,22,22,22,45,44,29,28,59,28,27,6,6,22,22,21,22,22,22,22,38,23,22,21,21,6,6,22,52,37,68,68,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,22,22,38,15,15,15,90,15,15,15,91,15,15,15,15,92,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,95,95,15,90,95,20,20,29,59,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,58,58,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,59,60,61,60,60,0,0,0,0,0,0,0,0,94,94,8,9,8,8,8,8,8,9,8,8,8,8,8,8,8,8,24,10,8,8,8,8,8,8,8,8,8,8,8,8,8,8,24,8,8,8,8,8,8,9,8,8,8,8,8,8,8,8,8,8,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,92,0,0,0,0,0,0,0,0,0,-120,105,75,106,75,73,106,76,90,90,90,74,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,45,45,59,76,59,60,60,60,60,61,44,45,59,45,44,60,59,45,75,60,60,45,59,44,76,75,75,44,44,60,45,59,29,60,45,60,45,45,90,74,59,58,58,90,58,58,60,-120,58,58,29,58,59,45,75,59,44,76,29,29,45,45,60,29,43,44,60,61,45,29,59,10,10,10,94,10,10,10,94,94,94,94,94,94,0,0,0,0,0,0,0,0,0,0,0,60,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47,27,74,61,60,60,59,46,60,46,44,44,44,47,29,45,31,31,31,60,62,44,60,46,46,46,28,45,14,44,30,30,45,45,30,76,61,45,30,45,45,93,95,95,95,94,95,10,10,94,93,95,10,93,95,0,0,0,0,0,0,0,0,0,59,43,45,93,45,60,30,44,30,77,44,29,90,95,0,0,75,43,46,46,29,57,76,77,45,45,0,0,43,-120,105,91,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,0,0,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,0,0,0,0,0,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,22,22,22,22,22,22,0,0,0,0,0,0,0,0,0,0,0,0,23,23,23,23,7,0,0,0,0,0,53,22,23,6,45,45,45,45,77,13,45,45,23,23,23,23,23,23,23,23,23,22,22,22,36,38,0,22,36,22,22,38,0,23,0,37,22,0,22,22,0,22,22,22,23,22,36,23,22,22,22,38,7,22,23,5,7,22,23,5,7,22,23,5,7,22,23,5,7,22,23,5,7,22,23,5,7,22,23,5,7,22,23,5,7,22,23,5,7,22,23,5,7,22,23,5,7,22,23,5,7,22,23,22,23,22,23,22,23,22,23,22,23,6,7,6,7,6,7,6,7,6,7,6,7,6,7,6,7,22,23,22,23,5,7,37,39,37,23,5,7,22,23,6,7,23,23,23,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,23,6,7,22,23,22,23,22,23,7,22,23,22,23,22,23,22,23,5,7,5,7,21,23,22,23,23,23,23,23,7,7,23,23,21,23,6,22,23,6,22,23,5,7,6,6,23,22,22,6,22,22,23,23,23,22,22,6,23,22,22,6,23,22,22,22,23,22,23,22,22,23,23,23,23,45,23,45,23,23,7,45,22,23,23,22,23,6,23,7,7,7,20,22,22,22,20,22,22,6,22,22,6,6,21,59,59,23,23,23,21,22,22,22,22,22,23,22,22,22,22,22,23,22,22,22,21,23,23,22,22,22,23,23,23,22,22,23,22,22,22,22,22,21,23,7,23,7,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,39,7,39,7,7,21,22,22,22,7,23,23,23,23,23,23,23,23,23,23,23,23,23,6,6,6,6,7,5,5,5,6,7,6,6,6,6,7,6,6,7,6,7,6,7,13,13,13,7,13,13,11,13,13,13,11,23,7,6,5,6,5,6,6,6,4,6,4,6,6,6,6,5,5,5,5,6,6,6,6,6,6,5,5,5,6,7,7,7,6,6,6,6,6,7,7,7,7,7,7,7,7,7,15,15,15,15,7,7,7,7,7,7,7,7,7,7,23,23,22,22,22,22,30,30,30,30,22,22,23,23,22,22,23,23,23,23,23,23,7,23,7,7,7,7,23,23,23,23,23,23,31,31,31,31,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,13,13,13,13,13,13,7,15,15,15,15,15,15,7,7,55,55,30,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,31,13,13,13,13,29,13,31,13,31,31,15,15,15,15,14,14,31,14,15,15,31,15,14,15,31,14,31,14,15,15,14,31,14,14,15,31,47,14,15,47,15,15,31,13,31,31,63,47,47,12,31,31,13,47,12,13,13,15,13,13,13,13,0,0,13,14,14,12,31,31,12,15,31,31,63,14,15,15,15,15,15,31,31,15,15,15,15,15,15,15,31,31,31,31,31,31,31,15,13,12,15,15,12,15,12,31,31,47,15,15,15,31,15,15,15,14,15,15,0,0,0,0,0,0,0,0,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,30,44,46,29,62,30,15,30,30,30,15,31,46,15,0,0,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,-51,-84,-83,-51,-51,-35,-115,45,45,-51,0,0,0,0,0,0,23,6,23,6,23,6,7,0,0,0,0,0,0,0,0,0,120,-120,-120,17,2,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,-100,-99,30,30,46,30,15,15,46,30,15,104,104,120,0,104,120,104,-120,89,120,120,121,104,121,104,89,105,104,89,105,121,104,105,0,105,89,105,105,0,0,0,0,37,7,38,53,37,0,37,7,21,7,37,7,21,7,37,7,37,23,7,36,23,22,23,53,23,22,23,5,7,68,39,22,23,5,7,38,39,22,23,5,7,22,23,5,7,22,23,6,7,22,23,5,7,22,23,5,7,22,23,22,23,22,23,22,23,23,23,6,7,23,23,6,7,23,23,6,7,23,23,6,7,22,23,6,7,22,23,6,7,22,23,6,7,22,23,6,7,22,23,5,7,22,23,5,7,22,23,6,7,22,23,5,7,38,39,6,7,22,23,5,7,37,39,6,7,22,23,22,23,22,23,5,7,6,7,22,23,38,23,38,23,0,0,15,0,87,90,44,59,29,30,104,-100,54,59,61,52,46,52,29,60,91,76,60,61,76,59,59,60,59,120,120,44,45,61,75,45,46,60,44,60,60,76,46,45,74,77,61,60,29,29,45,77,46,61,60,44,45,45,30,44,44,60,-116,29,55,89,15,104,76,75,74,75,74,75,75,76,121,73,59,104,61,76,75,75,75,91,91,58,76,59,45,59,76,75,-116,-120,55,45,60,60,19,54,37,20,52,22,22,22,22,22,22,22,22,37,22,37,7,6,22,7,7,22,7,22,7,22,7,23,7,6,22,22,7,7,7,54,7,7,22,7,22,7,22,22,7,7,7,37,7,6,6,7,7,22,22,38,7,39,22,22,23,36,36,15,22,21,23,22,39,39,22,21,37,22,6,6,6,6,7,6,21,22,6,23,21,7,21,21,7,21,22,22,22,6,0,0,0,36,36,36,36,36,21,0,0,36,21,21,22,6,21,0,0,21,21,37,22,37,21,0,0,22,36,68,0,0,0,59,47,45,60,-120,61,46,0,51,22,38,22,38,22,22,0,0,0,0,0,0,0,0,0,0,15,15,15,15,22,15,15],"non-unicode":"ÀÁÂÈÊËÍÓÔÕÚßãõğİıŒœŞşŴŵžȇ\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜø£Ø׃áíóúñѪº¿®¬½¼¡«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀αβΓπΣσμτΦΘΩδ∞∅∈∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■\u0000"}