diff --git a/dough-protection/pom.xml b/dough-protection/pom.xml
index fa2e72ee..1b41349a 100644
--- a/dough-protection/pom.xml
+++ b/dough-protection/pom.xml
@@ -63,6 +63,10 @@
william278-repo
https://repo.william278.net/snapshots/
+
+ towny-repo
+ https://repo.glaremasters.me/repository/towny/
+
@@ -157,9 +161,9 @@
- com.github.LlmDl
- Towny
- 1b86d017c5
+ com.palmergames.bukkit.towny
+ towny
+ 0.100.3.0
provided
@@ -282,6 +286,14 @@
1.0.580
provided
+
+
+
+ com.github.jwkerr
+ Quarters
+ e9ed8a133a
+ provided
+
diff --git a/dough-protection/src/main/java/io/github/bakedlibs/dough/protection/ProtectionManager.java b/dough-protection/src/main/java/io/github/bakedlibs/dough/protection/ProtectionManager.java
index bebd5b33..49f5f4f5 100644
--- a/dough-protection/src/main/java/io/github/bakedlibs/dough/protection/ProtectionManager.java
+++ b/dough-protection/src/main/java/io/github/bakedlibs/dough/protection/ProtectionManager.java
@@ -32,6 +32,7 @@
import io.github.bakedlibs.dough.protection.modules.LocketteProtectionModule;
import io.github.bakedlibs.dough.protection.modules.PlotSquaredProtectionModule;
import io.github.bakedlibs.dough.protection.modules.PreciousStonesProtectionModule;
+import io.github.bakedlibs.dough.protection.modules.QuartersProtectionModule;
import io.github.bakedlibs.dough.protection.modules.RedProtectProtectionModule;
import io.github.bakedlibs.dough.protection.modules.ShopChestProtectionModule;
import io.github.bakedlibs.dough.protection.modules.TownyProtectionModule;
@@ -80,7 +81,6 @@ private void loadModuleImplementations(Plugin plugin) {
// We sadly cannot use ModuleName::new as this would load the class into memory prematurely
registerModule(pm, "WorldGuard", worldGuard -> new WorldGuardProtectionModule(worldGuard));
- registerModule(pm, "Towny", towny -> new TownyProtectionModule(towny));
registerModule(pm, "GriefPrevention", griefPrevention -> new GriefPreventionProtectionModule(griefPrevention));
registerModule(pm, "LWC", lwc -> new LWCProtectionModule(lwc));
registerModule(pm, "PreciousStones", preciousStones -> new PreciousStonesProtectionModule(preciousStones));
@@ -98,6 +98,13 @@ private void loadModuleImplementations(Plugin plugin) {
registerModule(pm, "HuskClaims", huskClaims -> new HuskClaimsProtectionModule(huskClaims));
registerModule(pm, "Bolt", bolt -> new BoltProtectionModule(bolt));
+ // If Quarters is installed, the QuartersProtectionModule must be enabled instead of the Towny module.
+ if (pm.isPluginEnabled("Quarters")) {
+ registerModule(pm, "Quarters", quarters -> new QuartersProtectionModule(quarters));
+ } else {
+ registerModule(pm, "Towny", towny -> new TownyProtectionModule(towny));
+ }
+
/*
* The following Plugins work by utilising one of the above listed
* Plugins in the background.
diff --git a/dough-protection/src/main/java/io/github/bakedlibs/dough/protection/modules/QuartersProtectionModule.java b/dough-protection/src/main/java/io/github/bakedlibs/dough/protection/modules/QuartersProtectionModule.java
new file mode 100644
index 00000000..733905fe
--- /dev/null
+++ b/dough-protection/src/main/java/io/github/bakedlibs/dough/protection/modules/QuartersProtectionModule.java
@@ -0,0 +1,112 @@
+package io.github.bakedlibs.dough.protection.modules;
+
+import au.lupine.quarters.api.manager.QuarterManager;
+import au.lupine.quarters.object.entity.Quarter;
+import com.palmergames.bukkit.towny.TownyAPI;
+import com.palmergames.bukkit.towny.object.Resident;
+import com.palmergames.bukkit.towny.object.TownBlock;
+import com.palmergames.bukkit.towny.object.TownyPermission.ActionType;
+import com.palmergames.bukkit.towny.utils.PlayerCacheUtil;
+import io.github.bakedlibs.dough.protection.Interaction;
+import io.github.bakedlibs.dough.protection.ProtectionModule;
+import org.bukkit.Location;
+import org.bukkit.OfflinePlayer;
+import org.bukkit.entity.Player;
+import org.bukkit.plugin.Plugin;
+
+import javax.annotation.Nonnull;
+
+
+/**
+ * Protection handling module for Quarters, a Towny add-on.
+ * If Quarters is installed on a server, this module must be registered
+ * instead of the Towny module.
+ *
+ * @author galacticwarrior9
+ */
+public class QuartersProtectionModule implements ProtectionModule {
+
+ private final Plugin plugin;
+
+ public QuartersProtectionModule(@Nonnull Plugin plugin) {
+ this.plugin = plugin;
+ }
+
+ @Override
+ public Plugin getPlugin() {
+ return plugin;
+ }
+
+ @Override
+ public void load() {
+ // We don't need to load any APIs, everything is static
+ }
+
+ @Override
+ public boolean hasPermission(OfflinePlayer p, Location l, Interaction action) {
+ if (!(p instanceof Player)) {
+ return false;
+ }
+ return isInteractionAllowed((Player) p, convert(action), l);
+ }
+
+ private boolean isInteractionAllowed(Player player, ActionType type, Location l) {
+ boolean allowedInUnderlyingPlot = PlayerCacheUtil.getCachePermission(player, l, l.getBlock().getType(), type);
+ if (allowedInUnderlyingPlot) {
+ return true;
+ }
+
+ Quarter quarter = QuarterManager.getInstance().getQuarter(l);
+ if (quarter == null) {
+ return false;
+ }
+
+ Resident resident = TownyAPI.getInstance().getResident(player.getUniqueId());
+ if (resident == null) {
+ return true;
+ }
+
+ return quarter.testPermission(convertToQuartersAction(type), resident);
+ }
+
+ /**
+ * Returns the corresponding Towny {@link ActionType} from the dough {@link Interaction}
+ *
+ * @param action The dough {@link Interaction}
+ * @return The corresponding Towny {@link ActionType}
+ */
+ private ActionType convert(Interaction action) {
+ switch (action) {
+ case INTERACT_BLOCK:
+ return ActionType.SWITCH;
+ case INTERACT_ENTITY:
+ case ATTACK_PLAYER:
+ case ATTACK_ENTITY:
+ return ActionType.ITEM_USE;
+ case BREAK_BLOCK:
+ return ActionType.DESTROY;
+ case PLACE_BLOCK:
+ default:
+ return ActionType.BUILD;
+ }
+ }
+
+ /**
+ * Returns the corresponding Quarters {@link au.lupine.quarters.object.state.ActionType} from the Towny {@link ActionType}
+ *
+ * @param action The Towny {@link ActionType}
+ * @return The corresponding Quarters {@link au.lupine.quarters.object.state.ActionType}
+ */
+ private au.lupine.quarters.object.state.ActionType convertToQuartersAction(ActionType action) {
+ switch (action) {
+ case DESTROY:
+ return au.lupine.quarters.object.state.ActionType.DESTROY;
+ case ITEM_USE:
+ return au.lupine.quarters.object.state.ActionType.ITEM_USE;
+ case SWITCH:
+ return au.lupine.quarters.object.state.ActionType.SWITCH;
+ default:
+ return au.lupine.quarters.object.state.ActionType.BUILD;
+ }
+ }
+}