diff --git a/Citadel.jar b/Citadel.jar deleted file mode 100644 index 27a9d078..00000000 Binary files a/Citadel.jar and /dev/null differ diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 00000000..9da27ba4 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,11 @@ +http://opensource.org/licenses/BSD-3-Clause + +Copyright (c) 2013, Justin Kilpatrick, CivCraft +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +Neither the name of CivCraft nor the names of the contributors may be used to endorse or promote products derived from this software without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/bin/META-INF/MANIFEST.MF b/bin/META-INF/MANIFEST.MF index 58630c02..59499bce 100644 --- a/bin/META-INF/MANIFEST.MF +++ b/bin/META-INF/MANIFEST.MF @@ -1,2 +1,2 @@ -Manifest-Version: 1.0 - +Manifest-Version: 1.0 + diff --git a/config.yml b/config.yml index baf4708a..d158b06b 100644 --- a/config.yml +++ b/config.yml @@ -4,6 +4,7 @@ general: autoModeReset: 30 redstoneDistance: 9 groupsAllowed: 60 + reinforcedCrops: true materials: - name: STONE strength: 25 @@ -23,8 +24,10 @@ additionalSecurable: - BEACON - ANVIL nonReinforceable: +- BEDROCK +- ENDER_PORTAL_FRAME - SAPLING -- GRASS +- LONG_GRASS - DEAD_BUSH - PISTON_EXTENSION - PISTON_MOVING_PIECE @@ -59,3 +62,22 @@ database: caching: max_age: 300000 max_chunks: 10000 + +# Possible verboseMessages: +# - InteractionAttempt +# - ReinDelegation +# - AdminReinBypass +# - ReinBypass +# - RedstoneOpen +# - GolemCreated +# - NullGroup +# - AdminReinLocked +# - ReinLocked +# - CropTrample +# - ReinOvergrowth +# - ReinCreated +# - ReinDmg +# - ReinDestroyed +# - Enabled +# - Disabled +verboseMessages: diff --git a/plugin.yml b/plugin.yml index b28addd4..0ece9acb 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,6 +1,6 @@ name: Citadel main: com.untamedears.citadel.Citadel -version: 2.0.1 +version: 2.5.25 description: Reinforce your structures! author: chrisrico, jonnyd (aka Gu3rr1lla) database: false @@ -29,6 +29,10 @@ commands: aliases: cti description: While in this mode, punching a block gives you information about it's reinforcement and security level. usage: /ctinfo (or /cti) + ctinsecure: + aliases: ctis + description: While in this mode, punching a reinforcement toggles its insecure status. + usage: /ctinsecure (or /ctis) ctoff: aliases: cto description: Turns off reinforcement, fortification, and info mode. @@ -112,21 +116,53 @@ commands: aliases: ctgi description: Displays information about a group. usage: /ctgroupinfo (or /ctgi) - ctcon: - description: Restricted. - usage: /ctcon - permission: citadel.console ctgstats: aliases: ctgst - description: Displayers group statistics + description: Displays group statistics usage: /ctgstats + ctcon: + description: Restricted console commands + usage: /ctcon permission: citadel.console ctpstats: aliases: ctpst description: Displayers player statistics usage: /ctpstats - permission: citadel.console + permission: citadel.admin.ctpstats + ctdiscipline: + aliases: ctadg + description: Disciplines a group + usage: /ctdiscipline [del] + permission: citadel.admin.ctdiscipline + ctreprieve: + aliases: ctarg + description: Reprieves a group + usage: /ctreprieve + permission: citadel.admin.ctreprieve + ctacid: + description: Trigger acid block to eat a reinforcement + usage: /ctacid + permission: citadel.ctacid + ctmature: + description: Instantly mature a reinforcement + usage: /ctmature permissions: citadel.console: description: Console commands for Citadel - default: false \ No newline at end of file + default: false + citadel.admin: + description: Grants permission to Citadels admin commands. + default: op + children: + citadel.admin.ctgstats: true + citadel.admin.ctpstats: true + citadel.admin.ctdiscipline: true + citadel.admin.ctreprieve: true + citadel.admin.ctinfodetails: true + citadel.admin.accesssecurable: true + citadel.admin.cttransfer: true + citadel.admin.reassignreinforcements: true + citadel.admin.bypassmode: true + citadel.ctacid: + description: Allow /ctacid + default: true diff --git a/pom.xml b/pom.xml index 4e0e5573..ffa6c617 100644 --- a/pom.xml +++ b/pom.xml @@ -1,73 +1,56 @@ - 4.0.0 - - com.untamedears.citadel - citadel - 2.0.2 - jar - - Citadel - Bukkit Server Plugin - 2012 - - ${basedir}/src - + 4.0.0 + com.untamedears + Citadel + jar + 2.5.25-SNAPSHOT + Citadel + https://github.com/ttk2/Citadel + + + + - maven-compiler-plugin - 2.3.2 - - 1.6 - 1.6 - + maven-compiler-plugin + 2.3.2 + + 1.6 + 1.6 + - - + + - - CP1252 - - - - - org.bukkit - craftbukkit - 1.3.2-R1.0 - - - - - - - bukkit-repo - http://repo.bukkit.org/content/groups/public/ - - + ${basedir}/src - - - - + + + ${basedir} + + LICENSE.txt + *.yml + + + + + + + + org.bukkit + craftbukkit + 1.7.2-R0.3 + provided + + + + + + bukkit-repo + http://repo.bukkit.org/content/groups/public/ + + + diff --git a/src/com/untamedears/citadel/Citadel.java b/src/com/untamedears/citadel/Citadel.java index 348940c7..c6eabfdd 100644 --- a/src/com/untamedears/citadel/Citadel.java +++ b/src/com/untamedears/citadel/Citadel.java @@ -4,10 +4,13 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; -import java.util.logging.Logger; +import java.util.Map; import java.util.Random; +import java.util.logging.Logger; +import org.bukkit.Server; import org.bukkit.block.Block; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; @@ -18,36 +21,6 @@ import com.untamedears.citadel.access.AccessDelegate; import com.untamedears.citadel.command.CommandHandler; -import com.untamedears.citadel.command.commands.AddModCommand; -import com.untamedears.citadel.command.commands.AllowCommand; -import com.untamedears.citadel.command.commands.BypassCommand; -import com.untamedears.citadel.command.commands.ConsoleCommands; -import com.untamedears.citadel.command.commands.CreateCommand; -import com.untamedears.citadel.command.commands.DeleteCommand; -import com.untamedears.citadel.command.commands.DisallowCommand; -import com.untamedears.citadel.command.commands.FortifyCommand; -import com.untamedears.citadel.command.commands.GroupCommand; -import com.untamedears.citadel.command.commands.GroupInfoCommand; -import com.untamedears.citadel.command.commands.GroupStatsCommand; -import com.untamedears.citadel.command.commands.GroupsCommand; -import com.untamedears.citadel.command.commands.InfoCommand; -import com.untamedears.citadel.command.commands.JoinCommand; -import com.untamedears.citadel.command.commands.LeaveCommand; -import com.untamedears.citadel.command.commands.MaterialsCommand; -import com.untamedears.citadel.command.commands.MembersCommand; -import com.untamedears.citadel.command.commands.ModeratorsCommand; -import com.untamedears.citadel.command.commands.NonReinforceableCommand; -import com.untamedears.citadel.command.commands.OffCommand; -import com.untamedears.citadel.command.commands.PasswordCommand; -import com.untamedears.citadel.command.commands.PlayerStatsCommand; -import com.untamedears.citadel.command.commands.PrivateCommand; -import com.untamedears.citadel.command.commands.PublicCommand; -import com.untamedears.citadel.command.commands.ReinforceCommand; -import com.untamedears.citadel.command.commands.RemoveModCommand; -import com.untamedears.citadel.command.commands.SecurableCommand; -import com.untamedears.citadel.command.commands.StatsCommand; -import com.untamedears.citadel.command.commands.TransferCommand; -import com.untamedears.citadel.command.commands.VersionCommand; import com.untamedears.citadel.dao.CitadelCachingDao; import com.untamedears.citadel.dao.CitadelDao; import com.untamedears.citadel.entity.Faction; @@ -62,7 +35,9 @@ import com.untamedears.citadel.listener.BlockListener; import com.untamedears.citadel.listener.ChunkListener; import com.untamedears.citadel.listener.EntityListener; +import com.untamedears.citadel.listener.InventoryListener; import com.untamedears.citadel.listener.PlayerListener; +import com.untamedears.citadel.listener.WorldListener; /** * User: JonnyD @@ -80,18 +55,63 @@ public class Citadel extends JavaPlugin { private static final Random randomGenerator = new Random(); private static CitadelCachingDao dao; private static Citadel plugin; - + + public enum VerboseMsg { + InteractionAttempt, + ReinDelegation, + AdminReinBypass, + ReinBypass, + RedstoneOpen, + GolemCreated, + NullGroup, + AdminReinLocked, + ReinLocked, + CropTrample, + ReinOvergrowth, + ReinCreated, + ReinDmg, + ReinDestroyed, + Enabled, + Disabled + }; + public static final Map VERBOSE_MESSAGES = new HashMap(); + public static final Map INSENSITIVE_VERBOSE_MESSAGES = new HashMap(); + + static { + VERBOSE_MESSAGES.put(VerboseMsg.InteractionAttempt, "Attempted interaction with block at %s"); + VERBOSE_MESSAGES.put(VerboseMsg.ReinDelegation, "Delegated to block at %s"); + VERBOSE_MESSAGES.put(VerboseMsg.AdminReinBypass, "[Admin] %s bypassed reinforcement at %s"); + VERBOSE_MESSAGES.put(VerboseMsg.ReinBypass, "%s bypassed reinforcement at %s"); + VERBOSE_MESSAGES.put(VerboseMsg.RedstoneOpen, "Prevented redstone from opening reinforcement at %s"); + VERBOSE_MESSAGES.put(VerboseMsg.GolemCreated, "Reinforcement removed due to golem creation at %s"); + VERBOSE_MESSAGES.put(VerboseMsg.NullGroup, "Null group %s(%s)"); + VERBOSE_MESSAGES.put(VerboseMsg.AdminReinLocked, "[Admin] %s accessed locked reinforcement at %s"); + VERBOSE_MESSAGES.put(VerboseMsg.ReinLocked, "%s failed to access locked reinforcement at %s"); + VERBOSE_MESSAGES.put(VerboseMsg.CropTrample, "Prevented reinforced crop trample at %s"); + VERBOSE_MESSAGES.put(VerboseMsg.ReinOvergrowth, "Prevented growth over reinforcement at %s"); + VERBOSE_MESSAGES.put(VerboseMsg.ReinCreated, "PlRein:%s:%d@%s,%d,%d,%d"); + VERBOSE_MESSAGES.put(VerboseMsg.ReinDmg, "Reinforcement damaged at %s"); + VERBOSE_MESSAGES.put(VerboseMsg.ReinDestroyed, "Reinforcement destroyed at %s"); + VERBOSE_MESSAGES.put(VerboseMsg.Enabled, "Citadel is now enabled."); + VERBOSE_MESSAGES.put(VerboseMsg.Disabled, "Citadel is now disabled."); + + for (VerboseMsg msg : VerboseMsg.values()) { + INSENSITIVE_VERBOSE_MESSAGES.put(msg.name().toLowerCase(), msg); + } + } + public boolean onCommand(CommandSender sender, Command command, String label, String[] args){ return commandHandler.dispatch(sender, label, args); } public void onEnable() { plugin = this; + saveDefaultConfig(); configManager.load(); dao = new CitadelCachingDao(this); dao.updateDatabase(); setUpStorage(); - registerCommands(); + commandHandler.registerCommands(); // Events must register after dao is available registerEvents(); for(Player player : getServer().getOnlinePlayers()){ @@ -105,22 +125,23 @@ public void onEnable() { System.out.println("failed"); } ConsoleCommandSender console = getServer().getConsoleSender(); + console.addAttachment(this, "citadel.admin", true); console.addAttachment(this, "citadel.console", true); - log.info("[Citadel] Citadel is now enabled."); + Citadel.verbose(VerboseMsg.Enabled); } public void onDisable() { //There should be some interface that CitadelCachingDao can implement that does this automatically on disable: //I don't want to do this as close() or finalize() because I want to make sure the database connection is still alive. - if( dao instanceof CitadelCachingDao ){ - ((CitadelCachingDao)dao).shutDown(); + if(dao instanceof CitadelCachingDao) { + dao.shutDown(); } - log.info("[Citadel] Citadel is now disabled."); + Citadel.verbose(VerboseMsg.Disabled); } public void setUpStorage(){ GroupStorage groupStorage = new GroupStorage(dao); - groupManager.setStorage(groupStorage); + groupManager.initialize(groupStorage); PersonalGroupStorage personalGroupStorage = new PersonalGroupStorage(dao); personalGroupManager.setStorage(personalGroupStorage); @@ -132,40 +153,6 @@ public void setUpStorage(){ reinforcementManager.setStorage(reinforcementStorage); } - public void registerCommands(){ - commandHandler.addCommand(new AddModCommand()); - commandHandler.addCommand(new AllowCommand()); - commandHandler.addCommand(new BypassCommand()); - commandHandler.addCommand(new ConsoleCommands()); - commandHandler.addCommand(new CreateCommand()); - commandHandler.addCommand(new DeleteCommand()); - commandHandler.addCommand(new DisallowCommand()); - commandHandler.addCommand(new FortifyCommand()); - commandHandler.addCommand(new GroupCommand()); - commandHandler.addCommand(new GroupInfoCommand()); - commandHandler.addCommand(new GroupsCommand()); - //commandHandler.addCommand(new Help()) - commandHandler.addCommand(new InfoCommand()); - commandHandler.addCommand(new JoinCommand()); - commandHandler.addCommand(new LeaveCommand()); - commandHandler.addCommand(new MaterialsCommand()); - commandHandler.addCommand(new MembersCommand()); - commandHandler.addCommand(new ModeratorsCommand()); - commandHandler.addCommand(new NonReinforceableCommand()); - commandHandler.addCommand(new OffCommand()); - commandHandler.addCommand(new PasswordCommand()); - commandHandler.addCommand(new PrivateCommand()); - commandHandler.addCommand(new PublicCommand()); - commandHandler.addCommand(new ReinforceCommand()); - commandHandler.addCommand(new RemoveModCommand()); - commandHandler.addCommand(new SecurableCommand()); - commandHandler.addCommand(new StatsCommand()); - commandHandler.addCommand(new GroupStatsCommand()); - commandHandler.addCommand(new PlayerStatsCommand()); - commandHandler.addCommand(new TransferCommand()); - commandHandler.addCommand(new VersionCommand()); - } - public void registerEvents(){ try { PluginManager pm = getServer().getPluginManager(); @@ -173,6 +160,8 @@ public void registerEvents(){ pm.registerEvents(new ChunkListener(this.dao), this); pm.registerEvents(new PlayerListener(), this); pm.registerEvents(new EntityListener(), this); + pm.registerEvents(new InventoryListener(), this); + pm.registerEvents(new WorldListener(), this); } catch(Exception e) { @@ -192,13 +181,40 @@ public List> getDatabaseClasses() { classes.add(Moderator.class); return classes; } - - public static void info(String message){ - if(configManager.getVerboseLogging()){ - log.info("[Citadel] " + message); + + public static boolean verboseEnabled(VerboseMsg id) { + return configManager.getVerboseLogging() + && configManager.isVerboseSettingEnabled(id); + } + + public static String verboseFmt(VerboseMsg id, Object... args) { + String fmt = VERBOSE_MESSAGES.get(id); + if (fmt == null) { + return "Invalid verbose setting: " + id; } + return String.format(fmt, args); } - + + public static void verbose(VerboseMsg id) { + if (Citadel.verboseEnabled(id)) { + String msg = VERBOSE_MESSAGES.get(id); + if (msg == null) { + Citadel.info("Invalid verbose setting: " + id); + } + Citadel.info(msg); + } + } + + public static void verbose(VerboseMsg id, Object... args) { + if (Citadel.verboseEnabled(id)) { + Citadel.info(Citadel.verboseFmt(id, args)); + } + } + + public static void info(String message){ + log.info("[Citadel] " + message); + } + public static void severe(String message){ log.severe("[Citadel] " + message); } @@ -275,4 +291,8 @@ public boolean playerCanAccessBlock(Block block, String name) { public static CitadelDao getDao() { return (CitadelDao)dao; } + + public static Server getStaticServer() { + return plugin.getServer(); + } } diff --git a/src/com/untamedears/citadel/ConfigManager.java b/src/com/untamedears/citadel/ConfigManager.java index 1578d99a..55042dde 100644 --- a/src/com/untamedears/citadel/ConfigManager.java +++ b/src/com/untamedears/citadel/ConfigManager.java @@ -1,11 +1,14 @@ package com.untamedears.citadel; import java.util.LinkedHashMap; +import java.util.HashMap; +import java.util.Map; import org.bukkit.Material; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.FileConfiguration; +import com.untamedears.citadel.Citadel.VerboseMsg; import com.untamedears.citadel.NaturalReinforcementConfig; import com.untamedears.citadel.entity.NaturalReinforcement; import com.untamedears.citadel.entity.PlayerReinforcement; @@ -25,6 +28,15 @@ public class ConfigManager { private int groupsAllowed; private long cacheMaxAge; private int cacheMaxChunks; + private boolean reinforcedCrops; + private boolean enableMaturation; + private int maturationInterval; + private double maturationIntervalD; + private Integer acidBlockTypeId = null; + private double acidBlockReinforcementTax = 0.00000001D; + private Map verboseMessageSettings = new HashMap(); + private int batchUpdateSize; + private int batchUpdateTimeoutMs; public void load(){ Citadel.getPlugin().reloadConfig(); @@ -35,6 +47,26 @@ public void load(){ verboseLogging = config.getBoolean("general.verboseLogging"); redstoneDistance = config.getDouble("general.redstoneDistance"); groupsAllowed = config.getInt("general.groupsAllowed"); + reinforcedCrops = config.getBoolean("general.reinforcedCrops", true); + batchUpdateSize = config.getInt("general.batchUpdateSize", 500); + batchUpdateTimeoutMs = config.getInt("general.batchUpdateTimeoutMs", 600000); + enableMaturation = config.getBoolean("general.enableMaturation", false); + maturationInterval = config.getInt("general.maturationInterval", 50); + maturationIntervalD = (double)maturationInterval; + acidBlockReinforcementTax = config.getDouble("general.acidBlockTax", 0.00000001D); + if (config.contains("general.acidBlock")) { + String acidBlockMaterial = config.getString("general.acidBlock"); + Material material = Material.matchMaterial(acidBlockMaterial); + if (material != null) { + acidBlockTypeId = material.getId(); + } else { + try { + acidBlockTypeId = Integer.parseInt(acidBlockMaterial); + } catch (NumberFormatException e) { + Citadel.warning("Invalid acidblock material " + acidBlockMaterial); + } + } + } cacheMaxAge = config.getLong("caching.max_age"); cacheMaxChunks = config.getInt("caching.max_chunks"); for (Object obj : config.getList("materials")) { @@ -79,8 +111,38 @@ public void load(){ NaturalReinforcement.CONFIGURATION.put(natReinCfg.getMaterialId(), natReinCfg); } } + ConfigurationSection materialScaling = config.getConfigurationSection("materialScaling"); + if (materialScaling != null) { + for (String materialName : materialScaling.getKeys(false)) { + double scale = materialScaling.getDouble(materialName, 1.0); + Material material = Material.matchMaterial(materialName); + if (material != null) { + PlayerReinforcement.MATERIAL_SCALING.put(material.getId(), scale); + } else { + try { + PlayerReinforcement.MATERIAL_SCALING.put(Integer.parseInt(materialName), scale); + } catch (NumberFormatException e) { + Citadel.warning("Invalid materialScaling material " + materialName); + } + } + } + } + for (String verboseMsg : config.getStringList("verboseMessages")) { + Citadel.info(String.format("verboseMessages %s", verboseMsg)); //XXX + try { + verboseMsg = verboseMsg.toLowerCase(); + VerboseMsg setting = Citadel.INSENSITIVE_VERBOSE_MESSAGES.get(verboseMsg); + if (setting == null) { + continue; + } + verboseMessageSettings.put(setting, true); + Citadel.info(String.format("%s enabled", setting.name())); //XXX + } catch (Exception ex) { + // Skip unknown value + } + } } - + public double getRedstoneDistance(){ return this.redstoneDistance; } @@ -108,7 +170,31 @@ public void setFlashLength(int fl){ public int getGroupsAllowed(){ return this.groupsAllowed; } + + public boolean allowReinforcedCrops() { + return reinforcedCrops; + } + public int getBatchUpdateSize(){ + return this.batchUpdateSize; + } + + public int getBatchUpdateTimeoutMs(){ + return this.batchUpdateTimeoutMs; + } + + public boolean maturationEnabled() { + return enableMaturation; + } + + public int getMaturationInterval() { + return maturationInterval; + } + + public double getMaturationIntervalD() { + return maturationIntervalD; + } + public void setGroupsAllowed(int ga){ this.groupsAllowed = ga; } @@ -145,4 +231,20 @@ public int getMaterialBreakCount(int materialId, int blockY){ } return natReinCfg.generateDurability(blockY); } + + public Integer getAcidBlockType() { + return acidBlockTypeId; + } + + public double getAcidBlockReinforcementTax() { + return acidBlockReinforcementTax; + } + + public boolean isVerboseSettingEnabled(VerboseMsg id) { + Boolean enabled = verboseMessageSettings.get(id); + if (enabled == null) { + return false; + } + return enabled; + } } diff --git a/src/com/untamedears/citadel/GroupManager.java b/src/com/untamedears/citadel/GroupManager.java index e7136282..79736a5d 100644 --- a/src/com/untamedears/citadel/GroupManager.java +++ b/src/com/untamedears/citadel/GroupManager.java @@ -2,9 +2,12 @@ import java.util.Set; +import org.bukkit.entity.Player; + import com.untamedears.citadel.entity.Faction; import com.untamedears.citadel.entity.FactionMember; import com.untamedears.citadel.entity.Moderator; +import com.untamedears.citadel.entity.PersonalGroup; /** * User: JonnyD @@ -16,6 +19,14 @@ public class GroupManager { public GroupManager(){} + public void initialize(GroupStorage storage) { + setStorage(storage); + storage.batchRemoveDeletedGroups(); + + // If the batch update times out, this will load the remaining deleted groups + storage.loadDeletedGroups(); + } + public GroupStorage getStorage() { return this.storage; } @@ -28,44 +39,44 @@ public boolean isGroup(String groupName){ return this.storage.isGroup(groupName); } - public FactionMember getMemberFromGroup(String groupName, String memberName){ - return this.storage.findMemberByGroup(groupName, memberName); - } - public Faction getGroup(String groupName){ return this.storage.findGroupByName(groupName); } - public void addGroup(Faction group){ - this.storage.addGroup(group); + public void addGroup(Faction group, Player initiator){ + this.storage.addGroup(group, initiator); } - - public void removeGroup(Faction group){ - this.storage.removeGroup(group); + + public void removeGroup(Faction group, Player initiator){ + this.storage.removeGroup(group, null, initiator); } + public void removeGroup(Faction group, PersonalGroup redirectToGroup, Player initiator){ + this.storage.removeGroup(group, redirectToGroup, initiator); + } + public Set getMembersOfGroup(String groupName) { - return this.storage.findMembersOfGroup(groupName); + return this.storage.getMembersOfGroup(groupName); } public boolean hasGroupMember(String groupName, String memberName){ return this.storage.hasGroupMember(groupName, memberName); } - public void addMemberToGroup(String groupName, String memberName){ - addMemberToGroup(new FactionMember(memberName, groupName)); + public void addMemberToGroup(String groupName, String memberName, Player initiator){ + addMemberToGroup(new FactionMember(memberName, groupName), initiator); } - public void addMemberToGroup(FactionMember factionMember){ - this.storage.addMemberToGroup(factionMember); + public void addMemberToGroup(FactionMember factionMember, Player initiator){ + this.storage.addMemberToGroup(factionMember, initiator); } - public void removeMemberFromGroup(String groupName, String memberName){ - removeMemberFromGroup(new FactionMember(memberName, groupName)); + public void removeMemberFromGroup(String groupName, String memberName, Player initiator){ + removeMemberFromGroup(new FactionMember(memberName, groupName), initiator); } - public void removeMemberFromGroup(FactionMember factionMember){ - this.storage.removeMemberFromGroup(factionMember); + public void removeMemberFromGroup(FactionMember factionMember, Player initiator){ + this.storage.removeMemberFromGroup(factionMember, initiator); } public void removeAllMembersFromGroup(String groupName){ @@ -73,39 +84,39 @@ public void removeAllMembersFromGroup(String groupName){ } public Set getGroupsByMember(String memberName){ - return this.storage.findGroupsByMember(memberName); + return this.storage.getGroupsByMember(memberName); } public Set getGroupsByFounder(String memberName){ - return this.storage.findGroupsByFounder(memberName); + return this.storage.getGroupsByFounder(memberName); } public boolean hasGroupModerator(String groupName, String memberName){ return this.storage.hasGroupModerator(groupName, memberName); } - public void addModeratorToGroup(String groupName, String memberName){ - addModeratorToGroup(new Moderator(memberName, groupName)); + public void addModeratorToGroup(String groupName, String memberName, Player initiator){ + addModeratorToGroup(new Moderator(memberName, groupName), initiator); } - public void addModeratorToGroup(Moderator moderator){ - this.storage.addModeratorToGroup(moderator); + public void addModeratorToGroup(Moderator moderator, Player initiator){ + this.storage.addModeratorToGroup(moderator, initiator); } - public void removeModeratorFromGroup(String groupName, String memberName){ - removeModeratorFromGroup(new Moderator(memberName, groupName)); + public void removeModeratorFromGroup(String groupName, String memberName, Player initiator){ + removeModeratorFromGroup(new Moderator(memberName, groupName), initiator); } - public void removeModeratorFromGroup(Moderator moderator){ - this.storage.removeModeratorToGroup(moderator); + public void removeModeratorFromGroup(Moderator moderator, Player initiator){ + this.storage.removeModeratorToGroup(moderator, initiator); } public Set getGroupsByModerator(String memberName) { - return this.storage.findGroupsByModerator(memberName); + return this.storage.getGroupsByModerator(memberName); } public Set getModeratorsOfGroup(String groupName) { - return this.storage.findModeratorsOfGroup(groupName); + return this.storage.getModeratorsOfGroup(groupName); } public void removeAllModeratorsFromGroup(String groupName) { @@ -113,10 +124,30 @@ public void removeAllModeratorsFromGroup(String groupName) { } public int getGroupsAmount() { - return this.storage.findGroupsAmount(); + return this.storage.getGroupsAmount(); } public int getPlayerGroupsAmount(String playerName){ - return this.storage.findPlayerGroupsAmount(playerName); + return this.storage.getPlayerGroupsAmount(playerName); } + + public boolean isDeleted(String groupName) { + return this.storage.isDeleted(groupName); + } + + public String mapDeletedGroup(String groupName) { + return this.storage.mapDeletedGroup(groupName); + } + + public String getDelegatedGroupName(String groupName) { + final String delegatedName = mapDeletedGroup(groupName); + if (delegatedName != null) { + return delegatedName; + } + return groupName; + } + + public Faction getDelegatedGroup(String groupName) { + return getGroup(getDelegatedGroupName(groupName)); + } } diff --git a/src/com/untamedears/citadel/GroupStorage.java b/src/com/untamedears/citadel/GroupStorage.java index fcbed67e..a39193b5 100644 --- a/src/com/untamedears/citadel/GroupStorage.java +++ b/src/com/untamedears/citadel/GroupStorage.java @@ -1,12 +1,21 @@ package com.untamedears.citadel; import java.util.HashSet; +import java.util.Map; import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; + +import org.bukkit.entity.Player; import com.untamedears.citadel.dao.CitadelDao; import com.untamedears.citadel.entity.Faction; +import com.untamedears.citadel.entity.FactionDelete; import com.untamedears.citadel.entity.FactionMember; import com.untamedears.citadel.entity.Moderator; +import com.untamedears.citadel.entity.PersonalGroup; +import com.untamedears.citadel.events.GroupChangeEvent; +import com.untamedears.citadel.events.GroupChangeType; /** * User: JonnyD @@ -14,107 +23,348 @@ * Time: 11:57 PM */ public class GroupStorage { - - private CitadelDao dao; - - public GroupStorage(CitadelDao dao){ - this.dao = dao; - } - - public CitadelDao getStorage() { - return this.dao; - } - - public void addGroup(Faction group){ - this.dao.save(group); - } - - public void removeGroup(Faction group){ - this.dao.delete(group); - } - - public Faction findGroupByName(String groupName){ - return this.dao.findGroupByName(groupName); - } - - public FactionMember findMemberByGroup(String groupName, String memberName){ - return this.dao.findGroupMember(groupName, memberName); - } - - public void addMemberToGroup(FactionMember factionMember){ - this.dao.save(factionMember); - } - - public void removeMemberFromGroup(FactionMember factionMember){ - this.dao.delete(factionMember); - } - - public void removeAllMembersFromGroup(String groupName){ - this.dao.removeAllMembersFromGroup(groupName); - } - - public Set findGroupsByFounder(String founderName){ - return this.dao.findGroupsByFounder(founderName); - } - - public Set findGroupsByMember(String memberName){ - Set groups = new HashSet(); - for(FactionMember factionMember : this.dao.findGroupsByMember(memberName)){ - Faction group = new Faction(factionMember.getFactionName(), factionMember.getMemberName()); - groups.add(group); - } - return groups; - } - - public Set findMembersOfGroup(String groupName){ - return this.dao.findMembersOfGroup(groupName); - } - - public boolean hasGroupMember(String groupName, String memberName){ - return this.dao.hasGroupMember(groupName, memberName); - } - - public boolean isGroup(String groupName) { - if(this.dao.findGroup(groupName) != null){ - return true; - } - return false; - } - - public boolean hasGroupModerator(String groupName, String memberName){ - return this.dao.hasGroupModerator(groupName, memberName); - } - - public void addModeratorToGroup(Moderator moderator){ - this.dao.save(moderator); - } - - public void removeModeratorToGroup(Moderator moderator){ - this.dao.delete(moderator); - } - - public Set findGroupsByModerator(String memberName) { - Set groups = new HashSet(); - for(Moderator mod : this.dao.findGroupsByModerator(memberName)){ - Faction group = new Faction(mod.getFactionName(), mod.getMemberName()); - groups.add(group); - } - return groups; - } - - public Set findModeratorsOfGroup(String groupName) { - return this.dao.findModeratorsOfGroup(groupName); - } - - public void removeAllModeratorsFromGroup(String groupName) { - this.dao.removeAllModeratorsFromGroup(groupName); - } - - public int findGroupsAmount() { - return this.dao.countGroups(); - } - - public int findPlayerGroupsAmount(String playerName) { - return this.dao.countPlayerGroups(playerName); - } + + private CitadelDao dao; + private Map groupStorage = new TreeMap(); + private Map> memberStorage = new TreeMap>(); + private Map> moderatorStorage = new TreeMap>(); + private Map deletedGroups = new TreeMap(); + + public GroupStorage(CitadelDao dao){ + this.dao = dao; + } + + public CitadelDao getStorage() { + return this.dao; + } + + public static String normalizeName(String name) { + return name.toLowerCase(); + } + + public boolean isGroup(String groupName) { + return findGroupByName(groupName) != null; + } + + public Faction addGroup(Faction group, Player initiator){ + Faction existingGroup = findGroupByName(group.getName()); + if (existingGroup != null) { + // This is also used to save DB changes to the Faction + existingGroup.Copy(group); + this.dao.save(group); + return existingGroup; + } + GroupChangeEvent event = new GroupChangeEvent( + GroupChangeType.CREATE, initiator, group.getName(), null); + Citadel.getStaticServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return null; + } + String normalizedName = group.getNormalizedName(); + this.groupStorage.put(normalizedName, group); + this.memberStorage.remove(normalizedName); + this.moderatorStorage.remove(normalizedName); + this.dao.save(group); + return group; + } + + public void removeGroup(Faction group, PersonalGroup redirectToGroup, Player initiator){ + final String groupName = group.getName(); + if (!isGroup(groupName) || isDeleted(groupName)) { + return; + } + GroupChangeEvent event = new GroupChangeEvent( + GroupChangeType.DELETE, initiator, group.getName(), null); + Citadel.getStaticServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return; + } + final String normalizedName = group.getNormalizedName(); + removeAllModeratorsFromGroup(normalizedName); + removeAllMembersFromGroup(normalizedName); + if (redirectToGroup != null) { + FactionDelete facDel = new FactionDelete(); + facDel.setDeletedFaction(group.getName()); + facDel.setPersonalGroup(redirectToGroup.getGroupName()); + this.dao.save(facDel); + + deletedGroups.put(normalizedName, normalizeName(redirectToGroup.getGroupName())); + group.setDeleted(true); + this.dao.save(group); + } else { + this.groupStorage.remove(normalizedName); + this.dao.delete(group); + } + } + + public Faction findGroupByName(String groupName){ + Faction group = this.groupStorage.get(normalizeName(groupName)); + if (group == null) { + group = this.dao.findGroupByName(groupName); + if (group == null) { + return null; + } + this.groupStorage.put(group.getNormalizedName(), group); + this.memberStorage.remove(groupName); + this.moderatorStorage.remove(groupName); + } + return group; + } + + private Set loadMembers(String groupName) { + if (!isGroup(groupName)) { + return null; + } + String normalizedGroupName = normalizeName(groupName); + Set members = this.memberStorage.get(normalizedGroupName); + if (members == null) { + Set dbMembers = this.dao.findMembersOfGroup(groupName); + if (dbMembers != null) { + members = new TreeSet(); + for (FactionMember fm : dbMembers) { + members.add(normalizeName(fm.getMemberName())); + } + this.memberStorage.put(normalizedGroupName, members); + } + } + return members; + } + + public boolean isMember(String groupName, String playerName){ + Set members = loadMembers(groupName); + return members != null && members.contains(normalizeName(playerName)); + } + + public boolean addMemberToGroup(FactionMember factionMember, Player initiator){ + String groupName = factionMember.getFactionName(); + String playerName = factionMember.getMemberName(); + if (isMember(groupName, playerName)) { + return false; + } + GroupChangeEvent event = new GroupChangeEvent( + GroupChangeType.ADD_MEMBER, initiator, groupName, playerName); + Citadel.getStaticServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return false; + } + Set members = this.memberStorage.get(normalizeName(groupName)); + if (members == null) { + members = new HashSet(); + } + members.add(normalizeName(playerName)); + this.dao.save(factionMember); + return true; + } + + public boolean removeMemberFromGroup(FactionMember factionMember, Player initiator){ + String groupName = factionMember.getFactionName(); + String playerName = factionMember.getMemberName(); + if (!isMember(groupName, playerName)) { + return false; + } + GroupChangeEvent event = new GroupChangeEvent( + GroupChangeType.RM_MEMBER, initiator, groupName, playerName); + Citadel.getStaticServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return false; + } + Set members = this.memberStorage.get(normalizeName(groupName)); + members.remove(normalizeName(playerName)); + this.dao.delete(factionMember); + return true; + } + + public Set getMembersOfGroup(String groupName) { + Set members = loadMembers(groupName); + if (members == null) { + return null; + } + Set results = new TreeSet(); + for (String mem : members) { + results.add(new FactionMember(mem, groupName)); + } + return results; + } + + public boolean removeAllMembersFromGroup(String groupName){ + if (!isGroup(groupName)) { + return false; + } + this.memberStorage.remove(normalizeName(groupName)); + this.dao.removeAllMembersFromGroup(groupName); + return true; + } + + public boolean hasGroupMember(String groupName, String memberName){ + return isMember(groupName, memberName); + } + + private Set loadModerators(String groupName) { + if (!isGroup(groupName)) { + return null; + } + String normalizedGroupName = normalizeName(groupName); + Set moderators = this.moderatorStorage.get(normalizedGroupName); + if (moderators == null) { + Set dbModerators = this.dao.findModeratorsOfGroup(groupName); + if (dbModerators != null) { + moderators = new TreeSet(); + for (Moderator mod : dbModerators) { + moderators.add(normalizeName(mod.getMemberName())); + } + this.moderatorStorage.put(normalizedGroupName, moderators); + } + } + return moderators; + } + + public boolean isModerator(String groupName, String playerName){ + Set moderators = loadModerators(groupName); + return moderators != null && moderators.contains(normalizeName(playerName)); + } + + public boolean hasGroupModerator(String groupName, String memberName) { + return isModerator(groupName, memberName); + } + + public boolean addModeratorToGroup(Moderator moderator, Player initiator){ + String groupName = moderator.getFactionName(); + String playerName = moderator.getMemberName(); + if (isModerator(groupName, playerName)) { + return false; + } + GroupChangeEvent event = new GroupChangeEvent( + GroupChangeType.ADD_MODERATOR, initiator, groupName, playerName); + Citadel.getStaticServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return false; + } + Set moderators = this.moderatorStorage.get(normalizeName(groupName)); + if (moderators == null) { + moderators = new HashSet(); + } + moderators.add(normalizeName(playerName)); + this.dao.save(moderator); + return true; + } + + public boolean removeModeratorToGroup(Moderator moderator, Player initiator){ + String groupName = moderator.getFactionName(); + String playerName = moderator.getMemberName(); + if (!isModerator(groupName, playerName)) { + return false; + } + GroupChangeEvent event = new GroupChangeEvent( + GroupChangeType.RM_MODERATOR, initiator, groupName, playerName); + Citadel.getStaticServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return false; + } + Set moderators = this.moderatorStorage.get(normalizeName(groupName)); + moderators.remove(normalizeName(playerName)); + this.dao.delete(moderator); + return true; + } + + public Set getModeratorsOfGroup(String groupName) { + Set moderators = loadModerators(groupName); + if (moderators == null) { + return null; + } + Set results = new TreeSet(); + for (String mod : moderators) { + results.add(new Moderator(mod, groupName)); + } + return results; + } + + public boolean removeAllModeratorsFromGroup(String groupName) { + if (!isGroup(groupName)) { + return false; + } + this.moderatorStorage.remove(normalizeName(groupName)); + this.dao.removeAllModeratorsFromGroup(groupName); + return true; + } + + public Set getGroupsByMember(String playerName) { + Set groups = this.dao.findGroupsByMember(playerName); + if (groups == null) { + return null; + } + Set results = new TreeSet(); + for (FactionMember member : groups) { + Faction faction = findGroupByName(member.getFactionName()); + if (faction == null) { + continue; + } + results.add(faction); + } + return results; + } + + public Set getGroupsByModerator(String playerName) { + Set groups = this.dao.findGroupsByModerator(playerName); + if (groups == null) { + return null; + } + Set results = new TreeSet(); + for (Moderator moderator : groups) { + Faction faction = findGroupByName(moderator.getFactionName()); + if (faction == null) { + continue; + } + results.add(faction); + } + return results; + } + + public Set getGroupsByFounder(String playerName) { + Set groups = this.dao.findGroupsByFounder(playerName); + if (groups == null) { + return null; + } + Set results = new TreeSet(); + for (Faction founder : groups) { + Faction faction = findGroupByName(founder.getName()); + if (faction == null) { + continue; + } + results.add(faction); + } + return results; + } + + public int getGroupsAmount() { + return this.dao.countGroups(); + } + + public int getPlayerGroupsAmount(String playerName) { + return this.dao.countPlayerGroups(playerName); + } + + public boolean isDeleted(String groupName) { + final String normalizedName = normalizeName(groupName); + return deletedGroups.containsKey(normalizedName); + } + + public String mapDeletedGroup(String groupName) { + final String normalizedName = normalizeName(groupName); + if (!deletedGroups.containsKey(normalizedName)) { + return normalizedName; + } + return deletedGroups.get(normalizedName); + } + + public void loadDeletedGroups() { + for (FactionDelete facDel : this.dao.loadFactionDeletions()) { + deletedGroups.put( + normalizeName(facDel.getDeletedFaction()), + normalizeName(facDel.getPersonalGroup())); + } + } + + public void batchRemoveDeletedGroups() { + this.dao.batchRemoveDeletedGroups(); + } } diff --git a/src/com/untamedears/citadel/MemberManager.java b/src/com/untamedears/citadel/MemberManager.java index f3ee3584..ef66d1b5 100644 --- a/src/com/untamedears/citadel/MemberManager.java +++ b/src/com/untamedears/citadel/MemberManager.java @@ -74,7 +74,7 @@ public void removeMember(Member member){ } public boolean hasMember(Player player){ - if(this.storage.getMember(player.getDisplayName()) != null){ + if(this.storage.getMember(player.getName()) != null){ return true; } return false; diff --git a/src/com/untamedears/citadel/PlacementMode.java b/src/com/untamedears/citadel/PlacementMode.java index deb51985..e74d3ead 100644 --- a/src/com/untamedears/citadel/PlacementMode.java +++ b/src/com/untamedears/citadel/PlacementMode.java @@ -18,5 +18,7 @@ public enum PlacementMode { // optionally sets a security level FORTIFICATION, // punching blocks gives information about their reinforcement and access level - INFO -} \ No newline at end of file + INFO, + // punching reinforcements toggles their insecure mode + INSECURE +} diff --git a/src/com/untamedears/citadel/Utility.java b/src/com/untamedears/citadel/Utility.java index c1f4de38..551fd50d 100644 --- a/src/com/untamedears/citadel/Utility.java +++ b/src/com/untamedears/citadel/Utility.java @@ -1,30 +1,41 @@ package com.untamedears.citadel; +import java.util.ArrayList; import java.util.Arrays; +import java.util.ConcurrentModificationException; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Random; +import java.util.Set; import org.bukkit.ChatColor; import org.bukkit.DyeColor; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.Server; +import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; import org.bukkit.material.MaterialData; import org.bukkit.material.Wool; +import com.untamedears.citadel.Citadel; +import com.untamedears.citadel.Citadel.VerboseMsg; import com.untamedears.citadel.access.AccessDelegate; import com.untamedears.citadel.entity.Faction; import com.untamedears.citadel.entity.PlayerState; import com.untamedears.citadel.entity.IReinforcement; import com.untamedears.citadel.entity.NaturalReinforcement; import com.untamedears.citadel.entity.PlayerReinforcement; +import com.untamedears.citadel.entity.ReinforcementKey; import com.untamedears.citadel.entity.ReinforcementMaterial; +import com.untamedears.citadel.events.CreateReinforcementEvent; /** * Created by IntelliJ IDEA. @@ -45,17 +56,19 @@ public class Utility { } public static Block getAttachedChest(Block block) { - if (block.getType() == Material.CHEST) + Material mat = block.getType(); + if (mat == Material.CHEST || mat == Material.TRAPPED_CHEST) { for (BlockFace face : new BlockFace[]{BlockFace.NORTH, BlockFace.EAST, BlockFace.SOUTH, BlockFace.WEST}) { Block b = block.getRelative(face); - if (b.getType() == Material.CHEST) { + if (b.getType() == mat) { return b; } } + } return null; } - public static IReinforcement createNaturalReinforcement(Block block) { + public static IReinforcement createNaturalReinforcement(Block block, Player player) { Material material = block.getType(); int breakCount = Citadel .getConfigManager() @@ -64,15 +77,36 @@ public static IReinforcement createNaturalReinforcement(Block block) { return null; } NaturalReinforcement nr = new NaturalReinforcement(block, breakCount); + CreateReinforcementEvent event = new CreateReinforcementEvent(nr, block, player); + Citadel.getStaticServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return null; + } Citadel.getReinforcementManager().addReinforcement(nr); return nr; } + @SuppressWarnings("deprecation") public static IReinforcement createPlayerReinforcement(Player player, Block block) { int blockTypeId = block.getTypeId(); if (PlayerReinforcement.NON_REINFORCEABLE.contains(blockTypeId)) return null; PlayerState state = PlayerState.get(player); + Faction group = state.getFaction(); + if(group == null) { + try { + group = Citadel.getMemberManager().getMember(player.getName()).getPersonalGroup(); + } catch (NullPointerException e){ + sendMessage(player, ChatColor.RED, "You don't seem to have a personal group. Try logging out and back in first"); + } + } + if(group == null) { + return null; + } + if (group.isDisciplined()) { + sendMessage(player, ChatColor.RED, Faction.kDisciplineMsg); + return null; + } ReinforcementMaterial material; switch (state.getMode()) { case REINFORCEMENT: @@ -92,47 +126,78 @@ public static IReinforcement createPlayerReinforcement(Player player, Block bloc return null; } - if (player.getInventory().contains(material.getMaterial(), material.getRequirements())) { - Faction group = state.getFaction(); - if(group == null){ - try { - group = Citadel.getMemberManager().getMember(player.getDisplayName()).getPersonalGroup(); - } catch (NullPointerException e){ - sendMessage(player, ChatColor.RED, "You don't seem to have a personal group. Try logging out and back in first"); - } - } - - // workaround fix for 1.4.6, it doesnt remove the placed item if its already removed for some reason? - if ((state.getMode() == PlacementMode.FORTIFICATION) && (blockTypeId == material.getMaterialId())) { - ItemStack stack = player.getItemInHand(); - if (stack.getAmount() < material.getRequirements() + 1) { - sendMessage(player, ChatColor.RED, "Not enough material in hand to place and fortify this block"); - return null; - } - stack.setAmount(stack.getAmount() - (material.getRequirements() + 1)); - player.setItemInHand(stack); + // Find necessary itemstacks + final PlayerInventory inv = player.getInventory(); + final int invSize = inv.getSize(); + final Material materialType = material.getMaterial(); + List slots = new ArrayList(material.getRequirements()); + int requirements = material.getRequirements(); + if (requirements <= 0) { + Citadel.severe("Reinforcement requirements too low for " + materialType); + return null; + } + try { + for (int slot = 0; slot < invSize && requirements > 0; ++slot) { + final ItemStack slotItem = inv.getItem(slot); + if (slotItem == null) { + continue; + } + if (!slotItem.getType().equals(materialType)) { + continue; + } + requirements -= slotItem.getAmount(); + slots.add(slot); } - else { - player.getInventory().removeItem(material.getRequiredMaterials()); + } catch (Exception ex) { + // Eat any inventory size mis-match exceptions, like with the Anvil + } + if (requirements > 0) { + // Not enough reinforcement material + return null; + } + // Fire the creation event + PlayerReinforcement reinforcement = new PlayerReinforcement(block, material, group, state.getSecurityLevel()); + CreateReinforcementEvent event = new CreateReinforcementEvent(reinforcement, block, player); + Citadel.getStaticServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return null; + } + // Now eat the materials + requirements = material.getRequirements(); + for (final int slot : slots) { + if (requirements <= 0) { + break; } - //TODO: there will eventually be a better way to flush inventory changes to the client - player.updateInventory(); - PlayerReinforcement reinforcement = new PlayerReinforcement(block, material, group, state.getSecurityLevel()); - reinforcement = (PlayerReinforcement)Citadel.getReinforcementManager().addReinforcement(reinforcement); - String securityLevelText = state.getSecurityLevel().name(); - if(securityLevelText.equalsIgnoreCase("group")){ - securityLevelText = securityLevelText + "-" + state.getFaction().getName(); + final ItemStack slotItem = inv.getItem(slot); + final int stackSize = slotItem.getAmount(); + final int deduction = Math.min(stackSize, requirements); + if (deduction < stackSize) { + slotItem.setAmount(stackSize - deduction); + } else { + inv.clear(slot); } - sendThrottledMessage(player, ChatColor.GREEN, "Reinforced with %s at security level %s", material.getMaterial().name(), securityLevelText); - Citadel.warning(String.format("PlRein:%s:%d@%s,%d,%d,%d", - player.getName(), material.getMaterialId(), block.getWorld().getName(), block.getX(), block.getY(), block.getZ())); - // TODO: enable chained flashers, they're pretty cool - //new BlockFlasher(block, material.getFlasher()).start(getPlugin()); - //new BlockFlasher(block, material.getFlasher()).chain(securityMaterial.get(state.getSecurityLevel())).start(); - return reinforcement; - } else { - return null; + requirements -= deduction; + } + if (requirements != 0) { + Citadel.warning(String.format( + "Reinforcement material out of sync %d vs %d", requirements, material.getRequirements())); } + //TODO: there will eventually be a better way to flush inventory changes to the client + player.updateInventory(); + reinforcement = (PlayerReinforcement)Citadel.getReinforcementManager().addReinforcement(reinforcement); + String securityLevelText = state.getSecurityLevel().name(); + if(securityLevelText.equalsIgnoreCase("group")){ + securityLevelText = securityLevelText + "-" + group.getName(); + } + sendThrottledMessage(player, ChatColor.GREEN, "Reinforced with %s at security level %s", material.getMaterial().name(), securityLevelText); + Citadel.verbose( + VerboseMsg.ReinCreated, + player.getName(), material.getMaterialId(), block.getWorld().getName(), + block.getX(), block.getY(), block.getZ()); + // TODO: enable chained flashers, they're pretty cool + //new BlockFlasher(block, material.getFlasher()).start(getPlugin()); + //new BlockFlasher(block, material.getFlasher()).chain(securityMaterial.get(state.getSecurityLevel())).start(); + return reinforcement; } public static void sendMessage(CommandSender sender, ChatColor color, String messageFormat, Object... params) { @@ -154,8 +219,7 @@ public static boolean explodeReinforcement(Block block) { AccessDelegate delegate = AccessDelegate.getDelegate(block); IReinforcement reinforcement = delegate.getReinforcement(); if (reinforcement == null) { - reinforcement = (IReinforcement)createNaturalReinforcement( - block); + reinforcement = createNaturalReinforcement(block, null); } if (reinforcement == null) { return false; @@ -189,20 +253,102 @@ public static void removeReinforcement(IReinforcement reinforcement) { Citadel.getReinforcementManager().removeReinforcement(reinforcement); } + public static boolean isAuthorizedPlayerNear(PlayerReinforcement reinforcement, double distance) { + ReinforcementKey key = reinforcement.getId(); + World reinWorld = Citadel.getPlugin().getServer().getWorld(key.getWorld()); + Location reinLocation = new Location( + reinWorld, (double)key.getX(), (double)key.getY(), (double)key.getZ()); + double min_x = reinLocation.getX() - distance; + double min_z = reinLocation.getZ() - distance; + double max_x = reinLocation.getX() + distance; + double max_z = reinLocation.getZ() + distance; + List onlinePlayers = reinWorld.getPlayers(); + boolean result = false; + try { + for (Player player : onlinePlayers) { + if (player.isDead()) { + continue; + } + Location playerLocation = player.getLocation(); + double player_x = playerLocation.getX(); + double player_z = playerLocation.getZ(); + // Simple bounding box check to quickly rule out Players + // before doing the more expensive playerLocation.distance + if (player_x < min_x || player_x > max_x || + player_z < min_z || player_z > max_z) { + continue; + } + if (!reinforcement.isAccessible(player) && + !player.hasPermission("citadel.admin.accesssecurable")) { + continue; + } + double distanceSquared = playerLocation.distance(reinLocation); + if (distanceSquared <= distance) { + result = true; + break; + } + } + } catch (ConcurrentModificationException e) { + Citadel.warning("ConcurrentModificationException at redstonePower() in BlockListener"); + } + return result; + } + public static boolean maybeReinforcementDamaged(Block block) { AccessDelegate delegate = AccessDelegate.getDelegate(block); IReinforcement reinforcement = delegate.getReinforcement(); return reinforcement != null && reinforcementDamaged(reinforcement); } + public static int timeUntilMature(IReinforcement reinforcement) { + // Doesn't explicitly save the updated Maturation time into the cache. + // That's the responsibility of the caller. + if (reinforcement instanceof PlayerReinforcement) { + int maturationTime = reinforcement.getMaturationTime(); + if (maturationTime > 0) { + final int curMinute = (int)(System.currentTimeMillis() / 60000L); + if (curMinute >= maturationTime) { + maturationTime = 0; + reinforcement.setMaturationTime(0); + } else { + maturationTime = maturationTime - curMinute; + } + } + return maturationTime; + } + return 0; + } + public static boolean reinforcementDamaged(IReinforcement reinforcement) { - reinforcement.setDurability(reinforcement.getDurability() - 1); - boolean cancelled = reinforcement.getDurability() > 0; - if (reinforcement.getDurability() <= 0) { + int durability = reinforcement.getDurability(); + int durabilityLoss = 1; + if (reinforcement instanceof PlayerReinforcement && Citadel.getConfigManager().maturationEnabled()) { + final int maturationTime = timeUntilMature(reinforcement); + if (maturationTime > 0) { + durabilityLoss = maturationTime / 60 + 1; + int blockType = reinforcement.getBlock().getTypeId(); + if (PlayerReinforcement.MATERIAL_SCALING.containsKey(blockType)) { + final double scale = PlayerReinforcement.MATERIAL_SCALING.get(blockType); + durabilityLoss = (int)((double)durabilityLoss * scale); + if (durabilityLoss < 0) { + durabilityLoss = 1; + } + } + } + if (durability < durabilityLoss) { + durabilityLoss = durability; + } + } + durability -= durabilityLoss; + reinforcement.setDurability(durability); + boolean cancelled = durability > 0; + if (durability <= 0) { cancelled = reinforcementBroken(reinforcement); } else { if (reinforcement instanceof PlayerReinforcement) { - Citadel.info("Reinforcement damaged at " + reinforcement.getBlock().getLocation().toString()); + Citadel.verbose( + VerboseMsg.ReinDmg, + reinforcement.getBlock().getLocation().toString()); } Citadel.getReinforcementManager().addReinforcement(reinforcement); } @@ -213,8 +359,7 @@ public static boolean reinforcementBroken(IReinforcement reinforcement) { Citadel.getReinforcementManager().removeReinforcement(reinforcement); if (reinforcement instanceof PlayerReinforcement) { PlayerReinforcement pr = (PlayerReinforcement)reinforcement; - Citadel.info("Reinforcement destroyed at " + pr.getBlock().getLocation().toString()); - + Citadel.verbose(VerboseMsg.ReinDestroyed, pr.getBlock().getLocation().toString()); if (rng.nextDouble() <= pr.getHealth()) { Location location = pr.getBlock().getLocation(); ReinforcementMaterial material = pr.getMaterial(); @@ -237,11 +382,15 @@ public static SecurityLevel getSecurityLevel(String[] args, Player player) { return SecurityLevel.PRIVATE; } - private static List MULTI_MODE = Arrays.asList(PlacementMode.FORTIFICATION, PlacementMode.INFO, PlacementMode.REINFORCEMENT); + private static List MULTI_MODE = Arrays.asList(PlacementMode.FORTIFICATION, PlacementMode.INFO, PlacementMode.REINFORCEMENT, PlacementMode.INSECURE); public static void setMultiMode(PlacementMode mode, SecurityLevel securityLevel, String[] args, Player player, PlayerState state) { if (!MULTI_MODE.contains(mode)) return; - + Faction group = state.getFaction(); + if (group != null && group.isDisciplined()) { + sendMessage(player, ChatColor.RED, Faction.kDisciplineMsg); + return; + } if (state.getMode() == mode && state.getSecurityLevel() == securityLevel) { state.reset(); sendMessage(player, ChatColor.GREEN, "%s mode off", mode.name()); @@ -256,6 +405,7 @@ public static void setMultiMode(PlacementMode mode, SecurityLevel securityLevel, sendMessage(player, ChatColor.GREEN, "%s mode %s, %s", mode.name(), state.getReinforcementMaterial().getMaterial().name(), securityLevel.name()); break; case INFO: + case INSECURE: sendMessage(player, ChatColor.GREEN, "%s mode on", mode.name()); break; } @@ -264,6 +414,11 @@ public static void setMultiMode(PlacementMode mode, SecurityLevel securityLevel, } public static void setSingleMode(SecurityLevel securityLevel, PlayerState state, Player player) { + Faction group = state.getFaction(); + if (group != null && group.isDisciplined()) { + sendMessage(player, ChatColor.RED, Faction.kDisciplineMsg); + return; + } if (state.getMode() != PlacementMode.REINFORCEMENT_SINGLE_BLOCK) { state.setSecurityLevel(securityLevel); state.setMode(PlacementMode.REINFORCEMENT_SINGLE_BLOCK); @@ -280,4 +435,134 @@ public static String getTruncatedMaterialMessage(String prefix, List ma builder.insert(0, prefix); return builder.toString(); } + + public static Set getPlantSoilTypes(Material mat) { + Set soilTypes = new HashSet(); + if (isSoilPlant(mat)) { + soilTypes.add(Material.SOIL); + } + if (isDirtPlant(mat)) { + soilTypes.add(Material.DIRT); + } + if (isGrassPlant(mat)) { + soilTypes.add(Material.GRASS); + } + if (isSandPlant(mat)) { + soilTypes.add(Material.SAND); + } + if (isSoulSandPlant(mat)) { + soilTypes.add(Material.SOUL_SAND); + } + return soilTypes; + } + + public static Block findPlantSoil(Block plant) { + final Set soilTypes = getPlantSoilTypes(plant.getType()); + if (soilTypes.size() <= 0) { + return null; + } + return findSoilBelow(plant, soilTypes); + } + + public static boolean isSoilPlant(Material mat) { + return mat.equals(Material.WHEAT) + || mat.equals(Material.MELON_STEM) + || mat.equals(Material.PUMPKIN_STEM) + || mat.equals(Material.CARROT) + || mat.equals(Material.POTATO) + || mat.equals(Material.CROPS) + || mat.equals(Material.MELON_BLOCK) + || mat.equals(Material.PUMPKIN); + } + + public static boolean isDirtPlant(Material mat) { + return mat.equals(Material.SUGAR_CANE_BLOCK) + || mat.equals(Material.MELON_BLOCK) + || mat.equals(Material.PUMPKIN); + } + + public static boolean isGrassPlant(Material mat) { + return mat.equals(Material.SUGAR_CANE_BLOCK) + || mat.equals(Material.MELON_BLOCK) + || mat.equals(Material.PUMPKIN); + } + + public static boolean isSandPlant(Material mat) { + return mat.equals(Material.CACTUS) + || mat.equals(Material.SUGAR_CANE_BLOCK); + } + + public static boolean isSoulSandPlant(Material mat) { + return mat.equals(Material.NETHER_WARTS); + } + + public static boolean isPlant(Block plant) { + return isPlant(plant.getType()); + } + + public static boolean isPlant(Material mat) { + return isSoilPlant(mat) + || isDirtPlant(mat) + || isGrassPlant(mat) + || isSandPlant(mat) + || isSoulSandPlant(mat); + } + + public static boolean isReinforceablePlant(Material mat) { + // If this list changes, update wouldPlantDoubleReinforce to account + // for the new soil types. + return mat.equals(Material.MELON_BLOCK) + || mat.equals(Material.PUMPKIN); + } + + public static int maxPlantHeight(Block plant) { + switch(plant.getType()) { + case CACTUS: + case SUGAR_CANE_BLOCK: + return 3; + default: + return 1; + } + } + + public static Block findSoilBelow(Block plant, Set desiredTypes) { + Block down = plant; + int max_depth = maxPlantHeight(plant); + for (int i = 0; i < max_depth; ++i) { + down = down.getRelative(BlockFace.DOWN); + if (desiredTypes.contains(down.getType())) { + return down; + } + } + return null; + } + + public static boolean isRail(Block block) { + return isRail(block.getType()); + } + + public static boolean isRail(Material mat) { + return mat.equals(Material.RAILS) + || mat.equals(Material.POWERED_RAIL) + || mat.equals(Material.ACTIVATOR_RAIL) + || mat.equals(Material.DETECTOR_RAIL); + } + + public static boolean wouldPlantDoubleReinforce(final Block block) { + final Material blockMat = block.getType(); + if (isReinforceablePlant(blockMat) + && AccessDelegate.getDelegate(block).getReinforcement() != null) { + return true; + } + final Block above = block.getRelative(BlockFace.UP); + final Material aboveMat = above.getType(); + if (isReinforceablePlant(aboveMat)) { + final Set soilTypes = getPlantSoilTypes(aboveMat); + if (soilTypes.contains(blockMat) + && Citadel.getReinforcementManager().getReinforcement(above) != null) { + return true; + } + } + return false; + } } diff --git a/src/com/untamedears/citadel/access/AccessDelegate.java b/src/com/untamedears/citadel/access/AccessDelegate.java index 88c52ab6..58565092 100644 --- a/src/com/untamedears/citadel/access/AccessDelegate.java +++ b/src/com/untamedears/citadel/access/AccessDelegate.java @@ -1,5 +1,7 @@ package com.untamedears.citadel.access; +import static com.untamedears.citadel.Utility.isPlant; + import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.material.Bed; @@ -7,6 +9,7 @@ import org.bukkit.material.MaterialData; import com.untamedears.citadel.Citadel; +import com.untamedears.citadel.Citadel.VerboseMsg; import com.untamedears.citadel.entity.IReinforcement; /** @@ -19,23 +22,27 @@ public abstract class AccessDelegate { public static AccessDelegate getDelegate(Block block) { MaterialData data = block.getState().getData(); - if (data instanceof Door) { + if (DoorAccessDelegate.canDelegate(block, data)) { return new DoorAccessDelegate(block, (Door) data); - } else if (data instanceof Bed) { + + } else if (BedAccessDelegate.canDelegate(block, data)) { return new BedAccessDelegate(block, (Bed) data); - } else if (block.getType() == Material.CHEST) { + + } else if (ChestAccessDelegate.canDelegate(block, data)) { return new ChestAccessDelegate(block, data); - } else { - return new AccessDelegate(block, data) { - @Override - protected boolean shouldDelegate() { - return false; - } - @Override - protected void delegate() { - } - }; + + } else if (CropAccessDelegate.canDelegate(block, data)) { + return new CropAccessDelegate(block, data); } + return new AccessDelegate(block, data) { + @Override + protected boolean shouldDelegate() { + return false; + } + @Override + protected void delegate() { + } + }; } protected Block block; @@ -46,10 +53,19 @@ public AccessDelegate(Block block, T data) { this.block = block; this.data = data; + boolean show_info = !(this instanceof CropAccessDelegate); if (shouldDelegate()) { - Citadel.info("Attempted interaction with %s block at " + block.getLocation().toString()); + if (show_info) { + Citadel.verbose( + VerboseMsg.InteractionAttempt, + block.getLocation().toString()); + } delegate(); - Citadel.info("Delegated to %s block at " + block.getLocation().toString()); + if (show_info) { + Citadel.verbose( + VerboseMsg.ReinDelegation, + block.getLocation().toString()); + } } } @@ -64,4 +80,8 @@ public IReinforcement getReinforcement() { if (reinforcement == null) reinforcement = Citadel.getReinforcementManager().getReinforcement(block); return reinforcement; } + + public boolean isReinforced() { + return getReinforcement() != null; + } } diff --git a/src/com/untamedears/citadel/access/BedAccessDelegate.java b/src/com/untamedears/citadel/access/BedAccessDelegate.java index 08b3eb90..e239f192 100644 --- a/src/com/untamedears/citadel/access/BedAccessDelegate.java +++ b/src/com/untamedears/citadel/access/BedAccessDelegate.java @@ -1,7 +1,9 @@ package com.untamedears.citadel.access; +import org.bukkit.block.Block; import org.bukkit.block.Block; import org.bukkit.material.Bed; +import org.bukkit.material.MaterialData; /** * Created by IntelliJ IDEA. @@ -10,6 +12,10 @@ * Time: 3:24 PM */ public class BedAccessDelegate extends AccessDelegate { + public static boolean canDelegate(Block block, MaterialData data) { + return (data instanceof Bed); + } + public BedAccessDelegate(Block block, Bed data) { super(block, data); } diff --git a/src/com/untamedears/citadel/access/ChestAccessDelegate.java b/src/com/untamedears/citadel/access/ChestAccessDelegate.java index 79ec0d1f..9f4de856 100644 --- a/src/com/untamedears/citadel/access/ChestAccessDelegate.java +++ b/src/com/untamedears/citadel/access/ChestAccessDelegate.java @@ -1,5 +1,6 @@ package com.untamedears.citadel.access; +import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.material.MaterialData; @@ -12,6 +13,10 @@ * Time: 3:27 PM */ public class ChestAccessDelegate extends AccessDelegate { + public static boolean canDelegate(Block block, MaterialData data) { + final Material mat = block.getType(); + return mat.equals(Material.CHEST) || mat.equals(Material.TRAPPED_CHEST); + } private Block attachedChest; diff --git a/src/com/untamedears/citadel/access/CropAccessDelegate.java b/src/com/untamedears/citadel/access/CropAccessDelegate.java new file mode 100644 index 00000000..829a8bed --- /dev/null +++ b/src/com/untamedears/citadel/access/CropAccessDelegate.java @@ -0,0 +1,52 @@ +package com.untamedears.citadel.access; + +import static com.untamedears.citadel.Utility.findPlantSoil; +import static com.untamedears.citadel.Utility.isPlant; +import static com.untamedears.citadel.Utility.isReinforceablePlant; + +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.material.MaterialData; + +import com.untamedears.citadel.Citadel; +import com.untamedears.citadel.Utility; +import com.untamedears.citadel.entity.IReinforcement; + +public class CropAccessDelegate extends AccessDelegate { + public static boolean canDelegate(Block block, MaterialData data) { + if (!Citadel.getConfigManager().allowReinforcedCrops() || !isPlant(block)) { + return false; + } + if (!isReinforceablePlant(block.getType())) { + return true; + } + IReinforcement rein = Citadel.getReinforcementManager().getReinforcement(block); + return rein == null; + } + + private Block soil; + + public CropAccessDelegate(Block block, MaterialData data) { + super(block, data); + } + + @Override + protected boolean shouldDelegate() { + if (!isPlant(block)) { + return false; + } + soil = findPlantSoil(block); + if (soil == null) { + return false; + } + reinforcement = Citadel.getReinforcementManager().getReinforcement(block); + return reinforcement == null; + } + + @Override + protected void delegate() { + reinforcement = null; + block = soil; + } +} diff --git a/src/com/untamedears/citadel/access/DoorAccessDelegate.java b/src/com/untamedears/citadel/access/DoorAccessDelegate.java index d33f0f96..8462c084 100644 --- a/src/com/untamedears/citadel/access/DoorAccessDelegate.java +++ b/src/com/untamedears/citadel/access/DoorAccessDelegate.java @@ -3,6 +3,7 @@ import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.material.Door; +import org.bukkit.material.MaterialData; /** * Created by IntelliJ IDEA. * User: chrisrico @@ -10,6 +11,10 @@ * Time: 3:26 PM */ public class DoorAccessDelegate extends AccessDelegate { + public static boolean canDelegate(Block block, MaterialData data) { + return (data instanceof Door); + } + public DoorAccessDelegate(Block block, Door data) { super(block, data); } @@ -23,4 +28,4 @@ protected boolean shouldDelegate() { protected void delegate() { block = block.getRelative(BlockFace.DOWN); } -} \ No newline at end of file +} diff --git a/src/com/untamedears/citadel/command/CommandHandler.java b/src/com/untamedears/citadel/command/CommandHandler.java index 80e9b6b8..7e58979e 100644 --- a/src/com/untamedears/citadel/command/CommandHandler.java +++ b/src/com/untamedears/citadel/command/CommandHandler.java @@ -6,10 +6,43 @@ import java.util.Map; import org.bukkit.command.CommandSender; -import org.bukkit.command.ConsoleCommandSender; import com.untamedears.citadel.Citadel; -import com.untamedears.citadel.command.ConsoleCommand; +import com.untamedears.citadel.command.commands.AcidCommand; +import com.untamedears.citadel.command.commands.AddModCommand; +import com.untamedears.citadel.command.commands.AllowCommand; +import com.untamedears.citadel.command.commands.BypassCommand; +import com.untamedears.citadel.command.commands.ConsoleCommands; +import com.untamedears.citadel.command.commands.CreateCommand; +import com.untamedears.citadel.command.commands.DeleteCommand; +import com.untamedears.citadel.command.commands.DisallowCommand; +import com.untamedears.citadel.command.commands.DisciplineCommand; +import com.untamedears.citadel.command.commands.FortifyCommand; +import com.untamedears.citadel.command.commands.GroupCommand; +import com.untamedears.citadel.command.commands.GroupInfoCommand; +import com.untamedears.citadel.command.commands.GroupStatsCommand; +import com.untamedears.citadel.command.commands.GroupsCommand; +import com.untamedears.citadel.command.commands.InfoCommand; +import com.untamedears.citadel.command.commands.InsecureCommand; +import com.untamedears.citadel.command.commands.JoinCommand; +import com.untamedears.citadel.command.commands.LeaveCommand; +import com.untamedears.citadel.command.commands.MaterialsCommand; +import com.untamedears.citadel.command.commands.MatureCommand; +import com.untamedears.citadel.command.commands.MembersCommand; +import com.untamedears.citadel.command.commands.ModeratorsCommand; +import com.untamedears.citadel.command.commands.NonReinforceableCommand; +import com.untamedears.citadel.command.commands.OffCommand; +import com.untamedears.citadel.command.commands.PasswordCommand; +import com.untamedears.citadel.command.commands.PlayerStatsCommand; +import com.untamedears.citadel.command.commands.PrivateCommand; +import com.untamedears.citadel.command.commands.PublicCommand; +import com.untamedears.citadel.command.commands.ReprieveCommand; +import com.untamedears.citadel.command.commands.ReinforceCommand; +import com.untamedears.citadel.command.commands.RemoveModCommand; +import com.untamedears.citadel.command.commands.SecurableCommand; +import com.untamedears.citadel.command.commands.StatsCommand; +import com.untamedears.citadel.command.commands.TransferCommand; +import com.untamedears.citadel.command.commands.VersionCommand; /** * User: JonnyD @@ -20,7 +53,45 @@ public class CommandHandler { private Map commands = new LinkedHashMap(); private Map identifiers = new HashMap(); - + + public void registerCommands() { + this.addCommand(new AcidCommand()); + this.addCommand(new AddModCommand()); + this.addCommand(new AllowCommand()); + this.addCommand(new BypassCommand()); + this.addCommand(new ConsoleCommands()); + this.addCommand(new CreateCommand()); + this.addCommand(new DeleteCommand()); + this.addCommand(new DisallowCommand()); + this.addCommand(new DisciplineCommand()); + this.addCommand(new FortifyCommand()); + this.addCommand(new GroupCommand()); + this.addCommand(new GroupInfoCommand()); + this.addCommand(new GroupsCommand()); + this.addCommand(new InfoCommand()); + this.addCommand(new InsecureCommand()); + this.addCommand(new JoinCommand()); + this.addCommand(new LeaveCommand()); + this.addCommand(new MaterialsCommand()); + this.addCommand(new MatureCommand()); + this.addCommand(new MembersCommand()); + this.addCommand(new ModeratorsCommand()); + this.addCommand(new NonReinforceableCommand()); + this.addCommand(new OffCommand()); + this.addCommand(new PasswordCommand()); + this.addCommand(new PrivateCommand()); + this.addCommand(new PublicCommand()); + this.addCommand(new ReprieveCommand()); + this.addCommand(new ReinforceCommand()); + this.addCommand(new RemoveModCommand()); + this.addCommand(new SecurableCommand()); + this.addCommand(new StatsCommand()); + this.addCommand(new GroupStatsCommand()); + this.addCommand(new PlayerStatsCommand()); + this.addCommand(new TransferCommand()); + this.addCommand(new VersionCommand()); + } + public void addCommand(Command command){ this.commands.put(command.getName().toLowerCase(), command); for(String ident : command.getIdentifiers()){ @@ -29,7 +100,6 @@ public void addCommand(Command command){ } public boolean dispatch(CommandSender sender, String label, String[] args){ - boolean isConsoleSender = sender instanceof ConsoleCommandSender; for(int argsIncluded = args.length; argsIncluded >= 0; argsIncluded--){ StringBuilder identifier = new StringBuilder(label); for(int i = 0; i < argsIncluded; i++){ @@ -40,12 +110,7 @@ public boolean dispatch(CommandSender sender, String label, String[] args){ if(cmd == null){ continue; } - boolean isConsoleCmd = cmd instanceof ConsoleCommand; - if (!isConsoleSender && isConsoleCmd) { - // Don't allow console commands to be run by Players. - return true; - } - String[] realArgs = (String[])Arrays.copyOfRange(args, argsIncluded, args.length); + String[] realArgs = Arrays.copyOfRange(args, argsIncluded, args.length); if(!cmd.isInProgress(sender)){ if((realArgs.length < cmd.getMinArguments()) || (realArgs.length > cmd.getMaxArguments())){ @@ -77,7 +142,7 @@ private void displayCommandHelp(Command cmd, CommandSender sender){ private Command getCmdFromIdent(String ident, CommandSender executor) { ident = ident.toLowerCase(); if(this.identifiers.containsKey(ident)){ - return (Command)this.identifiers.get(ident); + return this.identifiers.get(ident); } for(Command cmd : this.commands.values()){ diff --git a/src/com/untamedears/citadel/command/CommandUtils.java b/src/com/untamedears/citadel/command/CommandUtils.java index 856303ea..291ee73e 100644 --- a/src/com/untamedears/citadel/command/CommandUtils.java +++ b/src/com/untamedears/citadel/command/CommandUtils.java @@ -1,6 +1,7 @@ package com.untamedears.citadel.command; import java.util.HashMap; +import java.util.List; import java.util.Set; import org.bukkit.Material; @@ -35,11 +36,11 @@ public static HashMap countReinforcements(String name) { return hash; } - public static void printReinforcements(CommandSender sender, String name, HashMap reinforcements) { - sender.sendMessage("Group name: "+name); + public static void formatReinforcements(List output, String name, HashMap reinforcements) { + output.add("Group name: "+name); Set mats = reinforcements.keySet(); for (Material m : mats) { - sender.sendMessage(m.name()+": "+reinforcements.get(m)); + output.add(m.name()+": "+reinforcements.get(m)); } } @@ -85,14 +86,14 @@ public static String joinFactionSet(Set set) { return result; } - public static void printGroupMembers(CommandSender sender, String name) { + public static void formatGroupMembers(List output, String name) { GroupManager groupManager = Citadel.getGroupManager(); Faction group = groupManager.getGroup(name); if (group != null) { - sender.sendMessage("Group name: "+name); - sender.sendMessage("Admin: "+group.getFounder()); - sender.sendMessage("Moderators: "+joinModeratorSet(groupManager.getModeratorsOfGroup(name))); - sender.sendMessage("Members: "+joinMemberSet(groupManager.getMembersOfGroup(name))); + output.add("Group name: "+name); + output.add("Admin: "+group.getFounder()); + output.add("Moderators: "+joinModeratorSet(groupManager.getModeratorsOfGroup(name))); + output.add("Members: "+joinMemberSet(groupManager.getMembersOfGroup(name))); } } } diff --git a/src/com/untamedears/citadel/command/ConsoleCommand.java b/src/com/untamedears/citadel/command/ConsoleCommand.java deleted file mode 100644 index dda62ffa..00000000 --- a/src/com/untamedears/citadel/command/ConsoleCommand.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.untamedears.citadel.command; - -import org.bukkit.command.CommandSender; - -public abstract class ConsoleCommand implements Command { - private final String name_; - private String description_; - private String usage_; - private int minArguments_; - private int maxArguments_; - private String[] identifiers_; - - public ConsoleCommand(String name) { - name_ = name; - description_ = ""; - usage_ = ""; - minArguments_ = 0; - maxArguments_ = 0; - identifiers_ = new String[0]; - } - - public String getName() { - return name_; - } - - public String getDescription() { - return description_; - } - - public String getUsage() { - return usage_; - } - - public int getMinArguments() { - return minArguments_; - } - - public int getMaxArguments() { - return maxArguments_; - } - - public String[] getIdentifiers() { - return identifiers_; - } - - public boolean isIdentifier(CommandSender executor, String input) { - for (String identifier : identifiers_) { - if (input.equalsIgnoreCase(identifier)) { - return true; - } - } - return false; - } - - public boolean isInProgress(CommandSender sender) { - return false; - } - - public void setDescription(String description) { - description_ = description; - } - - public void setUsage(String usage){ - usage_ = usage; - } - - public void setArgumentRange(int min, int max) { - minArguments_ = min; - maxArguments_ = max; - } - - public void setIdentifiers(String[] identifiers) { - identifiers_ = identifiers; - } -} diff --git a/src/com/untamedears/citadel/command/commands/AcidCommand.java b/src/com/untamedears/citadel/command/commands/AcidCommand.java new file mode 100644 index 00000000..22af5667 --- /dev/null +++ b/src/com/untamedears/citadel/command/commands/AcidCommand.java @@ -0,0 +1,118 @@ +package com.untamedears.citadel.command.commands; + +import static com.untamedears.citadel.Utility.reinforcementBroken; +import static com.untamedears.citadel.Utility.timeUntilMature; + +import java.util.Iterator; + +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.BlockIterator; + +import com.untamedears.citadel.Citadel; +import com.untamedears.citadel.access.AccessDelegate; +import com.untamedears.citadel.command.PlayerCommand; +import com.untamedears.citadel.entity.IReinforcement; +import com.untamedears.citadel.entity.PlayerReinforcement; + +public class AcidCommand extends PlayerCommand { + + public AcidCommand() { + super("Acid block"); + setDescription("Trigger acid block to eat a reinforcement"); + setUsage("/ctacid"); + setArgumentRange(0, 0); + setIdentifiers(new String[] {"ctacid"}); + } + + public boolean execute(CommandSender sender, String[] args) { + Integer acidBlockType = Citadel.getConfigManager().getAcidBlockType(); + if (acidBlockType == null) { + sender.sendMessage("Acid blocks are disabled"); + return true; + } + if (!(sender instanceof Player)) { + sender.sendMessage("Player only command"); + return true; + } + boolean successfulAcid = false; + Player player = (Player)sender; + String playerName = player.getName(); + Iterator itr = new BlockIterator(player, 40); // Within 2.5 chunks + while (itr.hasNext()) { + final Block block = itr.next(); + final int mat = block.getTypeId(); + if (mat != acidBlockType) { + continue; + } + IReinforcement rein = Citadel.getReinforcementManager().getReinforcement(block); + if (!(rein instanceof PlayerReinforcement)) { + continue; + } + if (timeUntilMature(rein) > 0) { + // Immature acid block + sender.sendMessage("The acid block isn't ready"); + return true; + } + PlayerReinforcement pr = (PlayerReinforcement)rein; + if (!pr.isAccessible(playerName)) { + sender.sendMessage("You cannot use this acid block"); + return true; + } + if (pr.getMaxDurability() <= Citadel.getConfigManager().getMaturationInterval()) { + sender.sendMessage("The acid block isn't strong enough to break anything"); + return true; + } + Block above = block.getRelative(BlockFace.UP); + IReinforcement aboveRein = AccessDelegate.getDelegate(above).getReinforcement(); + if (!(aboveRein instanceof PlayerReinforcement)) { + // This isn't really an acid block as there's no reinforcement above, ignore + continue; + } + PlayerReinforcement abovePr = (PlayerReinforcement)aboveRein; + if (abovePr.getMaxDurability() > pr.getMaxDurability()) { + sender.sendMessage("The acid block isn't strong enough"); + return true; + } + // if they try break bedrock, return + if (above.getType() == Material.BEDROCK + || above.getType() == Material.ENDER_PORTAL + || above.getType() == Material.ENDER_PORTAL_FRAME) { + sender.sendMessage("You cant break this block"); + return true; + } + // Break block above + aboveRein.setDurability(0); + reinforcementBroken(aboveRein); + above.setType(Material.AIR); + // Damage acid block + Double acidDamagePercentage = + Citadel.getConfigManager().getAcidBlockReinforcementTax(); + if (acidDamagePercentage > 0.9999999D) { + pr.setDurability(0); + } else if (acidDamagePercentage > 0.0000001) { + int damage = (int)( + (double)pr.getScaledMaxDurability() * acidDamagePercentage); + int durability = pr.getDurability(); + if (durability <= damage) { + pr.setDurability(0); + } else { + pr.setDurability(durability - damage); + } + } + // Break acid block + reinforcementBroken(pr); + block.breakNaturally(); + successfulAcid = true; + } + if (!successfulAcid) { + sender.sendMessage("No acid block was found"); + } + return true; + } +} diff --git a/src/com/untamedears/citadel/command/commands/AddModCommand.java b/src/com/untamedears/citadel/command/commands/AddModCommand.java index 261858f5..f44fa91b 100644 --- a/src/com/untamedears/citadel/command/commands/AddModCommand.java +++ b/src/com/untamedears/citadel/command/commands/AddModCommand.java @@ -40,6 +40,10 @@ public boolean execute(CommandSender sender, String[] args) { sendMessage(sender, ChatColor.RED, "Group doesn't exist"); return true; } + if (group.isDisciplined()) { + sendMessage(sender, ChatColor.RED, Faction.kDisciplineMsg); + return true; + } String founderName = sender.getName(); if(!group.isFounder(founderName)){ sendMessage(sender, ChatColor.RED, "Invalid permission to modify this group"); @@ -57,8 +61,12 @@ public boolean execute(CommandSender sender, String[] args) { sendMessage(sender, ChatColor.RED, "%s is already a moderator of %s", targetName, groupName); return true; } + Player player = null; + if (sender instanceof Player) { + player = (Player)sender; + } if(group.isMember(targetName)){ - groupManager.removeMemberFromGroup(groupName, targetName); + groupManager.removeMemberFromGroup(groupName, targetName, player); } MemberManager memberManager = Citadel.getMemberManager(); Member member = memberManager.getMember(targetName); @@ -66,7 +74,7 @@ public boolean execute(CommandSender sender, String[] args) { member = new Member(targetName); memberManager.addMember(member); } - groupManager.addModeratorToGroup(groupName, targetName); + groupManager.addModeratorToGroup(groupName, targetName, player); sendMessage(sender, ChatColor.GREEN, "%s has been added as a moderator to %s", targetName, groupName); if(memberManager.isOnline(targetName)){ sendMessage(memberManager.getOnlinePlayer(targetName), ChatColor.GREEN, "You have been added as " + diff --git a/src/com/untamedears/citadel/command/commands/AllowCommand.java b/src/com/untamedears/citadel/command/commands/AllowCommand.java index 10e4d051..2bb5a727 100644 --- a/src/com/untamedears/citadel/command/commands/AllowCommand.java +++ b/src/com/untamedears/citadel/command/commands/AllowCommand.java @@ -37,9 +37,13 @@ public boolean execute(CommandSender sender, String[] args) { GroupManager groupManager = Citadel.getGroupManager(); Faction group = groupManager.getGroup(groupName); if(group == null){ - sendMessage(sender, ChatColor.RED, "Group doesn't exist"); - return true; - } + sendMessage(sender, ChatColor.RED, "Group doesn't exist"); + return true; + } + if (group.isDisciplined()) { + sendMessage(sender, ChatColor.RED, Faction.kDisciplineMsg); + return true; + } String senderName = sender.getName(); if(!group.isFounder(senderName) && !group.isModerator(senderName)){ sendMessage(sender, ChatColor.RED, "Invalid access to modify this group"); @@ -65,13 +69,17 @@ public boolean execute(CommandSender sender, String[] args) { sendMessage(sender, ChatColor.RED, "%s is already a member of %s", targetName, group.getName()); return true; } + Player player = null; + if (sender instanceof Player) { + player = (Player)sender; + } MemberManager memberManager = Citadel.getMemberManager(); Member member = memberManager.getMember(targetName); if(member == null){ member = new Member(targetName); memberManager.addMember(member); } - groupManager.addMemberToGroup(groupName, targetName); + groupManager.addMemberToGroup(groupName, targetName, player); sendMessage(sender, ChatColor.GREEN, "Allowed %s access to %s blocks", targetName, groupName); if(memberManager.isOnline(targetName)){ sendMessage(memberManager.getOnlinePlayer(targetName), ChatColor.GREEN, diff --git a/src/com/untamedears/citadel/command/commands/ConsoleCommands.java b/src/com/untamedears/citadel/command/commands/ConsoleCommands.java index f1542b85..4e061d61 100644 --- a/src/com/untamedears/citadel/command/commands/ConsoleCommands.java +++ b/src/com/untamedears/citadel/command/commands/ConsoleCommands.java @@ -6,15 +6,14 @@ import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; -import org.bukkit.command.ConsoleCommandSender; import com.untamedears.citadel.Citadel; import com.untamedears.citadel.ConfigManager; -import com.untamedears.citadel.command.ConsoleCommand; +import com.untamedears.citadel.command.PlayerCommand; import com.untamedears.citadel.dao.CitadelDao; import com.untamedears.citadel.dao.CitadelCachingDao; -public class ConsoleCommands extends ConsoleCommand { +public class ConsoleCommands extends PlayerCommand { public ConsoleCommands() { super("Console Commands"); setDescription("Handles Console Commands"); @@ -24,25 +23,26 @@ public ConsoleCommands() { } public boolean execute(CommandSender sender, String[] args) { - if (!(sender instanceof ConsoleCommandSender)) { - return true; - } String command = args[0]; if (command.equalsIgnoreCase("getconfig")) { - return GetConfig((ConsoleCommandSender)sender, args); + return GetConfig(sender, args); } else if (command.equalsIgnoreCase("setconfig")) { - return SetConfig((ConsoleCommandSender)sender, args); + return SetConfig(sender, args); } else if (command.equalsIgnoreCase("daocachestats")) { - return GetDaoCacheStats((ConsoleCommandSender)sender, args); + return GetDaoCacheStats(sender, args); } else if (command.equalsIgnoreCase("daocachestatsslow")) { - return GetSlowDaoCacheStats((ConsoleCommandSender)sender, args); + return GetSlowDaoCacheStats(sender, args); } else if (command.equalsIgnoreCase("forcecacheflush")) { - return ForceCacheFlush((ConsoleCommandSender)sender, args); + return ForceCacheFlush(sender, args); + } else if (command.equalsIgnoreCase("forcechunkflush")) { + return ForceChunkFlush(sender, args); + } else if (command.equalsIgnoreCase("forcechunkunload")) { + return ForceChunkUnload(sender, args); } return false; } - public boolean GetConfig(ConsoleCommandSender sender, String[] args) { + public boolean GetConfig(CommandSender sender, String[] args) { if (args.length < 2) { sendMessage(sender, ChatColor.RED, "Specify setting"); return true; @@ -76,7 +76,7 @@ public boolean GetConfig(ConsoleCommandSender sender, String[] args) { return true; } - public boolean SetConfig(ConsoleCommandSender sender, String[] args) { + public boolean SetConfig(CommandSender sender, String[] args) { if (args.length < 3) { sendMessage(sender, ChatColor.RED, "Specify setting and new value"); return true; @@ -131,7 +131,7 @@ public boolean SetConfig(ConsoleCommandSender sender, String[] args) { return true; } - public boolean GetDaoCacheStats(ConsoleCommandSender sender, String[] args) { + public boolean GetDaoCacheStats(CommandSender sender, String[] args) { CitadelDao std_dao = Citadel.getDao(); if (!(std_dao instanceof CitadelCachingDao)) { sendMessage(sender, ChatColor.RED, "Sorry, the Caching DAO is not being used."); @@ -142,7 +142,7 @@ public boolean GetDaoCacheStats(ConsoleCommandSender sender, String[] args) { return true; } - public boolean GetSlowDaoCacheStats(ConsoleCommandSender sender, String[] args) { + public boolean GetSlowDaoCacheStats(CommandSender sender, String[] args) { CitadelDao std_dao = Citadel.getDao(); if (!(std_dao instanceof CitadelCachingDao)) { sendMessage(sender, ChatColor.RED, "Sorry, the Caching DAO is not being used."); @@ -159,7 +159,7 @@ public boolean GetSlowDaoCacheStats(ConsoleCommandSender sender, String[] args) return true; } - public boolean ForceCacheFlush(ConsoleCommandSender sender, String[] args) { + public boolean ForceCacheFlush(CommandSender sender, String[] args) { int flushCount = 5; if (args.length >= 2) { flushCount = Integer.parseInt(args[1]); @@ -174,4 +174,38 @@ public boolean ForceCacheFlush(ConsoleCommandSender sender, String[] args) { sendMessage(sender, ChatColor.YELLOW, "Flush complete."); return true; } + + public boolean ForceChunkFlush(CommandSender sender, String[] args) { + if (args.length < 2) { + sendMessage(sender, ChatColor.RED, "Please provide a Chunk ID"); + return true; + } + String chunk_id = args[1]; + CitadelDao std_dao = Citadel.getDao(); + if (!(std_dao instanceof CitadelCachingDao)) { + sendMessage(sender, ChatColor.RED, "Sorry, the Caching DAO is not being used."); + return true; + } + CitadelCachingDao dao = (CitadelCachingDao)std_dao; + dao.ForceChunkFlush(chunk_id); + sendMessage(sender, ChatColor.YELLOW, "Flush complete."); + return true; + } + + public boolean ForceChunkUnload(CommandSender sender, String[] args) { + if (args.length < 2) { + sendMessage(sender, ChatColor.RED, "Please provide a Chunk ID"); + return true; + } + String chunk_id = args[1]; + CitadelDao std_dao = Citadel.getDao(); + if (!(std_dao instanceof CitadelCachingDao)) { + sendMessage(sender, ChatColor.RED, "Sorry, the Caching DAO is not being used."); + return true; + } + CitadelCachingDao dao = (CitadelCachingDao)std_dao; + dao.ForceChunkUnload(chunk_id); + sendMessage(sender, ChatColor.YELLOW, "Unload complete."); + return true; + } } diff --git a/src/com/untamedears/citadel/command/commands/CreateCommand.java b/src/com/untamedears/citadel/command/commands/CreateCommand.java index ad4ccb2b..f8f1c703 100644 --- a/src/com/untamedears/citadel/command/commands/CreateCommand.java +++ b/src/com/untamedears/citadel/command/commands/CreateCommand.java @@ -3,6 +3,7 @@ import static com.untamedears.citadel.Utility.sendMessage; import org.bukkit.ChatColor; +import org.bukkit.entity.Player; import org.bukkit.command.CommandSender; import com.untamedears.citadel.Citadel; @@ -43,8 +44,12 @@ public boolean execute(CommandSender sender, String[] args) { sendMessage(sender, ChatColor.RED, "You already have too many groups. %s is the limit. Try deleting one first", groupsAllowed); return true; } + Player player = null; + if (sender instanceof Player) { + player = (Player)sender; + } Faction group = new Faction(groupName, senderName); - groupManager.addGroup(group); + groupManager.addGroup(group, player); sendMessage(sender, ChatColor.GREEN, "Created group: %s", groupName); return true; } diff --git a/src/com/untamedears/citadel/command/commands/DeleteCommand.java b/src/com/untamedears/citadel/command/commands/DeleteCommand.java index 784aac7d..9532d8f9 100644 --- a/src/com/untamedears/citadel/command/commands/DeleteCommand.java +++ b/src/com/untamedears/citadel/command/commands/DeleteCommand.java @@ -4,6 +4,7 @@ import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; import com.untamedears.citadel.Citadel; import com.untamedears.citadel.GroupManager; @@ -11,6 +12,7 @@ import com.untamedears.citadel.ReinforcementManager; import com.untamedears.citadel.command.PlayerCommand; import com.untamedears.citadel.entity.Faction; +import com.untamedears.citadel.entity.PersonalGroup; /** * User: JonnyD @@ -36,6 +38,10 @@ public boolean execute(CommandSender sender, String[] args) { sendMessage(sender, ChatColor.RED, "Group doesn't exist"); return true; } + if (group.isDisciplined()) { + sendMessage(sender, ChatColor.RED, Faction.kDisciplineMsg); + return true; + } String senderName = sender.getName(); if(!group.isFounder(senderName)){ sendMessage(sender, ChatColor.RED, "Invalid permission to delete this group"); @@ -45,17 +51,14 @@ public boolean execute(CommandSender sender, String[] args) { sendMessage(sender, ChatColor.RED, "You cannot delete your default group"); return true; } + Player player = null; + if (sender instanceof Player) { + player = (Player)sender; + } PersonalGroupManager personalGroupManager = Citadel.getPersonalGroupManager(); - String personalGroup = personalGroupManager.getPersonalGroup(senderName).getGroupName(); - - ReinforcementManager reinforcementManager = Citadel.getReinforcementManager(); - reinforcementManager.moveReinforcements(groupName, personalGroup); - - groupManager.removeAllMembersFromGroup(groupName); - groupManager.removeAllModeratorsFromGroup(groupName); - groupManager.removeGroup(group); - + PersonalGroup personalGroup = personalGroupManager.getPersonalGroup(senderName); + groupManager.removeGroup(group, personalGroup, player); sendMessage(sender, ChatColor.GREEN, "Deleted group: %s", groupName); return true; } diff --git a/src/com/untamedears/citadel/command/commands/DisallowCommand.java b/src/com/untamedears/citadel/command/commands/DisallowCommand.java index ae86e61a..05074fc7 100644 --- a/src/com/untamedears/citadel/command/commands/DisallowCommand.java +++ b/src/com/untamedears/citadel/command/commands/DisallowCommand.java @@ -4,6 +4,7 @@ import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; import com.untamedears.citadel.Citadel; import com.untamedears.citadel.GroupManager; @@ -33,6 +34,10 @@ public boolean execute(CommandSender sender, String[] args) { sendMessage(sender, ChatColor.RED, "Group doesn't exist"); return true; } + if (group.isDisciplined()) { + sendMessage(sender, ChatColor.RED, Faction.kDisciplineMsg); + return true; + } String senderName = sender.getName(); if(!group.isFounder(senderName) && !group.isModerator(senderName)){ sendMessage(sender, ChatColor.RED, "Invalid access to modify this group"); @@ -47,7 +52,11 @@ public boolean execute(CommandSender sender, String[] args) { sendMessage(sender, ChatColor.RED, "%s is not a member of this group", playerName); return true; } - groupManager.removeMemberFromGroup(groupName, playerName); + Player player = null; + if (sender instanceof Player) { + player = (Player)sender; + } + groupManager.removeMemberFromGroup(groupName, playerName, player); sendMessage(sender, ChatColor.GREEN, "Disallowed %s from access to %s blocks", playerName, group.getName()); return true; } diff --git a/src/com/untamedears/citadel/command/commands/DisciplineCommand.java b/src/com/untamedears/citadel/command/commands/DisciplineCommand.java new file mode 100644 index 00000000..4e04a321 --- /dev/null +++ b/src/com/untamedears/citadel/command/commands/DisciplineCommand.java @@ -0,0 +1,65 @@ +package com.untamedears.citadel.command.commands; + +import static com.untamedears.citadel.Utility.sendMessage; + +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import com.untamedears.citadel.Citadel; +import com.untamedears.citadel.GroupManager; +import com.untamedears.citadel.command.PlayerCommand; +import com.untamedears.citadel.entity.Faction; + +public class DisciplineCommand extends PlayerCommand { + + public DisciplineCommand() { + super("Discipline Group"); + setDescription("Disciplines a group"); + setUsage("/ctdiscipline §8 [del] "); + setArgumentRange(1,2); + setIdentifiers(new String[] {"ctadg"}); + } + + public boolean execute(CommandSender sender, String[] args) { + String group_name = null; + boolean delete_group = false; + if (args[0].equalsIgnoreCase("del")) { + delete_group = true; + group_name = args[1]; + } else { + group_name = args[0]; + } + GroupManager group_manager = Citadel.getGroupManager(); + Faction group = group_manager.getGroup(group_name); + if (group == null) { + sendMessage(sender, ChatColor.RED, "Group doesn't exist"); + return true; + } + if (group.isDeleted()) { + sendMessage(sender, ChatColor.YELLOW, "Group already deleted"); + return true; + } + if (group.isDisabled()) { + if (delete_group) { + group.setDisabled(false); + } else { + sendMessage(sender, ChatColor.YELLOW, "Group already disabled"); + return true; + } + } + if (delete_group) { + group.setDeleted(true); + sendMessage(sender, ChatColor.GREEN, "Group %s is deleted", group_name); + } else { + group.setDisabled(true); + sendMessage(sender, ChatColor.GREEN, "Group %s is disabled", group_name); + } + Player player = null; + if (sender instanceof Player) { + player = (Player)sender; + } + group_manager.addGroup(group, player); + return true; + } +} diff --git a/src/com/untamedears/citadel/command/commands/FortifyCommand.java b/src/com/untamedears/citadel/command/commands/FortifyCommand.java index d675224a..6ba01967 100644 --- a/src/com/untamedears/citadel/command/commands/FortifyCommand.java +++ b/src/com/untamedears/citadel/command/commands/FortifyCommand.java @@ -68,6 +68,10 @@ public boolean execute(CommandSender sender, String[] args) { sendMessage(sender, ChatColor.RED, "Group doesn't exist"); return true; } + if (group.isDisciplined()) { + sendMessage(sender, ChatColor.RED, Faction.kDisciplineMsg); + return true; + } String senderName = sender.getName(); if(!group.isFounder(senderName) && !group.isModerator(senderName)){ sendMessage(sender, ChatColor.RED, "Invalid permission to use this group"); diff --git a/src/com/untamedears/citadel/command/commands/GroupCommand.java b/src/com/untamedears/citadel/command/commands/GroupCommand.java index c9fde976..fac0e1eb 100644 --- a/src/com/untamedears/citadel/command/commands/GroupCommand.java +++ b/src/com/untamedears/citadel/command/commands/GroupCommand.java @@ -35,6 +35,10 @@ public boolean execute(CommandSender sender, String[] args) { sendMessage(sender, ChatColor.RED, "Group doesn't exist"); return true; } + if (group.isDisciplined()) { + sendMessage(sender, ChatColor.RED, Faction.kDisciplineMsg); + return true; + } String senderName = sender.getName(); if(!group.isFounder(senderName) && !group.isModerator(senderName)){ sendMessage(sender, ChatColor.RED, "Invalid permission to use this group"); diff --git a/src/com/untamedears/citadel/command/commands/GroupInfoCommand.java b/src/com/untamedears/citadel/command/commands/GroupInfoCommand.java index e5dabf28..8f166989 100644 --- a/src/com/untamedears/citadel/command/commands/GroupInfoCommand.java +++ b/src/com/untamedears/citadel/command/commands/GroupInfoCommand.java @@ -48,6 +48,16 @@ public boolean execute(CommandSender sender, String[] args) { } sender.sendMessage(new StringBuilder().append("§cJoinable:§e ").append(joinable).toString()); } + if (group.isDisciplined()) { + StringBuilder discipline = new StringBuilder().append("§cDiscipline:§e"); + if (group.isDisabled()) { + discipline.append(" Disabled"); + } + if (group.isDeleted()) { + discipline.append(" Deleted"); + } + sender.sendMessage(discipline.toString()); + } return true; } diff --git a/src/com/untamedears/citadel/command/commands/GroupStatsCommand.java b/src/com/untamedears/citadel/command/commands/GroupStatsCommand.java index 80e9f957..423385dc 100644 --- a/src/com/untamedears/citadel/command/commands/GroupStatsCommand.java +++ b/src/com/untamedears/citadel/command/commands/GroupStatsCommand.java @@ -1,23 +1,78 @@ package com.untamedears.citadel.command.commands; +import static com.untamedears.citadel.Utility.sendMessage; + +import java.util.LinkedList; +import java.util.List; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import com.untamedears.citadel.Citadel; import com.untamedears.citadel.command.CommandUtils; -import com.untamedears.citadel.command.ConsoleCommand; - -public class GroupStatsCommand extends ConsoleCommand { - - public GroupStatsCommand() { - super("View Group Stats"); - setDescription("View citadel group stats"); - setUsage("/ctgstats "); - setArgumentRange(1, 1); - setIdentifiers(new String[] {"ctgstats", "ctgst"}); - } - - public boolean execute(CommandSender sender, String[] args) { - CommandUtils.printGroupMembers(sender, args[0]); - CommandUtils.printReinforcements(sender, args[0], CommandUtils.countReinforcements(args[0])); - return true; - } +import com.untamedears.citadel.command.PlayerCommand; +import com.untamedears.citadel.entity.Faction; + +public class GroupStatsCommand extends PlayerCommand { + + public class SendResultsTask implements Runnable { + public QueryDbTask previousTask; + public SendResultsTask(QueryDbTask pt) { + previousTask = pt; + } + @Override + public void run() { + for (String line : previousTask.results) { + previousTask.sender.sendMessage(line); + } + } + } + + public class QueryDbTask implements Runnable { + public CommandSender sender; + public String groupName; + public List results = new LinkedList(); + public QueryDbTask(CommandSender s, String gn) { + sender = s; + groupName = gn; + } + @Override + public void run() { + CommandUtils.formatGroupMembers(results, groupName); + /*CommandUtils.formatReinforcements(results, groupName, + CommandUtils.countReinforcements(groupName));*/ + Bukkit.getScheduler().runTask( + Citadel.getPlugin(), new SendResultsTask(this)); + } + } + + public GroupStatsCommand() { + super("View Group Stats"); + setDescription("View citadel group stats"); + setUsage("/ctgstats "); + setArgumentRange(1, 1); + setIdentifiers(new String[] {"ctgstats", "ctgst"}); + } + + public boolean execute(CommandSender sender, String[] args) { + String group_name = args[0]; + Faction group = Citadel.getGroupManager().getGroup(group_name); + if (group == null) { + sendMessage(sender, ChatColor.RED, "Group not found"); + return true; + } + if (sender instanceof Player && !sender.hasPermission("citadel.admin.ctgstats")) { + Player player = (Player)sender; + String player_name = player.getName(); + if (!player_name.equals(group.getFounder()) && !group.isModerator(player_name)) { + sendMessage(sender, ChatColor.RED, "You do not have access to this group's stats"); + return true; + } + } + Bukkit.getScheduler().runTaskAsynchronously( + Citadel.getPlugin(), new QueryDbTask(sender, group_name)); + return true; + } } diff --git a/src/com/untamedears/citadel/command/commands/GroupsCommand.java b/src/com/untamedears/citadel/command/commands/GroupsCommand.java index 1444df6e..973bbfc0 100644 --- a/src/com/untamedears/citadel/command/commands/GroupsCommand.java +++ b/src/com/untamedears/citadel/command/commands/GroupsCommand.java @@ -82,18 +82,22 @@ public boolean execute(CommandSender sender, String[] args) { } for(int g = start; g < end; g++){ Faction group = groups.get(g); - String line = group.getName(); + StringBuilder line = new StringBuilder() + .append(group.getName()); if(ownedGroups.contains(group)){ - line = line + " (Owner)"; + line.append(" (Owner)"); } else if(moderatedGroups.contains(group)){ - line = line + " (Moderator)"; + line.append(" (Moderator)"); } else if(memberGroups.contains(group)){ - line = line + " (Member)"; + line.append(" (Member)"); } if(group.getName().equalsIgnoreCase(personalGroupName)){ - line = line + " (Default Group)"; + line.append(" (Default Group)"); } - sendMessage(sender, ChatColor.WHITE, line); + if (group.isDisciplined()) { + line.append(" (Disciplined)"); + } + sendMessage(sender, ChatColor.WHITE, line.toString()); } if(page + 1 < numPages){ sendMessage(sender, ChatColor.GRAY, "For more type \"/ctgroups [n]\""); diff --git a/src/com/untamedears/citadel/command/commands/InsecureCommand.java b/src/com/untamedears/citadel/command/commands/InsecureCommand.java new file mode 100644 index 00000000..1cbc0844 --- /dev/null +++ b/src/com/untamedears/citadel/command/commands/InsecureCommand.java @@ -0,0 +1,34 @@ +package com.untamedears.citadel.command.commands; + +import static com.untamedears.citadel.Utility.getSecurityLevel; +import static com.untamedears.citadel.Utility.setMultiMode; + +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import com.untamedears.citadel.PlacementMode; +import com.untamedears.citadel.SecurityLevel; +import com.untamedears.citadel.command.PlayerCommand; +import com.untamedears.citadel.entity.PlayerState; + +public class InsecureCommand extends PlayerCommand { + + public InsecureCommand() { + super("Insecure Mode"); + setDescription("Toggle insecure mode"); + setUsage("/ctinsecure"); + setIdentifiers(new String[] {"ctinsecure", "ctis"}); + } + + public boolean execute(CommandSender sender, String[] args) { + Player player = (Player) sender; + PlayerState state = PlayerState.get(player); + + SecurityLevel securityLevel = getSecurityLevel(args, player); + if (securityLevel == null) return false; + + setMultiMode(PlacementMode.INSECURE, SecurityLevel.PUBLIC, args, player, state); + return true; + } + +} diff --git a/src/com/untamedears/citadel/command/commands/JoinCommand.java b/src/com/untamedears/citadel/command/commands/JoinCommand.java index 9744b714..ae965fa7 100644 --- a/src/com/untamedears/citadel/command/commands/JoinCommand.java +++ b/src/com/untamedears/citadel/command/commands/JoinCommand.java @@ -4,6 +4,7 @@ import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; import com.untamedears.citadel.Citadel; import com.untamedears.citadel.GroupManager; @@ -34,6 +35,10 @@ public boolean execute(CommandSender sender, String[] args) { sendMessage(sender, ChatColor.RED, "Group doesn't exist"); return true; } + if (group.isDisciplined()) { + sendMessage(sender, ChatColor.RED, Faction.kDisciplineMsg); + return true; + } String playerName = sender.getName(); if(group.isFounder(playerName)){ sendMessage(sender, ChatColor.RED, "You are already owner of the group %s", groupName); @@ -58,7 +63,11 @@ public boolean execute(CommandSender sender, String[] args) { sendMessage(sender, ChatColor.RED, "Incorrect password"); return true; } - groupManager.addMemberToGroup(groupName, playerName); + Player player = null; + if (sender instanceof Player) { + player = (Player)sender; + } + groupManager.addMemberToGroup(groupName, playerName, player); sendMessage(sender, ChatColor.GREEN, "You have joined %s", groupName); MemberManager memberManager = Citadel.getMemberManager(); String founderName = group.getFounder(); diff --git a/src/com/untamedears/citadel/command/commands/LeaveCommand.java b/src/com/untamedears/citadel/command/commands/LeaveCommand.java index 2e2c771e..c32333f3 100644 --- a/src/com/untamedears/citadel/command/commands/LeaveCommand.java +++ b/src/com/untamedears/citadel/command/commands/LeaveCommand.java @@ -35,6 +35,10 @@ public boolean execute(CommandSender sender, String[] args) { sendMessage(sender, ChatColor.RED, "Group doesn't exist"); return true; } + if (group.isDisciplined()) { + sendMessage(sender, ChatColor.RED, Faction.kDisciplineMsg); + return true; + } String playerName = sender.getName(); if(group.isFounder(playerName)){ sendMessage(sender, ChatColor.RED, "You are the owner. If you wish to leave you must either delete or transfer the group"); @@ -48,11 +52,15 @@ public boolean execute(CommandSender sender, String[] args) { sendMessage(sender, ChatColor.RED, "You are not a member of %s", group.getName()); return true; } + Player player = null; + if (sender instanceof Player) { + player = (Player)sender; + } if(group.isModerator(playerName)){ - groupManager.removeModeratorFromGroup(groupName, playerName); + groupManager.removeModeratorFromGroup(groupName, playerName, player); } if(group.isMember(playerName)){ - groupManager.removeMemberFromGroup(groupName, playerName); + groupManager.removeMemberFromGroup(groupName, playerName, player); } sendMessage(sender, ChatColor.GREEN, "You have left the group %s", group.getName()); MemberManager memberManager = Citadel.getMemberManager(); diff --git a/src/com/untamedears/citadel/command/commands/MatureCommand.java b/src/com/untamedears/citadel/command/commands/MatureCommand.java new file mode 100644 index 00000000..546238e8 --- /dev/null +++ b/src/com/untamedears/citadel/command/commands/MatureCommand.java @@ -0,0 +1,56 @@ +package com.untamedears.citadel.command.commands; + +import static com.untamedears.citadel.Utility.timeUntilMature; + +import java.util.List; + +import org.bukkit.GameMode; +import org.bukkit.block.Block; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import com.untamedears.citadel.Citadel; +import com.untamedears.citadel.command.PlayerCommand; +import com.untamedears.citadel.entity.IReinforcement; +import com.untamedears.citadel.entity.PlayerReinforcement; + +public class MatureCommand extends PlayerCommand { + + public MatureCommand() { + super("Insta-mature"); + setDescription("Instantly mature a reinforcement"); + setUsage("/ctmature"); + setArgumentRange(0, 0); + setIdentifiers(new String[] {"ctmature"}); + } + + public boolean execute(CommandSender sender, String[] args) { + if (!(sender instanceof Player)) { + sender.sendMessage("Player only command"); + return true; + } + Player player = (Player)sender; + if (player.getGameMode() != GameMode.CREATIVE) { + sender.sendMessage("Access denied"); + return true; + } + String playerName = player.getName(); + List lastTwo = player.getLastTwoTargetBlocks(null, 64); + for (Block block : lastTwo) { + IReinforcement rein = Citadel.getReinforcementManager().getReinforcement(block); + if (!(rein instanceof PlayerReinforcement)) { + continue; + } + if (timeUntilMature(rein) <= 0) { + continue; + } + PlayerReinforcement pr = (PlayerReinforcement)rein; + pr.setMaturationTime(0); + Citadel.getReinforcementManager().addReinforcement(pr); + sender.sendMessage("Reinforcement matured"); + return true; + } + sender.sendMessage("No immature reinforcements under cursor"); + return true; + } +} diff --git a/src/com/untamedears/citadel/command/commands/MembersCommand.java b/src/com/untamedears/citadel/command/commands/MembersCommand.java index 9b824c70..3ea3f791 100644 --- a/src/com/untamedears/citadel/command/commands/MembersCommand.java +++ b/src/com/untamedears/citadel/command/commands/MembersCommand.java @@ -41,6 +41,10 @@ public boolean execute(CommandSender sender, String[] args) { sendMessage(sender, ChatColor.RED, "Group doesn't exist"); return true; } + if (group.isDisciplined()) { + sendMessage(sender, ChatColor.RED, Faction.kDisciplineMsg); + return true; + } String senderName = sender.getName(); if(!group.isFounder(senderName) && !group.isModerator(senderName)){ sendMessage(sender, ChatColor.RED, "Invalid permission to access this group"); diff --git a/src/com/untamedears/citadel/command/commands/ModeratorsCommand.java b/src/com/untamedears/citadel/command/commands/ModeratorsCommand.java index 7e24760e..da71b19d 100644 --- a/src/com/untamedears/citadel/command/commands/ModeratorsCommand.java +++ b/src/com/untamedears/citadel/command/commands/ModeratorsCommand.java @@ -37,6 +37,10 @@ public boolean execute(CommandSender sender, String[] args) { sendMessage(sender, ChatColor.RED, "Group doesn't exist"); return true; } + if (group.isDisciplined()) { + sendMessage(sender, ChatColor.RED, Faction.kDisciplineMsg); + return true; + } String senderName = sender.getName(); if(!group.isFounder(senderName) && !group.isModerator(senderName)){ sendMessage(sender, ChatColor.RED, "Invalid permission to access this group"); diff --git a/src/com/untamedears/citadel/command/commands/PasswordCommand.java b/src/com/untamedears/citadel/command/commands/PasswordCommand.java index f4b1b11e..9768abc2 100644 --- a/src/com/untamedears/citadel/command/commands/PasswordCommand.java +++ b/src/com/untamedears/citadel/command/commands/PasswordCommand.java @@ -4,6 +4,7 @@ import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; import com.untamedears.citadel.Citadel; import com.untamedears.citadel.GroupManager; @@ -33,6 +34,10 @@ public boolean execute(CommandSender sender, String[] args) { sendMessage(sender, ChatColor.RED, "Group doesn't exist"); return true; } + if (group.isDisciplined()) { + sendMessage(sender, ChatColor.RED, Faction.kDisciplineMsg); + return true; + } String playerName = sender.getName(); if(!group.isFounder(playerName)){ sendMessage(sender, ChatColor.RED, "Invalid permission to modify this group"); @@ -44,7 +49,11 @@ public boolean execute(CommandSender sender, String[] args) { return true; } group.setPassword(password); - groupManager.addGroup(group); + Player player = null; + if (sender instanceof Player) { + player = (Player)sender; + } + groupManager.addGroup(group, player); sendMessage(sender, ChatColor.GREEN, "Changed password for %s to \"%s\"", groupName, password); return true; } diff --git a/src/com/untamedears/citadel/command/commands/PlayerStatsCommand.java b/src/com/untamedears/citadel/command/commands/PlayerStatsCommand.java index 6c9b49e7..25563b56 100644 --- a/src/com/untamedears/citadel/command/commands/PlayerStatsCommand.java +++ b/src/com/untamedears/citadel/command/commands/PlayerStatsCommand.java @@ -1,53 +1,85 @@ package com.untamedears.citadel.command.commands; +import java.util.LinkedList; +import java.util.List; import java.util.Set; +import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import com.untamedears.citadel.Citadel; import com.untamedears.citadel.GroupManager; import com.untamedears.citadel.command.CommandUtils; -import com.untamedears.citadel.command.ConsoleCommand; +import com.untamedears.citadel.command.PlayerCommand; import com.untamedears.citadel.entity.Faction; import com.untamedears.citadel.entity.Member; -public class PlayerStatsCommand extends ConsoleCommand { - - public PlayerStatsCommand() { - super("View Player Stats"); - setDescription("View citadel player stats"); - setUsage("/ctgstats "); - setArgumentRange(1, 1); - setIdentifiers(new String[] {"ctpstats", "ctpst"}); - } - - public boolean execute(CommandSender sender, String[] args) { - GroupManager groupManager = Citadel.getGroupManager(); - Set memberGroups = groupManager.getGroupsByMember(args[0]); - Set moderatorGroups = groupManager.getGroupsByModerator(args[0]); - Set founderGroups = groupManager.getGroupsByFounder(args[0]); - sender.sendMessage("Player name: "+args[0]); - if (founderGroups.size() > 0) - sender.sendMessage("Admin of groups: "+CommandUtils.joinFactionSet(founderGroups)); - if (moderatorGroups.size() > 0) - sender.sendMessage("Moderator of groups: "+CommandUtils.joinFactionSet(moderatorGroups)); - if (memberGroups.size() > 0) - sender.sendMessage("Member of groups: "+CommandUtils.joinFactionSet(memberGroups)); - - Faction group = null; - Member member = Citadel.getMemberManager().getMember(args[0]); - if (member != null) { - group = member.getPersonalGroup(); - } - if (group != null) { - String personalGroupName = group.getName(); - sender.sendMessage("Personal group reinforcements: "); - CommandUtils.printReinforcements(sender, args[0], CommandUtils.countReinforcements(personalGroupName)); - } else { - sender.sendMessage("Player has no personal group."); - } - - return false; - } +public class PlayerStatsCommand extends PlayerCommand { + + public class SendResultsTask implements Runnable { + public QueryDbTask previousTask; + public SendResultsTask(QueryDbTask pt) { + previousTask = pt; + } + @Override + public void run() { + for (String line : previousTask.results) { + previousTask.sender.sendMessage(line); + } + } + } + + public class QueryDbTask implements Runnable { + public CommandSender sender; + public String playerName; + public List results = new LinkedList(); + public QueryDbTask(CommandSender s, String pn) { + sender = s; + playerName = pn; + } + @Override + public void run() { + GroupManager groupManager = Citadel.getGroupManager(); + Set memberGroups = groupManager.getGroupsByMember(playerName); + Set moderatorGroups = groupManager.getGroupsByModerator(playerName); + Set founderGroups = groupManager.getGroupsByFounder(playerName); + results.add("Player name: "+playerName); + if (founderGroups.size() > 0) + results.add("Admin of groups: "+CommandUtils.joinFactionSet(founderGroups)); + if (moderatorGroups.size() > 0) + results.add("Moderator of groups: "+CommandUtils.joinFactionSet(moderatorGroups)); + if (memberGroups.size() > 0) + results.add("Member of groups: "+CommandUtils.joinFactionSet(memberGroups)); + + Faction group = null; + Member member = Citadel.getMemberManager().getMember(playerName); + if (member != null) { + group = member.getPersonalGroup(); + } + if (group != null) { + String personalGroupName = group.getName(); + results.add("Personal group reinforcements: "); + CommandUtils.formatReinforcements(results, playerName, CommandUtils.countReinforcements(personalGroupName)); + } else { + results.add("Player has no personal group."); + } + Bukkit.getScheduler().runTask( + Citadel.getPlugin(), new SendResultsTask(this)); + } + } + + public PlayerStatsCommand() { + super("View Player Stats"); + setDescription("View citadel player stats"); + setUsage("/ctpstats "); + setArgumentRange(1, 1); + setIdentifiers(new String[] {"ctpstats", "ctpst"}); + } + + public boolean execute(CommandSender sender, String[] args) { + Bukkit.getScheduler().runTaskAsynchronously( + Citadel.getPlugin(), new QueryDbTask(sender, args[0])); + return true; + } } diff --git a/src/com/untamedears/citadel/command/commands/ReinforceCommand.java b/src/com/untamedears/citadel/command/commands/ReinforceCommand.java index 7871626a..a2568019 100644 --- a/src/com/untamedears/citadel/command/commands/ReinforceCommand.java +++ b/src/com/untamedears/citadel/command/commands/ReinforceCommand.java @@ -55,6 +55,10 @@ public boolean execute(CommandSender sender, String[] args) { sendMessage(sender, ChatColor.RED, "Group doesn't exist"); return true; } + if (group.isDisciplined()) { + sendMessage(sender, ChatColor.RED, Faction.kDisciplineMsg); + return true; + } String senderName = sender.getName(); if(!group.isFounder(senderName) && !group.isModerator(senderName)){ sendMessage(sender, ChatColor.RED, "Invalid permission to use this group"); diff --git a/src/com/untamedears/citadel/command/commands/RemoveModCommand.java b/src/com/untamedears/citadel/command/commands/RemoveModCommand.java index f48ff6da..1bd2190f 100644 --- a/src/com/untamedears/citadel/command/commands/RemoveModCommand.java +++ b/src/com/untamedears/citadel/command/commands/RemoveModCommand.java @@ -4,6 +4,7 @@ import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; import com.untamedears.citadel.Citadel; import com.untamedears.citadel.GroupManager; @@ -33,6 +34,10 @@ public boolean execute(CommandSender sender, String[] args) { sendMessage(sender, ChatColor.RED, "Group doesn't exist"); return true; } + if (group.isDisciplined()) { + sendMessage(sender, ChatColor.RED, Faction.kDisciplineMsg); + return true; + } String senderName = sender.getName(); if(!group.isFounder(senderName)){ sendMessage(sender, ChatColor.RED, "Invalid permission to modify this group"); @@ -43,9 +48,13 @@ public boolean execute(CommandSender sender, String[] args) { sendMessage(sender, ChatColor.RED, "%s is not a moderator of %s", targetName, groupName); return true; } - groupManager.removeModeratorFromGroup(groupName, targetName); + Player player = null; + if (sender instanceof Player) { + player = (Player)sender; + } + groupManager.removeModeratorFromGroup(groupName, targetName, player); if(!group.isMember(targetName)){ - groupManager.addMemberToGroup(groupName, targetName); + groupManager.addMemberToGroup(groupName, targetName, player); } sendMessage(sender, ChatColor.GREEN, "%s has been removed as moderator from %s and demoted to a member. Use /ctdisallow to remove as member", targetName, groupName); return true; diff --git a/src/com/untamedears/citadel/command/commands/ReprieveCommand.java b/src/com/untamedears/citadel/command/commands/ReprieveCommand.java new file mode 100644 index 00000000..48c6030a --- /dev/null +++ b/src/com/untamedears/citadel/command/commands/ReprieveCommand.java @@ -0,0 +1,49 @@ +package com.untamedears.citadel.command.commands; + +import static com.untamedears.citadel.Utility.sendMessage; + +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import com.untamedears.citadel.Citadel; +import com.untamedears.citadel.GroupManager; +import com.untamedears.citadel.command.PlayerCommand; +import com.untamedears.citadel.entity.Faction; + +public class ReprieveCommand extends PlayerCommand { + + public ReprieveCommand() { + super("Reprieve Group"); + setDescription("Reprieves a group"); + setUsage("/ctreprieve §8 "); + setArgumentRange(1,1); + setIdentifiers(new String[] {"ctarg"}); + } + + public boolean execute(CommandSender sender, String[] args) { + String group_name = args[0]; + GroupManager group_manager = Citadel.getGroupManager(); + Faction group = group_manager.getGroup(group_name); + if(group == null){ + sendMessage(sender, ChatColor.RED, "Group doesn't exist"); + return true; + } + if (group.isDeleted()) { + sendMessage(sender, ChatColor.RED, "Group is deleted, sorry"); + return true; + } + if (!group.isDisabled()) { + sendMessage(sender, ChatColor.YELLOW, "Group is not disabled"); + return true; + } + Player player = null; + if (sender instanceof Player) { + player = (Player)sender; + } + group.setDisabled(false); + sendMessage(sender, ChatColor.GREEN, "Group %s is enabled", group_name); + group_manager.addGroup(group, player); + return true; + } +} diff --git a/src/com/untamedears/citadel/command/commands/StatsCommand.java b/src/com/untamedears/citadel/command/commands/StatsCommand.java index 3fe3d28c..f0c39b7f 100644 --- a/src/com/untamedears/citadel/command/commands/StatsCommand.java +++ b/src/com/untamedears/citadel/command/commands/StatsCommand.java @@ -1,5 +1,6 @@ package com.untamedears.citadel.command.commands; +import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import com.untamedears.citadel.Citadel; @@ -14,6 +15,40 @@ */ public class StatsCommand extends PlayerCommand { + public class SendResultsTask implements Runnable { + public QueryDbTask previousTask; + public SendResultsTask(QueryDbTask pt) { + previousTask = pt; + } + @Override + public void run() { + previousTask.sender.sendMessage( + new StringBuilder().append("§cTotal Reinforcements:§e " ).append(previousTask.numReinforcements).toString()); + previousTask.sender.sendMessage( + new StringBuilder().append("§cTotal Groups:§e " ).append(previousTask.numGroups).toString()); + } + } + + public class QueryDbTask implements Runnable { + public CommandSender sender; + public int numReinforcements; + public int numGroups; + public QueryDbTask(CommandSender s) { + sender = s; + } + @Override + public void run() { + ReinforcementManager reinforcementManager = Citadel.getReinforcementManager(); + numReinforcements = reinforcementManager.getReinforcementsAmount(); + + GroupManager groupManager = Citadel.getGroupManager(); + numGroups = groupManager.getGroupsAmount(); + + Bukkit.getScheduler().runTask( + Citadel.getPlugin(), new SendResultsTask(this)); + } + } + public StatsCommand() { super("View Stats"); setDescription("View citadel stats"); @@ -22,14 +57,8 @@ public StatsCommand() { } public boolean execute(CommandSender sender, String[] args) { - ReinforcementManager reinforcementManager = Citadel.getReinforcementManager(); - int numReinforcements = reinforcementManager.getReinforcementsAmount(); - - GroupManager groupManager = Citadel.getGroupManager(); - int numGroups = groupManager.getGroupsAmount(); - - sender.sendMessage(new StringBuilder().append("§cTotal Reinforcements:§e " ).append(numReinforcements).toString()); - sender.sendMessage(new StringBuilder().append("§cTotal Groups:§e " ).append(numGroups).toString()); + Bukkit.getScheduler().runTaskAsynchronously( + Citadel.getPlugin(), new QueryDbTask(sender)); return true; } diff --git a/src/com/untamedears/citadel/command/commands/TransferCommand.java b/src/com/untamedears/citadel/command/commands/TransferCommand.java index ea2b0440..4534e1fe 100644 --- a/src/com/untamedears/citadel/command/commands/TransferCommand.java +++ b/src/com/untamedears/citadel/command/commands/TransferCommand.java @@ -4,6 +4,7 @@ import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; import com.untamedears.citadel.Citadel; import com.untamedears.citadel.GroupManager; @@ -35,8 +36,13 @@ public boolean execute(CommandSender sender, String[] args) { sendMessage(sender, ChatColor.RED, "Group doesn't exist"); return true; } + boolean admin_mode = sender.hasPermission("citadel.admin.cttransfer"); + if (group.isDisciplined() && !admin_mode) { + sendMessage(sender, ChatColor.RED, Faction.kDisciplineMsg); + return true; + } String senderName = sender.getName(); - if(!group.isFounder(senderName)){ + if(!group.isFounder(senderName) && !admin_mode){ sendMessage(sender, ChatColor.RED, "Invalid permission to transfer this group"); return true; } @@ -45,33 +51,37 @@ public boolean execute(CommandSender sender, String[] args) { return true; } String targetName = args[1]; - if(senderName.equalsIgnoreCase(targetName)){ + if(senderName.equalsIgnoreCase(targetName) && !admin_mode){ sendMessage(sender, ChatColor.RED, "You already own this group"); return true; } int groupsAllowed = Citadel.getConfigManager().getGroupsAllowed(); - if(groupManager.getPlayerGroupsAmount(targetName) >= groupsAllowed){ + if(groupManager.getPlayerGroupsAmount(targetName) >= groupsAllowed && !admin_mode){ sendMessage(sender, ChatColor.RED, "This player has already reached the maximum amount of groups allowed"); return true; } MemberManager memberManager = Citadel.getMemberManager(); - if(!memberManager.isOnline(targetName)){ + if(!memberManager.isOnline(targetName) && !admin_mode){ sendMessage(sender, ChatColor.RED, "User must be online"); return true; } + Player player = null; + if (sender instanceof Player) { + player = (Player)sender; + } Member member = memberManager.getMember(targetName); if(member == null){ member = new Member(targetName); memberManager.addMember(member); } if(group.isMember(targetName)){ - groupManager.removeMemberFromGroup(groupName, targetName); + groupManager.removeMemberFromGroup(groupName, targetName, player); } if(group.isModerator(targetName)){ - groupManager.removeModeratorFromGroup(groupName, targetName); + groupManager.removeModeratorFromGroup(groupName, targetName, player); } group.setFounder(targetName); - groupManager.addGroup(group); + groupManager.addGroup(group, player); sendMessage(sender, ChatColor.GREEN, "You have transferred %s to %s", groupName, targetName); if(memberManager.isOnline(targetName)){ sendMessage(memberManager.getOnlinePlayer(targetName), ChatColor.YELLOW, "%s has transferred the group %s to you", diff --git a/src/com/untamedears/citadel/dao/CitadelCachingDao.java b/src/com/untamedears/citadel/dao/CitadelCachingDao.java index 7bd45e2d..38872d1b 100644 --- a/src/com/untamedears/citadel/dao/CitadelCachingDao.java +++ b/src/com/untamedears/citadel/dao/CitadelCachingDao.java @@ -163,6 +163,30 @@ public void ForceCacheFlush(int flushCount) { } } + public boolean ForceChunkFlush(String chunk_id) { + ChunkCache cache = cachesByChunkId.get(chunk_id); + if (cache == null) { + return false; + } + if (cache.getTotalPendingCount() > 0) { + cache.flush(); + } + return true; + } + + public boolean ForceChunkUnload(String chunk_id) { + ChunkCache cache = cachesByChunkId.get(chunk_id); + if (cache == null) { + return false; + } + if (cache.getTotalPendingCount() > 0) { + cache.flush(); + } + cachesByChunkId.remove(chunk_id); + cachesByTime.remove(cache); + return true; + } + @Override public IReinforcement findReinforcement( Location location ){ return findReinforcement(location.getBlock()); @@ -530,5 +554,7 @@ public int compareTo(ChunkCache that) { } } - private class RefuseToPreventThrashingException extends Exception {} + private class RefuseToPreventThrashingException extends Exception { + private static final long serialVersionUID = 763853564799286588L; + } } diff --git a/src/com/untamedears/citadel/dao/CitadelDao.java b/src/com/untamedears/citadel/dao/CitadelDao.java index 62bdaedd..c9cea452 100644 --- a/src/com/untamedears/citadel/dao/CitadelDao.java +++ b/src/com/untamedears/citadel/dao/CitadelDao.java @@ -1,12 +1,13 @@ package com.untamedears.citadel.dao; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Date; import java.util.List; import java.util.Set; import java.util.TreeSet; - import javax.persistence.PersistenceException; import org.bukkit.Chunk; @@ -16,13 +17,20 @@ import org.bukkit.plugin.java.JavaPlugin; import com.avaje.ebean.LogLevel; +import com.avaje.ebean.Query; +import com.avaje.ebean.RawSql; +import com.avaje.ebean.RawSqlBuilder; import com.avaje.ebean.SqlRow; import com.avaje.ebean.SqlUpdate; import com.avaje.ebean.config.DataSourceConfig; import com.avaje.ebean.config.ServerConfig; import com.lennardf1989.bukkitex.MyDatabase; + +import com.untamedears.citadel.Citadel; import com.untamedears.citadel.DbUpdateAction; +import com.untamedears.citadel.entity.DbVersion; import com.untamedears.citadel.entity.Faction; +import com.untamedears.citadel.entity.FactionDelete; import com.untamedears.citadel.entity.FactionMember; import com.untamedears.citadel.entity.Member; import com.untamedears.citadel.entity.Moderator; @@ -74,6 +82,7 @@ public CitadelDao(JavaPlugin plugin) { @Override protected List> getDatabaseClasses() { return Arrays.asList( + DbVersion.class, FactionDelete.class, Faction.class, Member.class, FactionMember.class, PlayerReinforcement.class, ReinforcementKey.class, PersonalGroup.class, Moderator.class); @@ -286,56 +295,233 @@ public void removeAllModeratorsFromGroup(String groupName){ .setParameter("groupName", groupName); getDatabase().execute(update); } + - public void updateDatabase(){ - //this for when Citadel 2.0 is loaded after an older version of Citadel was previously installed - SqlUpdate createMemberTable = getDatabase().createSqlUpdate - ("CREATE TABLE IF NOT EXISTS member (member_name varchar(255) NOT NULL, PRIMARY KEY (member_name))"); - getDatabase().execute(createMemberTable); - - SqlUpdate createModeratorTable = getDatabase().createSqlUpdate - ("CREATE TABLE IF NOT EXISTS moderator (member_name varchar(255) NOT NULL, faction_name varchar(255) NOT NULL)"); - getDatabase().execute(createModeratorTable); - - SqlUpdate createPersonalGroupTable = getDatabase().createSqlUpdate - ("CREATE TABLE IF NOT EXISTS personal_group (group_name varchar(255) NOT NULL, owner_name varchar(255) NOT NULL)"); - getDatabase().execute(createPersonalGroupTable); - - try { - SqlUpdate alterFactionAddPassword = getDatabase().createSqlUpdate - ("ALTER TABLE faction ADD password varchar(255) DEFAULT NULL"); - getDatabase().execute(alterFactionAddPassword); - } catch(PersistenceException e){ - //column already exists - } + public Set loadFactionDeletions() { + return getDatabase() + .createQuery(FactionDelete.class, "find faction_delete") + .findSet(); + } + + + /** + * @author GFQ + * @date 4/25/2014 + * + * @brief Performs batch reinforcement updates for deleted factions + * + * When a faction is deleted, there could potentially be millions of reinforcement records + * that would need to be updated with a new group name. This is handled by adding it + * to a separate faction_delete table, and setting the delete flag for that faction. + * This batch method is then run on plugin startup. + * + * This function gets a set of all factions that are marked as deleted and performs + * a series of reinforcement update queries. If the group no longer has any existing + * reinforcement records then the group is deleted. If the total batch time exceeds + * the given time limit then the loop exits and more work will be done on the next + * restart. + */ + public void batchRemoveDeletedGroups() { + + final int BATCH_UPDATE_SIZE = Citadel.getConfigManager().getBatchUpdateSize(); + final int BATCH_TIMEOUT_MS = Citadel.getConfigManager().getBatchUpdateTimeoutMs(); + + // Mark the start time + long startTime = System.currentTimeMillis(); + + // Get all the groups that are in the faction_delete table and marked as delete in the main faction able + String joinQuery = String.format("select * from faction_delete left join (faction) " + + "on (faction.name = faction_delete.deleted_faction and faction.discipline_flags & %d = %d)", Faction.kDeletedFlag, Faction.kDeletedFlag); + Set groups = getDatabase().createSqlQuery(joinQuery).findSet(); + + for (SqlRow groupRow : groups) { + String groupName = groupRow.getString("deleted_faction"); + String personalGroup = groupRow.getString("personal_group"); + int recordsLeft = 1; + + // Do batch deletes in groups of BATCH_UPDATE_SIZE while there are records remaining and we're inside our time limit + while (recordsLeft > 0 && System.currentTimeMillis() - startTime <= BATCH_TIMEOUT_MS) { + // Get how many records still need to be updated from the deleted group name to the private group name + SqlRow row = getDatabase().createSqlQuery("select count(*) as count from reinforcement where name = :name") + .setParameter("name", groupName) + .findUnique(); + recordsLeft = row.getInteger("count"); + + getDatabase().beginTransaction(); + + if (recordsLeft > 0) { + getDatabase().createSqlUpdate("update reinforcement set name = :newName where name = :oldName limit :limit") + .setParameter("newName", personalGroup) + .setParameter("oldName", groupName) + .setParameter("limit", BATCH_UPDATE_SIZE) + .execute(); + } else { + // No more records to update, now we can safely delete the group + getDatabase().createSqlUpdate("delete from faction_delete where deleted_faction = :name") + .setParameter("name",groupName) + .execute(); + + getDatabase().createSqlUpdate("delete from faction where name = :name") + .setParameter("name", groupName) + .execute(); + } + + getDatabase().commitTransaction(); + } + } + } + public void updateDatabase() { + RawSql rawVersionQuery = RawSqlBuilder + .parse("SELECT MAX(db_version) AS db_version FROM db_version") + .columnMapping("MAX(db_version)", "dbVersion") + .create(); + Query dbVersionQuery = getDatabase().find(DbVersion.class); + dbVersionQuery.setRawSql(rawVersionQuery); + DbVersion dbVersion = null; try { - SqlUpdate addReinforcementVersion = getDatabase().createSqlUpdate( - "ALTER TABLE reinforcement ADD COLUMN version INT NOT NULL DEFAULT 0"); - getDatabase().execute(addReinforcementVersion); - } catch(PersistenceException e){ - //column already exists + dbVersion = dbVersionQuery.findUnique(); + } catch (PersistenceException ex) { + // table doesn't exist + } + if (dbVersion != null) { + // The previous query didn't actually grab the entire object due + // to the aggregation so retrieve the real object now. + dbVersion = getDatabase().createQuery( + DbVersion.class, + "find db_version where db_version = :ver") + .setParameter("ver", dbVersion.getDbVersion()) + .findUnique(); } - try { - // The initial add column statement is our indicator if the DB - // needs this reconstruction. - SqlUpdate addReinforcementChunkId = getDatabase().createSqlUpdate( - "ALTER TABLE reinforcement ADD COLUMN chunk_id VARCHAR(255)"); - getDatabase().execute(addReinforcementChunkId); - - addReinforcementChunkId = getDatabase().createSqlUpdate( - "UPDATE reinforcement SET chunk_id = " + - "CONCAT(world, ':', CONVERT(IF(x >= 0, x, x - 15) DIV 16, CHAR), ':'," + - "CONVERT(IF(z >= 0, z, z - 15) DIV 16, CHAR))"); - getDatabase().execute(addReinforcementChunkId); - - addReinforcementChunkId = getDatabase().createSqlUpdate( - "ALTER TABLE reinforcement ADD INDEX ix_chunk_id (chunk_id)"); - getDatabase().execute(addReinforcementChunkId); - } catch(PersistenceException e){ - //column already exists + if (dbVersion == null) { + Citadel.info("Updating to DB v2"); + //this for when Citadel 2.0 is loaded after an older version of Citadel + //was previously installed + SqlUpdate createMemberTable = getDatabase().createSqlUpdate( + "CREATE TABLE IF NOT EXISTS member " + + "(member_name varchar(255) NOT NULL, PRIMARY KEY (member_name))"); + getDatabase().execute(createMemberTable); + + SqlUpdate createModeratorTable = getDatabase().createSqlUpdate( + "CREATE TABLE IF NOT EXISTS moderator " + + "(member_name varchar(255) NOT NULL, faction_name varchar(255) NOT NULL)"); + getDatabase().execute(createModeratorTable); + + SqlUpdate createPersonalGroupTable = getDatabase().createSqlUpdate( + "CREATE TABLE IF NOT EXISTS personal_group " + + "(group_name varchar(255) NOT NULL, owner_name varchar(255) NOT NULL)"); + getDatabase().execute(createPersonalGroupTable); + + try { + SqlUpdate alterFactionAddPassword = getDatabase().createSqlUpdate + ("ALTER TABLE faction ADD password varchar(255) DEFAULT NULL"); + getDatabase().execute(alterFactionAddPassword); + } catch(PersistenceException e){ + //column already exists + } + + try { + SqlUpdate addReinforcementVersion = getDatabase().createSqlUpdate( + "ALTER TABLE reinforcement ADD COLUMN version INT NOT NULL DEFAULT 0"); + getDatabase().execute(addReinforcementVersion); + } catch(PersistenceException e){ + //column already exists + } + + try { + // The initial add column statement is our indicator if the DB + // needs this reconstruction. + SqlUpdate addReinforcementChunkId = getDatabase().createSqlUpdate( + "ALTER TABLE reinforcement ADD COLUMN chunk_id VARCHAR(255)"); + getDatabase().execute(addReinforcementChunkId); + + addReinforcementChunkId = getDatabase().createSqlUpdate( + "UPDATE reinforcement SET chunk_id = " + + "CONCAT(world, ':', CONVERT(IF(x >= 0, x, x - 15) DIV 16, CHAR), ':'," + + "CONVERT(IF(z >= 0, z, z - 15) DIV 16, CHAR))"); + getDatabase().execute(addReinforcementChunkId); + + addReinforcementChunkId = getDatabase().createSqlUpdate( + "ALTER TABLE reinforcement ADD INDEX ix_chunk_id (chunk_id)"); + getDatabase().execute(addReinforcementChunkId); + } catch(PersistenceException e){ + //column already exists + } + + try { + SqlUpdate addFactionDisabled = getDatabase().createSqlUpdate( + "ALTER TABLE faction ADD COLUMN discipline_flags TINYINT NOT NULL DEFAULT 0"); + getDatabase().execute(addFactionDisabled); + } catch(PersistenceException e){ + //column already exists + } + + try { + SqlUpdate addChunkidIdx = getDatabase().createSqlUpdate( + "ALTER TABLE reinforcement ADD INDEX idx_reinforcement_chunkid (chunk_id)"); + getDatabase().execute(addChunkidIdx); + } catch(PersistenceException e){ + //index already exists + } + + try { + SqlUpdate addReinforcementVersion = getDatabase().createSqlUpdate( + "ALTER TABLE faction ADD COLUMN version INT NOT NULL DEFAULT 0"); + getDatabase().execute(addReinforcementVersion); + } catch(PersistenceException e){ + //column already exists + } + + try { + SqlUpdate addReinforcementMaturationTime = getDatabase().createSqlUpdate( + "ALTER TABLE reinforcement ADD COLUMN maturation_time INT NOT NULL DEFAULT 0"); + getDatabase().execute(addReinforcementMaturationTime); + } catch(PersistenceException e){ + //column already exists + } + + try { + SqlUpdate addReinforcementInsecurity = getDatabase().createSqlUpdate( + "ALTER TABLE reinforcement ADD COLUMN insecure BIT NOT NULL DEFAULT 0"); + getDatabase().execute(addReinforcementInsecurity); + } catch(PersistenceException e){ + //column already exists + } + + SqlUpdate createVersionTable = getDatabase().createSqlUpdate( + "CREATE TABLE IF NOT EXISTS db_version " + + "(db_version INT NOT NULL, update_time varchar(24), " + + "PRIMARY KEY (db_version))"); + getDatabase().execute(createVersionTable); + + // The version table is empty, create a new object just for + // passing to advance for boot strapping. + dbVersion = new DbVersion(); + dbVersion.setDbVersion(1); + dbVersion = advanceDbVersion(dbVersion); } + + if (dbVersion.getDbVersion() == 2) { + Citadel.info("Updating to DB v3"); + + SqlUpdate createTable = getDatabase().createSqlUpdate( + "CREATE TABLE IF NOT EXISTS faction_delete " + + "(deleted_faction VARCHAR(255) NOT NULL, personal_group VARCHAR(255), " + + "PRIMARY KEY (deleted_faction))"); + getDatabase().execute(createTable); + + dbVersion = advanceDbVersion(dbVersion); + } + } + + protected DbVersion advanceDbVersion(DbVersion currentVersion) { + DbVersion newVersion = new DbVersion(); + newVersion.setDbVersion(currentVersion.getDbVersion() + 1); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + newVersion.setUpdateTime(sdf.format(new Date())); + getDatabase().save(newVersion); + return newVersion; } protected void prepareDatabaseAdditionalConfig(DataSourceConfig dataSourceConfig, ServerConfig serverConfig) { diff --git a/src/com/untamedears/citadel/entity/DbVersion.java b/src/com/untamedears/citadel/entity/DbVersion.java new file mode 100644 index 00000000..db4e7412 --- /dev/null +++ b/src/com/untamedears/citadel/entity/DbVersion.java @@ -0,0 +1,37 @@ +package com.untamedears.citadel.entity; + +import java.io.Serializable; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; + +@Entity(name = "db_version") +public class DbVersion implements Serializable { + private static final long serialVersionUID = 740812643372L; + + // DB Columns + @Id + @Column(name="db_version") + private Integer dbVersion; + + private String updateTime; + + + public DbVersion() {} + + public Integer getDbVersion() { + return this.dbVersion; + } + + public void setDbVersion(Integer val) { + this.dbVersion = val; + } + + public String getUpdateTime() { + return this.updateTime; + } + + public void setUpdateTime(String val) { + this.updateTime = val; + } +} diff --git a/src/com/untamedears/citadel/entity/Faction.java b/src/com/untamedears/citadel/entity/Faction.java index be61a703..26b19f13 100644 --- a/src/com/untamedears/citadel/entity/Faction.java +++ b/src/com/untamedears/citadel/entity/Faction.java @@ -2,8 +2,11 @@ import java.io.Serializable; +import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; +import javax.persistence.Transient; +import javax.persistence.Version; import com.untamedears.citadel.Citadel; @@ -14,19 +17,49 @@ * Time: 1:14 AM */ @Entity -public class Faction implements Serializable { +public class Faction implements Serializable, Comparable { + + private static final long serialVersionUID = -1660123901051487634L; + public static final byte kDisabledFlag = 0x01; + public static final byte kDeletedFlag = 0x02; + public static final byte kFlagMask = kDisabledFlag | kDeletedFlag; + public static final String kDisciplineMsg = "The group is under administrative discipline"; - private static final long serialVersionUID = -1660849671051487634L; - @Id private String name; + @Transient private String normalized_name; private String founder; private String password; - public Faction() {} + @Version + @Column(name="version") + private int dbRowVersion; // Do not touch + + @Column(name="discipline_flags", nullable=false) + private Integer disciplineFlags; + + public Faction() { + this.name = ""; + this.normalized_name = ""; + this.founder = ""; + this.disciplineFlags = 0; + } public Faction(String name, String founder) { this.name = name; + this.normalized_name = name.toLowerCase(); this.founder = founder; + this.disciplineFlags = 0; + } + + // Do not touch + public int getDbRowVersion() { return this.dbRowVersion; } + public void setDbRowVersion(int value) { this.dbRowVersion = value; } + // Do not touch + + public void Copy(Faction other) { + this.setFounder(other.getFounder()); + this.setPassword(other.getPassword()); + this.setDisciplineFlags(other.getDisciplineFlags()); } public String getName() { @@ -35,6 +68,11 @@ public String getName() { public void setName(String name) { this.name = name; + this.normalized_name = name.toLowerCase(); + } + + public String getNormalizedName() { + return this.normalized_name; } public String getFounder() { @@ -52,7 +90,49 @@ public String getPassword(){ public void setPassword(String password){ this.password = password; } - + + // Don't get/set this.disciplineFlags outside of these getter/setters + // even when accessing from inside the class + public Integer getDisciplineFlags() { + return this.disciplineFlags & kFlagMask; + } + + public void setDisciplineFlags(Integer flags) { + this.disciplineFlags = flags & kFlagMask; + } + + public boolean isDisabled() { + return (getDisciplineFlags() & kDisabledFlag) != 0; + } + + public void setDisabled(boolean set) { + Integer flag = getDisciplineFlags(); + if (set) { + flag |= kDisabledFlag; + } else { + flag &= ~kDisabledFlag; + } + setDisciplineFlags(flag); + } + + public boolean isDeleted() { + return (getDisciplineFlags() & kDeletedFlag) != 0; + } + + public void setDeleted(boolean set) { + Integer flag = getDisciplineFlags(); + if (set) { + flag |= kDeletedFlag; + } else { + flag &= ~kDeletedFlag; + } + setDisciplineFlags(flag); + } + + public boolean isDisciplined() { + return getDisciplineFlags() != 0; + } + public boolean isFounder(String memberName){ return isFounder(new Member(memberName)); } @@ -87,11 +167,20 @@ public boolean equals(Object o) { if (!(o instanceof Faction)) return false; Faction faction = (Faction) o; - return this.name.equalsIgnoreCase(faction.getName()); + return this.normalized_name.equals(faction.getNormalizedName()); } @Override public int hashCode() { - return name.hashCode(); + return this.normalized_name.hashCode(); + } + + @Override + public int compareTo(Object o) { + if (!(o instanceof Faction)) { + throw new ClassCastException(); + } + Faction other = (Faction)o; + return this.getNormalizedName().compareTo(other.getNormalizedName()); } } diff --git a/src/com/untamedears/citadel/entity/FactionDelete.java b/src/com/untamedears/citadel/entity/FactionDelete.java new file mode 100644 index 00000000..6e98d893 --- /dev/null +++ b/src/com/untamedears/citadel/entity/FactionDelete.java @@ -0,0 +1,38 @@ +package com.untamedears.citadel.entity; + +import java.io.Serializable; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; + +@Entity(name = "faction_delete") +public class FactionDelete implements Serializable { + private static final long serialVersionUID = 54271370768632792L; + + // DB Columns + @Id + @Column(name="deleted_faction") + private String deletedFaction; + + @Column(name="personal_group") + private String personalGroup; + + + public FactionDelete() {} + + public String getDeletedFaction() { + return this.deletedFaction; + } + + public void setDeletedFaction(String val) { + this.deletedFaction = val; + } + + public String getPersonalGroup() { + return this.personalGroup; + } + + public void setPersonalGroup(String val) { + this.personalGroup = val; + } +} diff --git a/src/com/untamedears/citadel/entity/FactionMember.java b/src/com/untamedears/citadel/entity/FactionMember.java index e3207b8c..b5abba09 100644 --- a/src/com/untamedears/citadel/entity/FactionMember.java +++ b/src/com/untamedears/citadel/entity/FactionMember.java @@ -14,7 +14,7 @@ @Entity @Table(name="faction_member", uniqueConstraints={ @UniqueConstraint(columnNames={"faction_name", "member_name"})}) -public class FactionMember { +public class FactionMember implements Comparable { @Id private String factionName; @Id private String memberName; @@ -55,4 +55,17 @@ public int hashCode() { result = 31 * result + memberName.hashCode(); return result; } + + @Override + public int compareTo(Object o) { + if (!(o instanceof FactionMember)) { + throw new ClassCastException(); + } + FactionMember other = (FactionMember)o; + int compare = this.getFactionName().compareTo(other.getFactionName()); + if (compare != 0) { + return compare; + } + return this.getMemberName().compareTo(other.getMemberName()); + } } diff --git a/src/com/untamedears/citadel/entity/IReinforcement.java b/src/com/untamedears/citadel/entity/IReinforcement.java index ad4cd8dd..4985c1dc 100644 --- a/src/com/untamedears/citadel/entity/IReinforcement.java +++ b/src/com/untamedears/citadel/entity/IReinforcement.java @@ -12,4 +12,6 @@ public abstract interface IReinforcement extends public double getHealth(); public String getHealthText(); public String getStatus(); + public int getMaturationTime(); // Minutes since the epoch (1970-Jan-1) + public void setMaturationTime(int time); } diff --git a/src/com/untamedears/citadel/entity/Moderator.java b/src/com/untamedears/citadel/entity/Moderator.java index 4ef1fc40..197da5db 100644 --- a/src/com/untamedears/citadel/entity/Moderator.java +++ b/src/com/untamedears/citadel/entity/Moderator.java @@ -14,7 +14,7 @@ @Entity @Table(name="moderator", uniqueConstraints={ @UniqueConstraint(columnNames={"faction_name", "member_name"})}) -public class Moderator { +public class Moderator implements Comparable { @Id private String memberName; @Id private String factionName; @@ -56,4 +56,17 @@ public int hashCode() { result = 31 * result + memberName.hashCode(); return result; } + + @Override + public int compareTo(Object o) { + if (!(o instanceof Moderator)) { + throw new ClassCastException(); + } + Moderator other = (Moderator)o; + int compare = this.getFactionName().compareTo(other.getFactionName()); + if (compare != 0) { + return compare; + } + return this.getMemberName().compareTo(other.getMemberName()); + } } diff --git a/src/com/untamedears/citadel/entity/NaturalReinforcement.java b/src/com/untamedears/citadel/entity/NaturalReinforcement.java index 671e14d4..f8940f07 100644 --- a/src/com/untamedears/citadel/entity/NaturalReinforcement.java +++ b/src/com/untamedears/citadel/entity/NaturalReinforcement.java @@ -66,6 +66,14 @@ public String getHealthText() { public String getStatus() { return getHealthText(); } + public int getMaturationTime() { + return 0; + } + + public void setMaturationTime(int time) { + return; + } + @Override public String toString() { return String.format("%s, durability: %d of %d", id_, durability_, max_durability_); diff --git a/src/com/untamedears/citadel/entity/PlayerReinforcement.java b/src/com/untamedears/citadel/entity/PlayerReinforcement.java index f3885e27..eb555c5d 100644 --- a/src/com/untamedears/citadel/entity/PlayerReinforcement.java +++ b/src/com/untamedears/citadel/entity/PlayerReinforcement.java @@ -2,6 +2,8 @@ import java.util.ArrayList; import java.util.List; +import java.util.HashMap; +import java.util.Map; import javax.persistence.Column; import javax.persistence.Entity; @@ -16,8 +18,8 @@ import org.bukkit.ChatColor; import org.bukkit.Material; import org.bukkit.block.Block; -import org.bukkit.block.ContainerBlock; import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; import org.bukkit.material.Openable; import com.untamedears.citadel.Citadel; @@ -36,12 +38,14 @@ public class PlayerReinforcement implements public static final List SECURABLE = new ArrayList(); public static final List NON_REINFORCEABLE = new ArrayList(); + public static final Map MATERIAL_SCALING = new HashMap(); @Id private ReinforcementKey id; private int materialId; private int durability; private SecurityLevel securityLevel; private String chunkId; + private boolean insecure; // @Transient == not persisted in the DB @Transient private DbUpdateAction dbAction; @@ -49,9 +53,11 @@ public class PlayerReinforcement implements @Column(name="version") private int dbRowVersion; // Do not touch - @ManyToOne - @JoinColumn(name = "name") - private Faction owner; + @Column(name = "name") + private String ownerName; + + @Column(name="maturation_time") + private int maturationTime; public PlayerReinforcement() { @@ -65,11 +71,25 @@ public PlayerReinforcement( SecurityLevel securityLevel) { this.id = new ReinforcementKey(block); this.materialId = material.getMaterial().getId(); - this.durability = material.getStrength(); - this.owner = owner; + double baseDurability = (double)material.getStrength(); + double scale = 1.00000001D; + int blockType = block.getTypeId(); + if (PlayerReinforcement.MATERIAL_SCALING.containsKey(blockType)) { + scale = PlayerReinforcement.MATERIAL_SCALING.get(blockType); + } + this.durability = (int)(baseDurability * scale); + this.ownerName = owner.getName(); this.securityLevel = securityLevel; this.chunkId = this.id.getChunkId(); this.dbAction = DbUpdateAction.INSERT; + if (this.durability < 50) { + this.maturationTime = 0; + } else { + final double interval = Citadel.getConfigManager().getMaturationIntervalD(); + // Maturation time is in minutes since the epoch (Jan 1, 1970) + double tmpDur = (baseDurability / interval) * 60.0D; + this.maturationTime = (int)(System.currentTimeMillis() / 60000L + (long)tmpDur); + } } private void flagForDbUpdate() { @@ -83,6 +103,7 @@ public void updateFrom(PlayerReinforcement that) { setDurability(that.getDurability()); setOwner(that.getOwner()); setSecurityLevel(that.getSecurityLevel()); + setMaturationTime(that.getMaturationTime()); if (getDbAction() == DbUpdateAction.DELETE) { setDbAction(DbUpdateAction.SAVE); } @@ -129,6 +150,22 @@ public void setDurability(int durability) { this.durability = durability; } + public double getScaleFactor() { + int blockType = this.getBlock().getTypeId(); + if (PlayerReinforcement.MATERIAL_SCALING.containsKey(blockType)) { + return PlayerReinforcement.MATERIAL_SCALING.get(blockType); + } + return 1.0000001D; + } + + public int getMaxDurability() { + return this.getMaterial().getStrength(); + } + + public int getScaledMaxDurability() { + return (int)((double)this.getMaterial().getStrength() * this.getScaleFactor()); + } + public String getChunkId() { return this.chunkId; } @@ -146,17 +183,49 @@ public void setSecurityLevel(SecurityLevel securityLevel) { this.securityLevel = securityLevel; } + public String getOwnerName() { + return ownerName; + } + + public void setOwnerName(String newOwner) { + flagForDbUpdate(); + ownerName = newOwner; + } + public Faction getOwner() { - return owner; + return Citadel.getGroupManager().getDelegatedGroup(getOwnerName()); } public void setOwner(Faction group) { - flagForDbUpdate(); - this.owner = group; + String group_name = null; + if (group != null) { + group_name = group.getName(); + } + setOwnerName(group_name); + } + + public int getMaturationTime() { + return this.maturationTime; + } + + public void setMaturationTime(int time) { + this.maturationTime = time; + } + + public boolean isInsecure() { + return this.insecure; + } + + public void setInsecure(boolean value) { + this.insecure = value; + } + + public void toggleInsecure() { + this.setInsecure(!this.isInsecure()); } public double getHealth() { - return (double) durability / (double) getMaterial().getStrength(); + return (double)durability / ((double)this.getMaterial().getStrength() * this.getScaleFactor()); } public String getHealthText() { @@ -186,21 +255,32 @@ public String getStatus() { } public boolean isAccessible(Player player) { - String name = player.getDisplayName(); - return isAccessible(name); + final String name = player.getName(); + return isAccessible(player, name); } public boolean isAccessible(String name) { + final Player player = Bukkit.getPlayerExact(name); + return isAccessible(player, name); + } + + public boolean isAccessible(Player player, String name) { + final Faction owner = getOwner(); if (owner == null) { Citadel.severe(String.format("isAccessible(%s) encountered unowned reinforcement: %s", name, toString())); + sendMessage(player, ChatColor.RED, + "This reinforcement has an issue. Please send modmail. " + getId().toString()); + return false; + } + if (owner.isDisciplined()) { return false; } switch (securityLevel) { case PRIVATE: - return name.equals(owner.getFounder()); + return owner.isFounder(name); case GROUP: - return name.equals(owner.getFounder()) || owner.isMember(name) || owner.isModerator(name); + return owner.isFounder(name) || owner.isMember(name) || owner.isModerator(name); case PUBLIC: return true; } @@ -208,24 +288,38 @@ public boolean isAccessible(String name) { } public boolean isBypassable(Player player) { - String name = player.getDisplayName(); + final String name = player.getName(); + return isBypassable(player, name); + } + + public boolean isBypassable(String name) { + final Player player = Bukkit.getPlayerExact(name); + return isBypassable(player, name); + } + + public boolean isBypassable(Player player, String name) { + final Faction owner = getOwner(); if (owner == null) { Citadel.severe(String.format("isBypassable(%s) encountered unowned reinforcement: %s", name, toString())); - sendMessage(player, ChatColor.RED, "This reinforcement has an issue. Please send modmail."); + sendMessage(player, ChatColor.RED, + "This reinforcement has an issue. Please send modmail. " + getId().toString()); + return false; + } + if (owner.isDisciplined()) { return false; } switch (securityLevel) { case PRIVATE: - return name.equals(owner.getFounder()); + return owner.isFounder(name); default: - return name.equals(owner.getFounder()) || owner.isModerator(name); + return owner.isFounder(name) || owner.isModerator(name); } } public boolean isSecurable() { Block block = getBlock(); - return block.getState() instanceof ContainerBlock + return block.getState() instanceof InventoryHolder || block.getState().getData() instanceof Openable || SECURABLE.contains(block.getTypeId()); } diff --git a/src/com/untamedears/citadel/events/CreateReinforcementEvent.java b/src/com/untamedears/citadel/events/CreateReinforcementEvent.java new file mode 100644 index 00000000..db06b823 --- /dev/null +++ b/src/com/untamedears/citadel/events/CreateReinforcementEvent.java @@ -0,0 +1,76 @@ +package com.untamedears.citadel.events; + +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +import com.untamedears.citadel.entity.IReinforcement; + +public class CreateReinforcementEvent extends Event implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + + public static HandlerList getHandlerList() { + return handlers; + } + + private boolean cancelled_ = false; + + private final Player player_; + private final IReinforcement rein_; + private final Block block_; + private final boolean state_update_; + + // player is possibly null + public CreateReinforcementEvent( + final IReinforcement reinforcement, + final Block block, + final Player player) { + rein_ = reinforcement; + block_ = block; + player_ = player; + state_update_ = false; + } + + // player is possibly null + public CreateReinforcementEvent( + final IReinforcement reinforcement, + final Block block, + final Player player, + final boolean update) { + rein_ = reinforcement; + block_ = block; + player_ = player; + state_update_ = update; + } + + public HandlerList getHandlers() { + return CreateReinforcementEvent.getHandlerList(); + } + + public boolean isCancelled() { + return cancelled_; + } + + public void setCancelled(final boolean cancel) { + cancelled_ = cancel; + } + + public Player getPlayer() { + return player_; + } + + public IReinforcement getReinforcement() { + return rein_; + } + + public Block getBlock() { + return block_; + } + + public boolean isStateUpdate() { + return state_update_; + } +} + diff --git a/src/com/untamedears/citadel/events/GroupChangeEvent.java b/src/com/untamedears/citadel/events/GroupChangeEvent.java new file mode 100644 index 00000000..c744c3d5 --- /dev/null +++ b/src/com/untamedears/citadel/events/GroupChangeEvent.java @@ -0,0 +1,64 @@ +package com.untamedears.citadel.events; + +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +import com.untamedears.citadel.entity.Faction; +import com.untamedears.citadel.events.GroupChangeType; + +public class GroupChangeEvent extends Event implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + + public static HandlerList getHandlerList() { + return handlers; + } + + private boolean cancelled_ = false; + + private final GroupChangeType changeType_; + private final Player player_; + private final String faction_; + private final String targetPlayer_; + + // player is possibly null + public GroupChangeEvent( + final GroupChangeType changeType, + final Player player, + final String faction, + final String targetPlayer) { + changeType_ = changeType; + player_ = player; + faction_ = faction; + targetPlayer_ = targetPlayer; + } + + public HandlerList getHandlers() { + return GroupChangeEvent.getHandlerList(); + } + + public boolean isCancelled() { + return cancelled_; + } + + public void setCancelled(final boolean cancel) { + cancelled_ = cancel; + } + + public GroupChangeType getType() { + return changeType_; + } + + public Player getPlayer() { + return player_; + } + + public String getFactionName() { + return faction_; + } + + public String getTargetPlayerName() { + return targetPlayer_; + } +} diff --git a/src/com/untamedears/citadel/events/GroupChangeType.java b/src/com/untamedears/citadel/events/GroupChangeType.java new file mode 100644 index 00000000..de29c4ee --- /dev/null +++ b/src/com/untamedears/citadel/events/GroupChangeType.java @@ -0,0 +1,10 @@ +package com.untamedears.citadel.events; + +public enum GroupChangeType { + CREATE, + DELETE, + ADD_MODERATOR, + RM_MODERATOR, + ADD_MEMBER, + RM_MEMBER +} diff --git a/src/com/untamedears/citadel/events/PlayerDamageReinforcementEvent.java b/src/com/untamedears/citadel/events/PlayerDamageReinforcementEvent.java new file mode 100644 index 00000000..4db3f864 --- /dev/null +++ b/src/com/untamedears/citadel/events/PlayerDamageReinforcementEvent.java @@ -0,0 +1,57 @@ +package com.untamedears.citadel.events; + +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +import com.untamedears.citadel.entity.IReinforcement; + +public class PlayerDamageReinforcementEvent extends Event implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + + public static HandlerList getHandlerList() { + return handlers; + } + + private boolean cancelled_ = false; + + private final Player player_; + private final IReinforcement rein_; + private final Block block_; + + public PlayerDamageReinforcementEvent( + final IReinforcement reinforcement, + final Block block, + final Player player) { + rein_ = reinforcement; + block_ = block; + player_ = player; + } + + public HandlerList getHandlers() { + return PlayerDamageReinforcementEvent.getHandlerList(); + } + + public boolean isCancelled() { + return cancelled_; + } + + public void setCancelled(final boolean cancel) { + cancelled_ = cancel; + } + + public Player getPlayer() { + return player_; + } + + public IReinforcement getReinforcement() { + return rein_; + } + + public Block getBlock() { + return block_; + } +} + diff --git a/src/com/untamedears/citadel/listener/BlockListener.java b/src/com/untamedears/citadel/listener/BlockListener.java index ae308d19..9ff5527b 100644 --- a/src/com/untamedears/citadel/listener/BlockListener.java +++ b/src/com/untamedears/citadel/listener/BlockListener.java @@ -2,52 +2,116 @@ import static com.untamedears.citadel.Utility.createNaturalReinforcement; import static com.untamedears.citadel.Utility.createPlayerReinforcement; +import static com.untamedears.citadel.Utility.isAuthorizedPlayerNear; +import static com.untamedears.citadel.Utility.isPlant; +import static com.untamedears.citadel.Utility.isRail; import static com.untamedears.citadel.Utility.maybeReinforcementDamaged; import static com.untamedears.citadel.Utility.reinforcementBroken; import static com.untamedears.citadel.Utility.reinforcementDamaged; import static com.untamedears.citadel.Utility.sendMessage; +import static com.untamedears.citadel.Utility.sendThrottledMessage; +import static com.untamedears.citadel.Utility.wouldPlantDoubleReinforce; -import java.util.ConcurrentModificationException; -import java.util.HashSet; -import java.util.Set; +import java.util.Arrays; +import java.util.List; +import org.bukkit.Bukkit; import org.bukkit.ChatColor; -import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.BlockState; +import org.bukkit.block.ContainerBlock; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockBurnEvent; +import org.bukkit.event.block.BlockFromToEvent; +import org.bukkit.event.block.BlockPhysicsEvent; import org.bukkit.event.block.BlockPistonExtendEvent; import org.bukkit.event.block.BlockPistonRetractEvent; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.block.BlockRedstoneEvent; -import org.bukkit.event.block.BlockPhysicsEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; import org.bukkit.material.MaterialData; import org.bukkit.material.Openable; import org.bukkit.material.PistonBaseMaterial; -import org.bukkit.Effect; -import org.bukkit.World; import com.untamedears.citadel.Citadel; +import com.untamedears.citadel.Citadel.VerboseMsg; import com.untamedears.citadel.PlacementMode; import com.untamedears.citadel.SecurityLevel; import com.untamedears.citadel.access.AccessDelegate; -import com.untamedears.citadel.entity.PlayerState; +import com.untamedears.citadel.access.CropAccessDelegate; +import com.untamedears.citadel.entity.Faction; import com.untamedears.citadel.entity.IReinforcement; -import com.untamedears.citadel.entity.NaturalReinforcement; import com.untamedears.citadel.entity.PlayerReinforcement; +import com.untamedears.citadel.entity.PlayerState; import com.untamedears.citadel.entity.ReinforcementMaterial; +import com.untamedears.citadel.events.PlayerDamageReinforcementEvent; public class BlockListener implements Listener { + public static final List all_sides = Arrays.asList( + BlockFace.UP, BlockFace.DOWN, BlockFace.NORTH, + BlockFace.SOUTH, BlockFace.WEST, BlockFace.EAST); + + public static final List planar_sides = Arrays.asList( + BlockFace.NORTH, BlockFace.SOUTH, BlockFace.WEST, BlockFace.EAST); + + private boolean canPlace(Block block, String player_name) { + Material block_mat = block.getType(); + // See if block is a sign over a chest indicating it's a shop. + // Check to see if we need to worry about physical shop interaction. + if (block_mat == Material.WALL_SIGN + && Bukkit.getPluginManager().isPluginEnabled("PhysicalShop")) { + Block below = block.getRelative(BlockFace.DOWN); + if (below.getType() == Material.CHEST || block_mat == Material.TRAPPED_CHEST) { + IReinforcement rein = AccessDelegate.getDelegate(below).getReinforcement(); + if (null != rein + && rein instanceof PlayerReinforcement + && !((PlayerReinforcement)rein).isAccessible(player_name)) { + // Don't allow another player to access the chest by creating a shop + return false; + } + } + } + if (block_mat == Material.HOPPER || block_mat == Material.DROPPER){ + for (BlockFace direction : all_sides) { + Block adjacent = block.getRelative(direction); + if (!(adjacent.getState() instanceof ContainerBlock)) { + continue; + } + IReinforcement rein = AccessDelegate.getDelegate(adjacent).getReinforcement(); + if (null != rein && rein instanceof PlayerReinforcement) { + PlayerReinforcement pr = (PlayerReinforcement)rein; + if (!pr.isInsecure() && !pr.isAccessible(player_name)) { + return false; + } + } + } + } + if (block_mat == Material.CHEST || block_mat == Material.TRAPPED_CHEST){ + for (BlockFace direction : planar_sides) { + Block adjacent = block.getRelative(direction); + if (!(adjacent.getState() instanceof ContainerBlock)) { + continue; + } + IReinforcement rein = AccessDelegate.getDelegate(adjacent).getReinforcement(); + if (null != rein && rein instanceof PlayerReinforcement) { + PlayerReinforcement pr = (PlayerReinforcement)rein; + if (!pr.isAccessible(player_name)) { + return false; + } + } + } + } + return true; + } + /** * This handles the BlockPlaceEvent for Fortification mode (all placed blocks are reinforced) * @@ -55,9 +119,26 @@ public class BlockListener implements Listener { */ @EventHandler(ignoreCancelled = true) public void placeFortifiedBlock(BlockPlaceEvent bpe) { + Block block = bpe.getBlockPlaced(); + if (bpe.getBlockReplacedState().getType().equals(Material.AIR)) { + IReinforcement existingReinforcement = Citadel.getReinforcementManager().getReinforcement(block); + if (existingReinforcement != null && existingReinforcement instanceof PlayerReinforcement) { + Citadel.getReinforcementManager().removeReinforcement(existingReinforcement); + } + } Player player = bpe.getPlayer(); + if (!canPlace(block, player.getName())) { + sendThrottledMessage(player, ChatColor.RED, "Cancelled block place, mismatched reinforcement."); + bpe.setCancelled(true); + return; + } PlayerState state = PlayerState.get(player); - + Faction group = state.getFaction(); + if (group != null && group.isDisciplined()) { + sendThrottledMessage(player, ChatColor.RED, Faction.kDisciplineMsg); + bpe.setCancelled(true); + return; + } if (state.getMode() != PlacementMode.FORTIFICATION) { // if we are not in fortification mode // cancel event if we are not in normal mode @@ -65,17 +146,20 @@ public void placeFortifiedBlock(BlockPlaceEvent bpe) { bpe.setCancelled(true); return; } - - Block block = bpe.getBlockPlaced(); + // Don't allow double reinforcing reinforceable plants + if (wouldPlantDoubleReinforce(block)) { + sendThrottledMessage(player, ChatColor.RED, "Cancelled block place, crop would already be reinforced."); + bpe.setCancelled(true); + return; + } PlayerInventory inventory = player.getInventory(); - ReinforcementMaterial material = state.getReinforcementMaterial(); ItemStack required = material.getRequiredMaterials(); if (inventory.contains(material.getMaterial(), required.getAmount())) { if (createPlayerReinforcement(player, block) == null) { sendMessage(player, ChatColor.RED, "%s is not a reinforcible material", block.getType().name()); } else { - state.checkResetMode(); + state.checkResetMode(); } } else { sendMessage(player, ChatColor.YELLOW, "%s depleted, left fortification mode", material.getMaterial().name()); @@ -92,31 +176,77 @@ public void blockBreak(BlockBreakEvent bbe) { AccessDelegate delegate = AccessDelegate.getDelegate(block); IReinforcement reinforcement = delegate.getReinforcement(); if (reinforcement == null) { - reinforcement = createNaturalReinforcement(block); - if (reinforcement != null && reinforcementDamaged(reinforcement)) { - bbe.setCancelled(true); - block.getDrops().clear(); + reinforcement = createNaturalReinforcement(block, player); + if (reinforcement != null) { + PlayerDamageReinforcementEvent dre = new PlayerDamageReinforcementEvent(reinforcement, block, player); + + Bukkit.getPluginManager().callEvent(dre); + + if(dre.isCancelled()) { + bbe.setCancelled(true); + block.getDrops().clear(); + + return; + } + + if(reinforcementDamaged(reinforcement)) { + bbe.setCancelled(true); + block.getDrops().clear(); + } } - return; - } + return; + } boolean is_cancelled = true; if (reinforcement instanceof PlayerReinforcement) { PlayerReinforcement pr = (PlayerReinforcement)reinforcement; PlayerState state = PlayerState.get(player); - if (state.isBypassMode() && pr.isBypassable(player)) { - Citadel.info(player.getDisplayName() + " bypassed reinforcement %s at " - + pr.getBlock().getLocation().toString()); + boolean admin_bypass = player.hasPermission("citadel.admin.bypassmode"); + if ((delegate instanceof CropAccessDelegate) + && (pr.isAccessible(player) || admin_bypass)) { + // If this is a delegated reinforcement for a crop which the + // player has access to, allow the player to break the crop + // without effecting the reinforcement. + is_cancelled = false; + } else if (state.isBypassMode() && (pr.isBypassable(player) || admin_bypass)) { + if (admin_bypass) { + Citadel.verbose( + VerboseMsg.AdminReinBypass, + player.getName(), pr.getBlock().getLocation().toString()); + } else { + Citadel.verbose( + VerboseMsg.ReinBypass, + player.getName(), pr.getBlock().getLocation().toString()); + } is_cancelled = reinforcementBroken(reinforcement); } else { - is_cancelled = reinforcementDamaged(reinforcement); + PlayerDamageReinforcementEvent dre = new PlayerDamageReinforcementEvent(reinforcement, block, player); + + Bukkit.getPluginManager().callEvent(dre); + + if(dre.isCancelled()) { + is_cancelled = true; + } + else { + is_cancelled = reinforcementDamaged(reinforcement); + } } if (!is_cancelled) { // The player reinforcement broke. Now check for natural - is_cancelled = createNaturalReinforcement(block) != null; + is_cancelled = createNaturalReinforcement(block, player) != null; } } else { - is_cancelled = reinforcementDamaged(reinforcement); + PlayerDamageReinforcementEvent dre = new PlayerDamageReinforcementEvent(reinforcement, block, player); + + Bukkit.getPluginManager().callEvent(dre); + + if(dre.isCancelled()) { + is_cancelled = reinforcementDamaged(reinforcement); + return; + } + else { + is_cancelled = reinforcementDamaged(reinforcement); + } } if (is_cancelled) { @@ -127,70 +257,70 @@ public void blockBreak(BlockBreakEvent bbe) { @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH) public void pistonExtend(BlockPistonExtendEvent bpee) { - Block piston = bpee.getBlock(); - BlockState state = piston.getState(); - MaterialData data = state.getData(); - BlockFace direction = null; - - if (data instanceof PistonBaseMaterial) { - direction = ((PistonBaseMaterial) data).getFacing(); - } - - // if no direction was found, no point in going on - if (direction == null) - return; - - // Check the affected blocks - for (int i = 1; i < bpee.getLength() + 2; i++) { - Block block = piston.getRelative(direction, i); - - if (block.getType() == Material.AIR){ - break; - } - - AccessDelegate delegate = AccessDelegate.getDelegate(block); - IReinforcement reinforcement = delegate.getReinforcement(); - - if (reinforcement != null){ - bpee.setCancelled(true); - break; - } - } + Block piston = bpee.getBlock(); + BlockState state = piston.getState(); + MaterialData data = state.getData(); + BlockFace direction = null; + + if (data instanceof PistonBaseMaterial) { + direction = ((PistonBaseMaterial) data).getFacing(); + } + + // if no direction was found, no point in going on + if (direction == null) + return; + + // Check the affected blocks + for (int i = 1; i < bpee.getLength() + 2; i++) { + Block block = piston.getRelative(direction, i); + + if (block.getType() == Material.AIR){ + break; + } + + AccessDelegate delegate = AccessDelegate.getDelegate(block); + IReinforcement reinforcement = delegate.getReinforcement(); + + if (reinforcement != null){ + bpee.setCancelled(true); + break; + } + } } - + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH) public void pistonRetract(BlockPistonRetractEvent bpre) { - Block piston = bpre.getBlock(); - BlockState state = piston.getState(); - MaterialData data = state.getData(); - BlockFace direction = null; - - // Check the block it pushed directly - if (data instanceof PistonBaseMaterial) { - direction = ((PistonBaseMaterial) data).getFacing(); - } - - if (direction == null) - return; - - // the block that the piston moved - Block moved = piston.getRelative(direction, 2); - - AccessDelegate delegate = AccessDelegate.getDelegate(moved); - IReinforcement reinforcement = delegate.getReinforcement(); - - if (reinforcement != null) { - bpre.setCancelled(true); - } + Block piston = bpre.getBlock(); + BlockState state = piston.getState(); + MaterialData data = state.getData(); + BlockFace direction = null; + + // Check the block it pushed directly + if (data instanceof PistonBaseMaterial) { + direction = ((PistonBaseMaterial) data).getFacing(); + } + + if (direction == null) + return; + + // the block that the piston moved + Block moved = piston.getRelative(direction, 2); + + AccessDelegate delegate = AccessDelegate.getDelegate(moved); + IReinforcement reinforcement = delegate.getReinforcement(); + + if (reinforcement != null) { + bpre.setCancelled(true); + } } - + private static final Material matfire = Material.FIRE; @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH) public void blockBurn(BlockBurnEvent bbe) { boolean wasprotected = maybeReinforcementDamaged(bbe.getBlock()); - if (wasprotected) { + if (wasprotected) { bbe.setCancelled(wasprotected); - Block block = bbe.getBlock(); + Block block = bbe.getBlock(); // Basic essential fire protection if (block.getRelative(0,1,0).getType() == matfire) {block.getRelative(0,1,0).setTypeId(0);} // Essential // Extended fire protection (recommend) @@ -210,28 +340,19 @@ public void blockBurn(BlockBurnEvent bbe) { if (block.getRelative(0,1,-1).getType() == matfire) {block.getRelative(0,1,-1).setTypeId(0);} if (block.getRelative(0,-1,-1).getType() == matfire) {block.getRelative(0,-1,-1).setTypeId(0);} */ - } + } } @EventHandler(ignoreCancelled = true, priority = EventPriority.LOW) - public void blockPhysics(BlockPhysicsEvent bpe) { - Material changedType = bpe.getChangedType(); - if (Material.LAVA == changedType) { - Block block = bpe.getBlock(); - // Protection for reinforced rails types from lava. Similar to water, transform surrounding blocks in cobblestone to stop the lava effect. - if (Material.RAILS == block.getType() || Material.POWERED_RAIL == block.getType() || Material.DETECTOR_RAIL == block.getType()) { - boolean isReinforced = maybeReinforcementDamaged(block); - if (isReinforced) { - for (BlockFace blockFace : new BlockFace[]{BlockFace.DOWN, BlockFace.UP, BlockFace.EAST, BlockFace.WEST, BlockFace.NORTH, BlockFace.SOUTH}) { - Block otherBlock = block.getRelative(blockFace); - if (Material.LAVA == otherBlock.getType()) { - otherBlock.setType(Material.COBBLESTONE); - otherBlock.getWorld().playEffect(otherBlock.getLocation(), Effect.EXTINGUISH, 0); - } - } - } - } - } + public void onBlockFromToEvent(BlockFromToEvent event) { + Block to_block = event.getToBlock(); + if (!isRail(to_block) && !isPlant(to_block)) { + return; + } + AccessDelegate delegate = AccessDelegate.getDelegate(to_block); + if (delegate.isReinforced()) { + event.setCancelled(true); + } } @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) @@ -269,48 +390,10 @@ public void redstonePower(BlockRedstoneEvent bre) { return; } double redstoneDistance = Citadel.getConfigManager().getRedstoneDistance(); - Location blockLocation = block.getLocation(); - double min_x = blockLocation.getX() - redstoneDistance; - double min_z = blockLocation.getZ() - redstoneDistance; - double max_x = blockLocation.getX() + redstoneDistance; - double max_z = blockLocation.getZ() + redstoneDistance; - World blockWorld = blockLocation.getWorld(); - //Set onlinePlayers = new HashSet(Citadel.getMemberManager().getOnlinePlayers()); - Player[] onlinePlayers = Citadel.getPlugin().getServer().getOnlinePlayers(); - boolean isAuthorizedPlayerNear = false; - try { - for (Player player : onlinePlayers) { - if (player.isDead()) { - continue; - } - Location playerLocation = player.getLocation(); - double player_x = playerLocation.getX(); - double player_z = playerLocation.getZ(); - // Simple bounding box check to quickly rule out Players - // before doing the more expensive playerLocation.distance - if (player_x < min_x || player_x > max_x || - player_z < min_z || player_z > max_z) { - continue; - } - if (playerLocation.getWorld() != blockWorld) { - continue; - } - if (!reinforcement.isAccessible(player)) { - continue; - } - double distanceSquared = - playerLocation.distance(blockLocation); - if (distanceSquared <= redstoneDistance) { - isAuthorizedPlayerNear = true; - break; - } - } - } catch (ConcurrentModificationException e) { - Citadel.warning("ConcurrentModificationException at redstonePower() in BlockListener"); - } - if (!isAuthorizedPlayerNear) { - Citadel.info("Prevented redstone from opening reinforcement at " - + reinforcement.getBlock().getLocation().toString()); + if (!isAuthorizedPlayerNear(reinforcement, redstoneDistance)) { + Citadel.verbose( + VerboseMsg.RedstoneOpen, + reinforcement.getBlock().getLocation().toString()); bre.setNewCurrent(bre.getOldCurrent()); } } catch(Exception e) { diff --git a/src/com/untamedears/citadel/listener/EntityListener.java b/src/com/untamedears/citadel/listener/EntityListener.java index dcf509a5..67944bd3 100644 --- a/src/com/untamedears/citadel/listener/EntityListener.java +++ b/src/com/untamedears/citadel/listener/EntityListener.java @@ -17,9 +17,9 @@ import org.bukkit.event.entity.EntityBreakDoorEvent; import org.bukkit.event.entity.EntityChangeBlockEvent; import org.bukkit.event.entity.EntityExplodeEvent; -import org.bukkit.event.world.StructureGrowEvent; import com.untamedears.citadel.Citadel; +import com.untamedears.citadel.Citadel.VerboseMsg; import com.untamedears.citadel.ReinforcementManager; import com.untamedears.citadel.entity.IReinforcement; @@ -66,8 +66,9 @@ public void spawn(CreatureSpawnEvent cse) { for (Block block : getGolemBlocks(type, cse.getLocation().getBlock())) { IReinforcement reinforcement = reinforcementManager.getReinforcement(block); if (reinforcement != null) { - Citadel.info("Reinforcement %s removed due to golem creation at " - + reinforcement.getBlock().getLocation().toString()); + Citadel.verbose( + VerboseMsg.GolemCreated, + reinforcement.getBlock().getLocation().toString()); reinforcementManager.removeReinforcement(reinforcement); } } @@ -90,15 +91,4 @@ private List getGolemBlocks(EntityType type, Block base) { return blocks; } - - @EventHandler(ignoreCancelled = true) - public void grow(StructureGrowEvent sge) { - ReinforcementManager reinforcementManager = Citadel.getReinforcementManager(); - IReinforcement reinforcement = reinforcementManager.getReinforcement(sge.getLocation()); - if (reinforcement != null) { - Citadel.info("Reinforcement %s removed due to structure growth at " - + reinforcement.getBlock().getLocation().toString()); - reinforcementManager.removeReinforcement(reinforcement); - } - } } diff --git a/src/com/untamedears/citadel/listener/InventoryListener.java b/src/com/untamedears/citadel/listener/InventoryListener.java new file mode 100644 index 00000000..470c1731 --- /dev/null +++ b/src/com/untamedears/citadel/listener/InventoryListener.java @@ -0,0 +1,145 @@ +package com.untamedears.citadel.listener; + +import java.util.Set; +import java.util.TreeSet; + +import org.bukkit.Location; +import org.bukkit.block.BlockState; +import org.bukkit.block.DoubleChest; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryMoveItemEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; + +import com.untamedears.citadel.Citadel; +import com.untamedears.citadel.Citadel.VerboseMsg; +import com.untamedears.citadel.SecurityLevel; +import com.untamedears.citadel.access.AccessDelegate; +import com.untamedears.citadel.entity.Faction; +import com.untamedears.citadel.entity.IReinforcement; +import com.untamedears.citadel.entity.PlayerReinforcement; + +public class InventoryListener implements Listener { + private Set priorMessages_ = new TreeSet(); + + public PlayerReinforcement getReinforcement(Inventory inv) { + // Returns reinforcement of the inventory's holder or null if none exists + final InventoryHolder holder = inv.getHolder(); + Location loc; + if (holder instanceof DoubleChest) { + loc = ((DoubleChest)holder).getLocation(); + } else if (holder instanceof BlockState) { + loc = ((BlockState)holder).getLocation(); + } else { + // Entity or Vehicle inventories + return null; + } + final AccessDelegate delegate = AccessDelegate.getDelegate(loc.getBlock()); + final IReinforcement rein = delegate.getReinforcement(); + if (rein instanceof PlayerReinforcement) { + PlayerReinforcement pr = (PlayerReinforcement)rein; + // Treat public reinforcements as if they don't exist + if (!pr.getSecurityLevel().equals(SecurityLevel.PUBLIC)) { + return pr; + } + } + return null; + } + + @EventHandler(ignoreCancelled = true) + public void onInventoryMoveItemEvent(InventoryMoveItemEvent event) { + // Prevent hopper minecarts from extracting from reinforced containers or + // filling up reinforced containers. + // Prevent misowned hoppers from stealing from reinforced containers. + // + // Allowed transfers: + // Assertions: + // Public reinforcement == Non-reinforced + // Y is a member of Group X + // Insecure sources act like public reinforcements + // Public -> Public + // Public -> Group X + // Public -> Personal Y + // Group X -> Group X + // Group X -> Personal Y + // Personal Y -> Personal Y + + // Fail safe + event.setCancelled(true); + // Source + final Inventory src = event.getSource(); + final PlayerReinforcement srcRein = getReinforcement(src); + // Destination + final Inventory dest = event.getDestination(); + final PlayerReinforcement destRein = getReinforcement(dest); + if (srcRein == null) { + if (destRein != null) { + final Faction destOwner = destRein.getOwner(); + if (destOwner != null && destOwner.isDisciplined()) { + // Dest is disabled, deny + return; + } + } + // Public can transfer into any, allow + // (Public -> Public, Public -> Group X, Public -> Personal Y) + event.setCancelled(false); + return; + } + if (srcRein.isInsecure()) { + // Insecure source reinforcement allows transfer as if it's + // a public reinforcement, allow + event.setCancelled(false); + return; + } + // Assertion: srcRein != null + if (destRein == null) { + // Non-public cannot transfer into a public, deny + return; + } + // Assertion: srcRein != null && destRein != null + final Faction srcOwner = srcRein.getOwner(); + final Faction destOwner = destRein.getOwner(); + // Check for null group failure + if (srcOwner == null || destOwner == null) { + if (Citadel.verboseEnabled(VerboseMsg.NullGroup)) { + String msg; + if (srcOwner == null) { + msg = Citadel.verboseFmt( + VerboseMsg.NullGroup, + "srcOwner", srcRein.getOwnerName()); + } else { // destOwner == null + msg = Citadel.verboseFmt( + VerboseMsg.NullGroup, + "dstOwner", destRein.getOwnerName()); + } + if (!priorMessages_.contains(msg)) { + Citadel.info(msg); + priorMessages_.add(msg); + } + } + // Unable to determine reinforcement owner match, deny + return; + } + if (srcOwner.isDisciplined() || destOwner.isDisciplined()) { + // Disabled group involved, deny + return; + } + if (srcOwner == destOwner) { + // Reinforcement owners match, allow + // (Group X -> Group X, Personal Y -> Personal Y) + event.setCancelled(false); + return; + } + final boolean srcIsPersonal = srcOwner.isPersonalGroup(); + final boolean destIsPersonal = destOwner.isPersonalGroup(); + if (!srcIsPersonal && destIsPersonal && srcRein.isAccessible(destOwner.getFounder())) { + // Destination personal group owner has access to source group, allow + // (Group X -> Personal Y) + event.setCancelled(false); + return; + } + // Reinforcement owners don't match, deny + } +} diff --git a/src/com/untamedears/citadel/listener/PlayerListener.java b/src/com/untamedears/citadel/listener/PlayerListener.java index 664981d0..baaf2e1a 100644 --- a/src/com/untamedears/citadel/listener/PlayerListener.java +++ b/src/com/untamedears/citadel/listener/PlayerListener.java @@ -1,11 +1,16 @@ package com.untamedears.citadel.listener; import static com.untamedears.citadel.Utility.createPlayerReinforcement; -import static com.untamedears.citadel.Utility.maybeReinforcementDamaged; +import static com.untamedears.citadel.Utility.isPlant; +import static com.untamedears.citadel.Utility.isRail; +import static com.untamedears.citadel.Utility.isReinforced; import static com.untamedears.citadel.Utility.reinforcementBroken; import static com.untamedears.citadel.Utility.sendMessage; +import static com.untamedears.citadel.Utility.timeUntilMature; +import static com.untamedears.citadel.Utility.wouldPlantDoubleReinforce; import org.bukkit.ChatColor; +import org.bukkit.GameMode; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; @@ -22,17 +27,20 @@ import org.bukkit.material.Openable; import com.untamedears.citadel.Citadel; +import com.untamedears.citadel.Citadel.VerboseMsg; import com.untamedears.citadel.GroupManager; import com.untamedears.citadel.MemberManager; import com.untamedears.citadel.PersonalGroupManager; import com.untamedears.citadel.PlacementMode; import com.untamedears.citadel.SecurityLevel; import com.untamedears.citadel.access.AccessDelegate; +import com.untamedears.citadel.access.CropAccessDelegate; import com.untamedears.citadel.entity.Faction; import com.untamedears.citadel.entity.Member; import com.untamedears.citadel.entity.PlayerState; import com.untamedears.citadel.entity.IReinforcement; import com.untamedears.citadel.entity.PlayerReinforcement; +import com.untamedears.citadel.events.CreateReinforcementEvent; /** * Created by IntelliJ IDEA. @@ -44,46 +52,47 @@ * 7/18/12 */ public class PlayerListener implements Listener { - + @EventHandler public void login(PlayerLoginEvent ple) { - MemberManager memberManager = Citadel.getMemberManager(); - memberManager.addOnlinePlayer(ple.getPlayer()); - - String playerName = ple.getPlayer().getDisplayName(); - Member member = memberManager.getMember(playerName); - if(member == null){ - member = new Member(playerName); - memberManager.addMember(member); - } - - PersonalGroupManager personalGroupManager = Citadel.getPersonalGroupManager(); - boolean hasPersonalGroup = personalGroupManager.hasPersonalGroup(playerName); - GroupManager groupManager = Citadel.getGroupManager(); - if(!hasPersonalGroup){ - String groupName = playerName; - int i = 1; - while(groupManager.isGroup(groupName)){ - groupName = playerName + i; - i++; - } - Faction group = new Faction(groupName, playerName); - groupManager.addGroup(group); - personalGroupManager.addPersonalGroup(groupName, playerName); - } else if(hasPersonalGroup){ - String personalGroupName = personalGroupManager.getPersonalGroup(playerName).getGroupName(); - if(!groupManager.isGroup(personalGroupName)){ - Faction group = new Faction(personalGroupName, playerName); - groupManager.addGroup(group); - } - } + MemberManager memberManager = Citadel.getMemberManager(); + memberManager.addOnlinePlayer(ple.getPlayer()); + + Player player = ple.getPlayer(); + String playerName = player.getName(); + Member member = memberManager.getMember(playerName); + if(member == null){ + member = new Member(playerName); + memberManager.addMember(member); + } + + PersonalGroupManager personalGroupManager = Citadel.getPersonalGroupManager(); + boolean hasPersonalGroup = personalGroupManager.hasPersonalGroup(playerName); + GroupManager groupManager = Citadel.getGroupManager(); + if(!hasPersonalGroup){ + String groupName = playerName; + int i = 1; + while(groupManager.isGroup(groupName)){ + groupName = playerName + i; + i++; + } + Faction group = new Faction(groupName, playerName); + groupManager.addGroup(group, player); + personalGroupManager.addPersonalGroup(groupName, playerName); + } else if(hasPersonalGroup){ + String personalGroupName = personalGroupManager.getPersonalGroup(playerName).getGroupName(); + if(!groupManager.isGroup(personalGroupName)){ + Faction group = new Faction(personalGroupName, playerName); + groupManager.addGroup(group, player); + } + } } @EventHandler public void quit(PlayerQuitEvent pqe) { - Player player = pqe.getPlayer(); - MemberManager memberManager = Citadel.getMemberManager(); - memberManager.removeOnlinePlayer(player); + Player player = pqe.getPlayer(); + MemberManager memberManager = Citadel.getMemberManager(); + memberManager.removeOnlinePlayer(player); PlayerState.remove(player); } @@ -95,22 +104,24 @@ public void bookshelf(PlayerInteractEvent pie) { @EventHandler(ignoreCancelled = true, priority = EventPriority.LOW) public void bucketEmpty(PlayerBucketEmptyEvent pbee) { - Material bucket = pbee.getBucket(); - if (Material.LAVA_BUCKET == bucket) { - Block block = pbee.getBlockClicked(); - BlockFace face = pbee.getBlockFace(); - Block relativeBlock = block.getRelative(face); - // Protection for reinforced rails types from direct lava bucket drop. - if (Material.RAILS == relativeBlock.getType() || Material.POWERED_RAIL == relativeBlock.getType() || Material.DETECTOR_RAIL == relativeBlock.getType()) { - boolean isReinforced = maybeReinforcementDamaged(relativeBlock); - pbee.setCancelled(isReinforced); - } - } + Material bucket = pbee.getBucket(); + if (Material.LAVA_BUCKET == bucket || Material.WATER_BUCKET == bucket) { + Block baseBlock = pbee.getBlockClicked(); + BlockFace face = pbee.getBlockFace(); + Block block = baseBlock.getRelative(face); + AccessDelegate delegate = AccessDelegate.getDelegate(block); + // Protection for reinforced rails types from direct lava bucket drop. + if (isRail(block) || isPlant(block)) { + if (delegate.isReinforced()) { + pbee.setCancelled(true); + } + } + } } @EventHandler(priority = EventPriority.HIGHEST) public void interact(PlayerInteractEvent pie) { - try { + try { if (!pie.hasBlock()) return; Player player = pie.getPlayer(); @@ -124,17 +135,30 @@ public void interact(PlayerInteractEvent pie) { reinforcement = (PlayerReinforcement)generic_reinforcement; } - if (reinforcement != null - && reinforcement.isSecurable() - && !reinforcement.isAccessible(player)) { - Action action = pie.getAction(); - if(action == Action.RIGHT_CLICK_BLOCK){ - Citadel.info("%s failed to access locked reinforcement %s, " - + player.getDisplayName() + " at " - + block.getLocation().toString()); - sendMessage(pie.getPlayer(), ChatColor.RED, "%s is locked", block.getType().name()); - pie.setCancelled(true); - } + Action action = pie.getAction(); + boolean access_reinforcement = + action == Action.RIGHT_CLICK_BLOCK + && reinforcement != null + && reinforcement.isSecurable(); + boolean normal_access_denied = + reinforcement != null + && !reinforcement.isAccessible(player); + boolean admin_can_access = player.hasPermission("citadel.admin.accesssecurable"); + if (access_reinforcement && normal_access_denied && !admin_can_access) { + Citadel.verbose( + VerboseMsg.ReinLocked, + player.getName(), block.getLocation().toString()); + sendMessage(pie.getPlayer(), ChatColor.RED, "%s is locked", block.getType().name()); + pie.setCancelled(true); + } else if (action == Action.PHYSICAL) { + AccessDelegate aboveDelegate = AccessDelegate.getDelegate(block.getRelative(BlockFace.UP)); + if (aboveDelegate instanceof CropAccessDelegate + && aboveDelegate.isReinforced()) { + Citadel.verbose( + VerboseMsg.CropTrample, + block.getLocation().toString()); + pie.setCancelled(true); + } } if (pie.isCancelled()) return; @@ -142,29 +166,99 @@ public void interact(PlayerInteractEvent pie) { PlacementMode placementMode = state.getMode(); switch (placementMode) { case NORMAL: - return; + if (access_reinforcement && normal_access_denied && admin_can_access) { + Citadel.verbose( + VerboseMsg.AdminReinLocked, + player.getName(), block.getLocation().toString()); + } + return; case FORTIFICATION: return; case INFO: // did player click on a reinforced block? if (reinforcement != null) { - String reinforcementStatus = reinforcement.getStatus(); - SecurityLevel securityLevel = reinforcement.getSecurityLevel(); - if(reinforcement.isAccessible(player)){ - Faction group = reinforcement.getOwner(); - String groupName = group.getName(); - String message = ""; - if(group.isPersonalGroup()){ - message = String.format("%s, security: %s, group: %s (Default Group)", reinforcementStatus, securityLevel, groupName); - } else { - message = String.format("%s, security: %s, group: %s", reinforcementStatus, securityLevel, groupName); - } - sendMessage(player, ChatColor.GREEN, message); + String reinforcementStatus = reinforcement.getStatus(); + SecurityLevel securityLevel = reinforcement.getSecurityLevel(); + Faction group = reinforcement.getOwner(); + StringBuilder sb; + if (player.hasPermission("citadel.admin.ctinfodetails")) { + sendMessage(player, ChatColor.GREEN, String.format( + "Loc[%s] Chunk[%s]", reinforcement.getId().toString(), reinforcement.getChunkId())); + String groupName = "!NULL!"; + if (group != null) { + if (group.isPersonalGroup()) { + groupName = String.format("[%s] (Personal)", group.getName()); + } else { + groupName = String.format("[%s]", group.getName()); + } + } + sb = new StringBuilder(); + sb.append(String.format(" Group%s Durability[%d/%d]", + groupName, + reinforcement.getDurability(), + reinforcement.getScaledMaxDurability())); + int maturationTime = timeUntilMature(reinforcement); + if (maturationTime != 0) { + sb.append(" Immature["); + sb.append(maturationTime); + sb.append("]"); + } + if (reinforcement.isInsecure()) { + sb.append(" (Insecure)"); + } + sendMessage(player, ChatColor.GREEN, sb.toString()); + } else if(reinforcement.isAccessible(player)){ + sb = new StringBuilder(); + boolean immature = + timeUntilMature(reinforcement) != 0 + && (Citadel.getConfigManager().maturationEnabled() + || Citadel.getConfigManager().getAcidBlockType() == block.getTypeId()); + boolean is_personal_group = false; + String groupName = "!NULL!"; + if (group != null) { + groupName = group.getName(); + is_personal_group = group.isPersonalGroup(); + } + sb.append(String.format("%s, security: %s, group: %s", + reinforcementStatus, securityLevel, groupName)); + if(is_personal_group){ + sb.append(" (Default Group)"); + } + if(immature){ + sb.append(" (Hardening)"); + } + if (reinforcement.isInsecure()) { + sb.append(" (Insecure)"); + } + sendMessage(player, ChatColor.GREEN, sb.toString()); } else { - sendMessage(player, ChatColor.RED, "%s, security: %s", reinforcementStatus, securityLevel); + sendMessage(player, ChatColor.RED, "%s, security: %s", reinforcementStatus, securityLevel); + } + if (player.getGameMode() == GameMode.CREATIVE) { + pie.setCancelled(true); + } + } + break; + + case INSECURE: + // did player click on a reinforced block? + pie.setCancelled(true); + if (reinforcement != null) { + if (reinforcement.isBypassable(player)) { + reinforcement.toggleInsecure(); + // Save the change + Citadel.getReinforcementManager().addReinforcement(reinforcement); + if (reinforcement.isInsecure()) { + sendMessage(player, ChatColor.YELLOW, "Reinforcement now insecure"); + } else { + sendMessage(player, ChatColor.GREEN, "Reinforcement secured"); + } + } else { + sendMessage(player, ChatColor.RED, "Access denied"); } } break; + default: // player is in reinforcement mode if (reinforcement == null) { @@ -172,28 +266,38 @@ public void interact(PlayerInteractEvent pie) { if (generic_reinforcement != null) { reinforcementBroken(generic_reinforcement); } - createPlayerReinforcement(player, block); + // Don't allow double reinforcing reinforceable plants + if (wouldPlantDoubleReinforce(block)) { + sendMessage(player, ChatColor.RED, "Cancelled reinforcement, crop would already be reinforced."); + } else { + createPlayerReinforcement(player, block); + } } else if (reinforcement.isBypassable(player)) { - boolean update = false; - String message = ""; + boolean update = false; + String message = ""; if (reinforcement.getSecurityLevel() != state.getSecurityLevel()){ reinforcement.setSecurityLevel(state.getSecurityLevel()); update = true; message = String.format("Changed security level %s", reinforcement.getSecurityLevel().name()); } - if(!reinforcement.getOwner().equals(state.getFaction())) { - reinforcement.setOwner(state.getFaction()); + Faction group = state.getFaction(); + if(!reinforcement.getOwner().equals(group)) { + reinforcement.setOwner(group); update = true; if(!message.equals("")){ - message = message + ". "; + message = message + ". "; } if(reinforcement.getSecurityLevel() != SecurityLevel.PRIVATE){ - message = message + String.format("Changed group to %s", state.getFaction().getName()); + message = message + String.format("Changed group to %s", group.getName()); } } - if(update){ - Citadel.getReinforcementManager().addReinforcement(reinforcement); - sendMessage(player, ChatColor.GREEN, message); + if(update){ + CreateReinforcementEvent event = new CreateReinforcementEvent(reinforcement, block, player, true); + Citadel.getStaticServer().getPluginManager().callEvent(event); + if (!event.isCancelled()) { + Citadel.getReinforcementManager().addReinforcement(reinforcement); + sendMessage(player, ChatColor.GREEN, message); + } } } else { sendMessage(player, ChatColor.RED, "You are not permitted to modify this reinforcement"); @@ -209,7 +313,7 @@ public void interact(PlayerInteractEvent pie) { } catch(Exception e) { - Citadel.printStackTrace(e); + Citadel.printStackTrace(e); } } } diff --git a/src/com/untamedears/citadel/listener/WorldListener.java b/src/com/untamedears/citadel/listener/WorldListener.java new file mode 100644 index 00000000..057180a6 --- /dev/null +++ b/src/com/untamedears/citadel/listener/WorldListener.java @@ -0,0 +1,26 @@ +package com.untamedears.citadel.listener; + +import org.bukkit.block.BlockState; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.world.StructureGrowEvent; + +import com.untamedears.citadel.Citadel; +import com.untamedears.citadel.Citadel.VerboseMsg; +import com.untamedears.citadel.ReinforcementManager; + +public class WorldListener implements Listener { + @EventHandler(ignoreCancelled = true) + public void onStructureGrow(StructureGrowEvent event) { + ReinforcementManager rm = Citadel.getReinforcementManager(); + for (BlockState block_state : event.getBlocks()) { + if (rm.getReinforcement(block_state.getLocation()) != null) { + event.setCancelled(true); + Citadel.verbose( + VerboseMsg.ReinOvergrowth, + block_state.getLocation().toString()); + return; + } + } + } +}