diff --git a/src/main/java/com/massivecraft/factions/entity/MConf.java b/src/main/java/com/massivecraft/factions/entity/MConf.java index 22afa230..7babfac7 100644 --- a/src/main/java/com/massivecraft/factions/entity/MConf.java +++ b/src/main/java/com/massivecraft/factions/entity/MConf.java @@ -394,6 +394,9 @@ public class MConf extends Entity // INTEGRATION: LWC // -------------------------------------------- // + public boolean lwcMustHaveBuildRightsToCreate = true; + public boolean lwcRemoveIfNoBuildRights = false; + public Map lwcRemoveOnChange = MUtil.map( EventFactionsChunkChangeType.BUY, false, EventFactionsChunkChangeType.SELL, false, diff --git a/src/main/java/com/massivecraft/factions/entity/MPlayer.java b/src/main/java/com/massivecraft/factions/entity/MPlayer.java index c97c1e78..2d7b6dc8 100644 --- a/src/main/java/com/massivecraft/factions/entity/MPlayer.java +++ b/src/main/java/com/massivecraft/factions/entity/MPlayer.java @@ -814,10 +814,10 @@ public class MPlayer extends SenderEntity implements EconomyParticipato } String chunkString = chunk.toString(PSFormatHumanSpace.get()); - String typeString = event.getType().toString().toLowerCase(); + String typeString = event.getType().past; for (MPlayer informee : informees) { - informee.msg("%s did %s %s for %s from %s.", this.describeTo(informee, true), typeString, chunkString, newFaction.describeTo(informee), oldFaction.describeTo(informee)); + informee.msg("%s %s %s | %s --> %s", this.describeTo(informee, true), typeString, chunkString, oldFaction.describeTo(informee, true), newFaction.describeTo(informee, true)); } return true; diff --git a/src/main/java/com/massivecraft/factions/event/EventFactionsAbstractSender.java b/src/main/java/com/massivecraft/factions/event/EventFactionsAbstractSender.java index 1535ff05..532b864c 100644 --- a/src/main/java/com/massivecraft/factions/event/EventFactionsAbstractSender.java +++ b/src/main/java/com/massivecraft/factions/event/EventFactionsAbstractSender.java @@ -13,7 +13,7 @@ public abstract class EventFactionsAbstractSender extends EventMassiveCore private final CommandSender sender; public CommandSender getSender() { return this.sender; } - public MPlayer getUSender() { return this.sender == null ? null : MPlayer.get(this.sender); } + public MPlayer getMSender() { return this.sender == null ? null : MPlayer.get(this.sender); } // -------------------------------------------- // // CONSTRUCT diff --git a/src/main/java/com/massivecraft/factions/event/EventFactionsChunkChange.java b/src/main/java/com/massivecraft/factions/event/EventFactionsChunkChange.java index 52293735..dc5f9397 100644 --- a/src/main/java/com/massivecraft/factions/event/EventFactionsChunkChange.java +++ b/src/main/java/com/massivecraft/factions/event/EventFactionsChunkChange.java @@ -50,7 +50,7 @@ public class EventFactionsChunkChange extends EventFactionsAbstractSender if (currentFaction.isNone()) return EventFactionsChunkChangeType.BUY; if (newFaction.isNormal()) return EventFactionsChunkChangeType.CONQUER; - MPlayer usender = this.getUSender(); + MPlayer usender = this.getMSender(); if (usender != null && usender.getFaction() == currentFaction) return EventFactionsChunkChangeType.SELL; return EventFactionsChunkChangeType.PILLAGE; diff --git a/src/main/java/com/massivecraft/factions/event/EventFactionsChunkChangeType.java b/src/main/java/com/massivecraft/factions/event/EventFactionsChunkChangeType.java index 33cd4140..2666cf5e 100644 --- a/src/main/java/com/massivecraft/factions/event/EventFactionsChunkChangeType.java +++ b/src/main/java/com/massivecraft/factions/event/EventFactionsChunkChangeType.java @@ -2,9 +2,33 @@ package com.massivecraft.factions.event; public enum EventFactionsChunkChangeType { - BUY, - SELL, - CONQUER, - PILLAGE, + // -------------------------------------------- // + // ENUM + // -------------------------------------------- // + + BUY("buy", "bought"), + SELL("sell", "sold"), + CONQUER("conquer", "conquered"), + PILLAGE("pillage", "pillaged"), + + // END OF LIST ; + + // -------------------------------------------- // + // FIELDS + // -------------------------------------------- // + + public final String now; + public final String past; + + // -------------------------------------------- // + // CONSTRUCT + // -------------------------------------------- // + + EventFactionsChunkChangeType(String now, String past) + { + this.now = now; + this.past = past; + } + } diff --git a/src/main/java/com/massivecraft/factions/event/EventFactionsPvpDisallowed.java b/src/main/java/com/massivecraft/factions/event/EventFactionsPvpDisallowed.java index 926f2f25..dd137b9c 100644 --- a/src/main/java/com/massivecraft/factions/event/EventFactionsPvpDisallowed.java +++ b/src/main/java/com/massivecraft/factions/event/EventFactionsPvpDisallowed.java @@ -29,11 +29,11 @@ public class EventFactionsPvpDisallowed extends EventFactionsAbstract private final Player attacker; public Player getAttacker() { return this.attacker; } - public MPlayer getUAttacker() { return this.attacker == null ? null : MPlayer.get(this.attacker); } + public MPlayer getMAttacker() { return this.attacker == null ? null : MPlayer.get(this.attacker); } private final Player defender; public Player getDefender() { return this.defender; } - public MPlayer getUDefender() { return this.defender == null ? null : MPlayer.get(this.defender); } + public MPlayer getMDefender() { return this.defender == null ? null : MPlayer.get(this.defender); } private final EntityDamageByEntityEvent event; public EntityDamageByEntityEvent getEvent() { return this.event; } diff --git a/src/main/java/com/massivecraft/factions/integration/herochat/ChannelFactionsAbstract.java b/src/main/java/com/massivecraft/factions/integration/herochat/ChannelFactionsAbstract.java index c866e835..50f08dd4 100644 --- a/src/main/java/com/massivecraft/factions/integration/herochat/ChannelFactionsAbstract.java +++ b/src/main/java/com/massivecraft/factions/integration/herochat/ChannelFactionsAbstract.java @@ -228,6 +228,8 @@ public abstract class ChannelFactionsAbstract implements Channel { Set ret = new HashSet(); + // TODO: Dormant non-erased universe support? + MPlayer fpsender = MPlayer.get(sender); Faction faction = fpsender.getFaction(); String universe = fpsender.getUniverse(); diff --git a/src/main/java/com/massivecraft/factions/integration/lwc/EngineLwc.java b/src/main/java/com/massivecraft/factions/integration/lwc/EngineLwc.java index 0564648b..b0c96e19 100644 --- a/src/main/java/com/massivecraft/factions/integration/lwc/EngineLwc.java +++ b/src/main/java/com/massivecraft/factions/integration/lwc/EngineLwc.java @@ -1,18 +1,15 @@ package com.massivecraft.factions.integration.lwc; -import java.util.ArrayList; import java.util.List; -import org.bukkit.Chunk; -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.block.BlockState; +import org.bukkit.Bukkit; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.plugin.Plugin; import com.griefcraft.lwc.LWC; import com.griefcraft.model.Protection; +import com.griefcraft.sql.PhysDB; import com.massivecraft.factions.Factions; import com.massivecraft.factions.entity.Faction; import com.massivecraft.factions.entity.MConf; @@ -21,6 +18,7 @@ import com.massivecraft.factions.event.EventFactionsChunkChange; import com.massivecraft.factions.event.EventFactionsChunkChangeType; import com.massivecraft.massivecore.EngineAbstract; import com.massivecraft.massivecore.ps.PS; +import com.massivecraft.massivecore.util.IdUtil; public class EngineLwc extends EngineAbstract @@ -72,54 +70,70 @@ public class EngineLwc extends EngineAbstract if (remove == false) return; // ... then remove for all other factions than the new one. - removeAlienProtections(event.getChunk(), newFaction); + // First we wait one tick to make sure the chunk ownership changes have been applied. + // Then we remove the protections but we do it asynchronously to not lock the main thread. + removeAlienProtectionsAsyncNextTick(event.getChunk(), newFaction); } // -------------------------------------------- // // UTIL // -------------------------------------------- // - public static void removeAlienProtections(PS chunkPs, Faction faction) + // This method causes LWC to run an SQL query which can take a few milliseconds. + // For that reason this method should not be executed in the main server thread. + // After looking through the source code of LWC I am also hopeful this is thread safe. + public static List getProtectionsInChunk(PS chunkPs) + { + final int xmin = chunkPs.getChunkX() * 16; + final int xmax = xmin + 15; + + final int ymin = 0; + final int ymax = 255; + + final int zmin = chunkPs.getChunkZ() * 16; + final int zmax = zmin + 15; + + PhysDB db = LWC.getInstance().getPhysicalDatabase(); + return db.loadProtections(chunkPs.getWorld(), xmin, xmax, ymin, ymax, zmin, zmax); + } + + // As with the method above: Thread safe and slow. Do run asynchronously. + public static void removeAlienProtectionsRaw(PS chunkPs, Faction faction) { List nonAliens = faction.getMPlayers(); for (Protection protection : getProtectionsInChunk(chunkPs)) { - MPlayer owner = MPlayer.get(protection.getOwner()); + // NOTE: The LWC protection owner is still the name and not the UUID. For that reason we must convert it. + String ownerName = protection.getOwner(); + String ownerId = IdUtil.getId(ownerName); + MPlayer owner = MPlayer.get(ownerId); if (nonAliens.contains(owner)) continue; protection.remove(); } } - public static List getProtectionsInChunk(PS chunkPs) + public static void removeAlienProtectionsAsync(final PS chunkPs, final Faction faction) { - List ret = new ArrayList(); - - // Get the chunk - Chunk chunk = null; - try + Bukkit.getScheduler().runTaskAsynchronously(Factions.get(), new Runnable() { - chunk = chunkPs.asBukkitChunk(true); - } - catch (Exception e) - { - return ret; - } - - for (BlockState blockState : chunk.getTileEntities()) - { - // TODO: Can something else be protected by LWC? Or is it really only chests? - // TODO: How about we run through each block in the chunk just to be on the safe side? - if (blockState.getType() != Material.CHEST) continue; - Block block = blockState.getBlock(); - - Protection protection = LWC.getInstance().findProtection(block); - if (protection == null) continue; - - ret.add(protection); - } - - return ret; + @Override + public void run() + { + removeAlienProtectionsRaw(chunkPs, faction); + } + }); } + public static void removeAlienProtectionsAsyncNextTick(final PS chunkPs, final Faction faction) + { + Bukkit.getScheduler().runTaskLater(Factions.get(), new Runnable() + { + @Override + public void run() + { + removeAlienProtectionsAsync(chunkPs, faction); + } + }, 0); + } } diff --git a/src/main/java/com/massivecraft/factions/integration/lwc/FactionsLwcModule.java b/src/main/java/com/massivecraft/factions/integration/lwc/FactionsLwcModule.java index 51f270fb..425ba5fc 100644 --- a/src/main/java/com/massivecraft/factions/integration/lwc/FactionsLwcModule.java +++ b/src/main/java/com/massivecraft/factions/integration/lwc/FactionsLwcModule.java @@ -1,11 +1,24 @@ package com.massivecraft.factions.integration.lwc; +import org.bukkit.Location; +import org.bukkit.Sound; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; + import com.griefcraft.lwc.LWC; +import com.griefcraft.model.Protection; import com.griefcraft.scripting.JavaModule; +import com.griefcraft.scripting.event.LWCProtectionInteractEvent; import com.griefcraft.scripting.event.LWCProtectionRegisterEvent; import com.massivecraft.factions.Factions; +import com.massivecraft.factions.entity.MConf; +import com.massivecraft.factions.entity.MPlayer; import com.massivecraft.factions.listeners.FactionsListenerMain; +import com.massivecraft.massivecore.mixin.Mixin; import com.massivecraft.massivecore.ps.PS; +import com.massivecraft.massivecore.util.IdUtil; +import com.massivecraft.massivecore.util.SmokeUtil; +import com.massivecraft.massivecore.util.Txt; @SuppressWarnings("unused") public class FactionsLwcModule extends JavaModule @@ -31,12 +44,61 @@ public class FactionsLwcModule extends JavaModule // -------------------------------------------- // // OVERRIDE // -------------------------------------------- // + // @Override public void onRegisterProtection(LWCProtectionRegisterEvent event) { - if (FactionsListenerMain.canPlayerBuildAt(event.getPlayer(), PS.valueOf(event.getBlock()), false)) return; + // If this feature is enabled ... + if ( ! MConf.get().lwcMustHaveBuildRightsToCreate) return; + + // ... and the player don't have build rights here ... + // NOTE: We verbosely check the build rights so that a proper info message is sent + if (FactionsListenerMain.canPlayerBuildAt(event.getPlayer(), PS.valueOf(event.getBlock()), true)) return; + + // ... then cancel the event. event.setCancelled(true); } + @Override + public void onProtectionInteract(LWCProtectionInteractEvent event) + { + // If this feature is enabled ... + if ( ! MConf.get().lwcRemoveIfNoBuildRights) return; + + // ... gather data ... + final Protection protection = event.getProtection(); + final Block block = protection.getBlock(); + final PS ps = PS.valueOf(block); + // NOTE: The LWC protection owner is still the name and not the UUID. For that reason we must convert it. + final String ownerName = protection.getOwner(); + final String ownerId = IdUtil.getId(ownerName); + final MPlayer mowner = MPlayer.get(ownerId); + if (mowner == null) return; + + // ... and if the protection owner no longer has build rights for the area ... + // NOTE: We silently check the build rights for the protection owner. + // NOTE: The protection owner may even be offline at the moment. + if (FactionsListenerMain.canPlayerBuildAt(mowner, ps, false)) return; + + // ... remove the protection ... + protection.remove(); + + // ... cancel the event ... + // NOTE: The first time you click nothing but the unlock should happen. + // NOTE: This way it's more obvious the auto unlock system kicked in. + // NOTE: No inventory will get opened. + event.setResult(Result.CANCEL); + + // ... play FX ... + Location location = block.getLocation(); + SmokeUtil.spawnCloudSimple(location); + location.getWorld().playSound(location, Sound.DOOR_OPEN, 1, 1); + + // ... and inform. + Player player = event.getPlayer(); + String message = Txt.parse("Factions removed %s's LWC. They lacked build rights.", mowner.getDisplayName(player)); + player.sendMessage(message); + } + } diff --git a/src/main/java/com/massivecraft/factions/listeners/FactionsListenerEcon.java b/src/main/java/com/massivecraft/factions/listeners/FactionsListenerEcon.java index 40b3a605..a2b3f049 100644 --- a/src/main/java/com/massivecraft/factions/listeners/FactionsListenerEcon.java +++ b/src/main/java/com/massivecraft/factions/listeners/FactionsListenerEcon.java @@ -75,7 +75,7 @@ public class FactionsListenerEcon implements Listener public void takeOnDisband(EventFactionsDisband event) { // If there is a usender ... - MPlayer usender = event.getUSender(); + MPlayer usender = event.getMSender(); if (usender == null) return; // ... and economy is enabled ... @@ -100,7 +100,7 @@ public class FactionsListenerEcon implements Listener public static void payForAction(EventFactionsAbstractSender event, Double cost, String desc) { // If there is a sender ... - MPlayer usender = event.getUSender(); + MPlayer usender = event.getMSender(); if (usender == null) return; // ... and there is a cost ...