From 0ce9cce9d3c07cce11d9d1adfc9bd4d0c7a19e64 Mon Sep 17 00:00:00 2001 From: Olof Larsson Date: Sat, 8 Oct 2011 22:03:44 +0200 Subject: [PATCH] In progress: Using MassiveCraftCore and Allman indentation style and minor refactoring. --- lib/PermissionsEx.jar | Bin 0 -> 129884 bytes lib/gson.jar | Bin 173590 -> 173590 bytes src/com/massivecraft/factions/Board.java | 31 +- src/com/massivecraft/factions/Conf.java | 66 +- src/com/massivecraft/factions/FPlayer.java | 550 +++++++------- src/com/massivecraft/factions/FPlayers.java | 110 +++ src/com/massivecraft/factions/Faction.java | 629 ++++++++-------- src/com/massivecraft/factions/Factions.java | 695 ++++-------------- src/com/massivecraft/factions/P.java | 512 +++++++++++++ src/com/massivecraft/factions/SaveTask.java | 2 +- .../factions/commands/FBaseCommand.java | 8 +- .../commands/FCommandAutoSafeclaim.java | 4 +- .../commands/FCommandAutoWarclaim.java | 4 +- .../factions/commands/FCommandBalance.java | 4 +- .../factions/commands/FCommandBypass.java | 8 +- .../factions/commands/FCommandConfig.java | 6 +- .../factions/commands/FCommandCreate.java | 4 +- .../factions/commands/FCommandDeposit.java | 4 +- .../factions/commands/FCommandDisband.java | 8 +- .../factions/commands/FCommandKick.java | 4 +- .../factions/commands/FCommandLock.java | 4 +- .../factions/commands/FCommandNoBoom.java | 4 +- .../factions/commands/FCommandOwner.java | 4 +- .../factions/commands/FCommandOwnerList.java | 4 +- .../factions/commands/FCommandPay.java | 4 +- .../factions/commands/FCommandPeaceful.java | 4 +- .../factions/commands/FCommandPermanent.java | 4 +- .../factions/commands/FCommandPower.java | 4 +- .../factions/commands/FCommandReload.java | 20 +- .../factions/commands/FCommandSafeclaim.java | 4 +- .../commands/FCommandSafeunclaimall.java | 4 +- .../factions/commands/FCommandSaveAll.java | 6 +- .../factions/commands/FCommandSethome.java | 6 +- .../factions/commands/FCommandUnclaim.java | 6 +- .../factions/commands/FCommandVersion.java | 4 +- .../factions/commands/FCommandWarclaim.java | 4 +- .../commands/FCommandWarunclaimall.java | 4 +- .../factions/commands/FCommandWithdraw.java | 4 +- .../factions/commands/FRelationCommand.java | 4 +- .../factions/integration/Econ.java | 169 +++-- .../integration/EssentialsFeatures.java | 30 +- .../factions/integration/SpoutFeatures.java | 182 +++-- .../factions/integration/Worldguard.java | 48 +- .../listeners/FactionsBlockListener.java | 172 +++-- .../listeners/FactionsChatEarlyListener.java | 54 +- .../listeners/FactionsEntityListener.java | 351 ++++++--- .../listeners/FactionsPlayerListener.java | 412 +++++++---- .../listeners/FactionsServerListener.java | 40 +- .../factions/struct/Relation.java | 109 ++- .../massivecraft/factions/util/DiscUtil.java | 33 - .../factions/util/EntityUtil.java | 18 - .../massivecraft/factions/util/JarLoader.java | 59 -- .../util/MapFLocToStringSetTypeAdapter.java | 50 +- .../massivecraft/factions/util/MiscUtil.java | 47 +- .../factions/util/MyLocationTypeAdapter.java | 32 +- .../massivecraft/factions/util/TextUtil.java | 88 --- .../factions/zcore/CommandVisibility.java | 9 + .../massivecraft/factions/zcore/MCommand.java | 429 +++++++++++ .../massivecraft/factions/zcore/MPlugin.java | 236 ++++++ .../zcore/MPluginSecretPlayerListener.java | 55 ++ .../zcore/MPluginSecretServerListener.java | 26 + .../factions/zcore/persist/EM.java | 74 ++ .../factions/zcore/persist/Entity.java | 65 ++ .../zcore/persist/EntityCollection.java | 250 +++++++ .../factions/zcore/persist/PlayerEntity.java | 44 ++ .../zcore/persist/PlayerEntityCollection.java | 46 ++ .../factions/zcore/persist/SaveTask.java | 9 + .../factions/zcore/util/ClassLoadHack.java | 51 ++ .../factions/zcore/util/DiscUtil.java | 78 ++ .../factions/zcore/util/LibLoader.java | 50 ++ .../factions/zcore/util/PermUtil.java | 128 ++++ .../factions/zcore/util/Persist.java | 154 ++++ .../factions/zcore/util/TextUtil.java | 245 ++++++ .../factions/zcore/util/WorldUtil.java | 39 + src/plugin.yml | 9 +- 75 files changed, 4605 insertions(+), 2033 deletions(-) create mode 100644 lib/PermissionsEx.jar create mode 100644 src/com/massivecraft/factions/FPlayers.java create mode 100644 src/com/massivecraft/factions/P.java delete mode 100644 src/com/massivecraft/factions/util/DiscUtil.java delete mode 100644 src/com/massivecraft/factions/util/EntityUtil.java delete mode 100644 src/com/massivecraft/factions/util/JarLoader.java delete mode 100644 src/com/massivecraft/factions/util/TextUtil.java create mode 100644 src/com/massivecraft/factions/zcore/CommandVisibility.java create mode 100644 src/com/massivecraft/factions/zcore/MCommand.java create mode 100644 src/com/massivecraft/factions/zcore/MPlugin.java create mode 100644 src/com/massivecraft/factions/zcore/MPluginSecretPlayerListener.java create mode 100644 src/com/massivecraft/factions/zcore/MPluginSecretServerListener.java create mode 100644 src/com/massivecraft/factions/zcore/persist/EM.java create mode 100644 src/com/massivecraft/factions/zcore/persist/Entity.java create mode 100644 src/com/massivecraft/factions/zcore/persist/EntityCollection.java create mode 100644 src/com/massivecraft/factions/zcore/persist/PlayerEntity.java create mode 100644 src/com/massivecraft/factions/zcore/persist/PlayerEntityCollection.java create mode 100644 src/com/massivecraft/factions/zcore/persist/SaveTask.java create mode 100644 src/com/massivecraft/factions/zcore/util/ClassLoadHack.java create mode 100644 src/com/massivecraft/factions/zcore/util/DiscUtil.java create mode 100644 src/com/massivecraft/factions/zcore/util/LibLoader.java create mode 100644 src/com/massivecraft/factions/zcore/util/PermUtil.java create mode 100644 src/com/massivecraft/factions/zcore/util/Persist.java create mode 100644 src/com/massivecraft/factions/zcore/util/TextUtil.java create mode 100644 src/com/massivecraft/factions/zcore/util/WorldUtil.java diff --git a/lib/PermissionsEx.jar b/lib/PermissionsEx.jar new file mode 100644 index 0000000000000000000000000000000000000000..f2b1685397a9cfa7ac808df81f544f1e363d241a GIT binary patch literal 129884 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 literal 0 HcmV?d00001 diff --git a/lib/gson.jar b/lib/gson.jar index acd16c0646bc254fdca4ae1da41671a2c1d04d73..eb542743c52e00c69bcd995863560fb2ae31dc7f 100644 GIT binary patch delta 2279 zcmXApc~sS98pnMeF8d;I$8bBNnM%xPWk+eTTslG`X6~qo#Z)9JMqE@x5d>5)K=1-D zn|jsX?<6f}L{I(Ff-;XxDU};(GGkfl%*l>qI*vM%&i8q`f85V`*7sT7-&@xiQr8(0 zIz8FX71B$7E!CmJX;15;Swxn1H{Fl_BlIBI-m5ec{{#66{LkfhvP`vy-CiFLf#NWa zCVT%aewBbluDOEu{SyTL4M-F`6SSMj+J8CZlAtp@RFr2U4*3C%(S!V;JlN+ahGh3m z5!-M5BK(m#Y|wSG_YE#^K~*?ZurF?@2+Kwm33^6-DJj~<#0CPFo@y1zOsEP19!xqa z()Fo*kZFJL%o~#a?(BOKcXxib*v_TiQ>2X$WnlU#k-9T(2~KCWN!(V?MzQtF{Z4Ru z?QyXkEgS>vD|uaXS*7a)J>{Q?X1MakO0Y|4g7qMc@Bf z`T-3&zfGZF$s0JGJ`(-m8T5(dE1pG_Xv&G6OAljdCas#?X7~<@G=r1Lks+kOzI3H%arBOZHUEdC|#=ksemSgL;iCGMN9T^m4pR@Uh0r=|61xn zmFC|~bPMXM%`}w5t@Mg$@) zc2J9O{84%bdvLCF(se2JyAyPhw5oNkyRC;%)qL+E{}{8kn*z-ECXO;cpQ2-M{O~lr zEnU9+4aLb&D!OT)9Psu9ijbKfxJa?#NPOWM-I2t9zex{ED`B^(9K&-O{5c7qhDqCw z%TwPG=>$Y@qBuMk$*sbB(d_F_mRY|8OS#w^OUa7lQ0J?W+#}}pM|g%b5ILGNq=CG6 z_Q`nzCUJnsLlXJ4@IR9|LHKks$B6%&X?!ynoH~yuioS9IXA1wfkW+-SU*u-twKQHV zT<{9#x*}|h{E3Sp-R;z?*Kt2{!KVAnR~E-PgEIJYnft$%FiTlimhlwGrq#y1PFl`2 ze$-;lzkz3JkLi=m6Uj0u**pp;%jU=7_*phTO?I7!lORm-a3V~2&k`V=@$e*IU=Gg# zvU4~UIG)3^fnh5Yc`JAc0xqq9i)F^HHRgF2|Tb?rJA*z11UJ4JwSfG z+OFhlmO1PAH7MKGX|kaOyckkNfl4Kj;+J=;_TB51vY~Z+r3lv-Ek({N(_!r?<7XlLQpVGO@tak-eY3{lF_Y=y8IwV_zo{d@`{g#{ z%XMwF<(wiJhmpIk0&BHQVg*lyueXB#0_kRju61@LzXIt%rS2?d3(tbf(kT+Fo0x6Ad!C(_sJY=uMv#4{TlAb(bS{s147EblN4bO`J~~ zF7&}>n=T(Ju}%C=T}<&#y)MS%);qG8tyL5UtcWFTKZiVl6;Hhw%APXNizF-T5 V+h&`kx6x&5Z>hE(iZ|ia{{x5mR)GKj delta 2279 zcmX9;X;77A6!si0`yz5P+$POTqBOHnS}d1FNW{#2ODtMQzAH$8iYS7J3I+&X;AK;< z3R9L%qGMlLQ0hpfRBoh6S(aLpjYOKMmG+$D{c)dp&icO3dB0QH8c^995I8B$%Ms8; zehbS22U1Goqp3uedmG)0|3mZux!jj&F#h}S6ZoIOQRFgZ>FjiSxIYXBc^J8NU+32d zFwZeVV2^jSz&}1Q0?+tuW5C6L3ryDT*5E)fo(|dX1y&Q**9&Z4_unL>p~p<|{ns5+^6U`yJ5L{0asZ+|AsbQVDV=g*viJ)(W61ke6{ zjFhdtToXnvJBp|pR)h221az)rr+wBz5n`MCCP+_T>V1~|H&bo^h zqmJEqKb^%Zu#ZO2CqCe%57SQ&*-nq5M{q>D{xLcs_RNu#BKBXR=~1!wdX|2|h3x;v zlfTsUokSl=or9C<6Y;N_N+r0I9X5l8AkQR}vC|CPOrfS<969Wyc$(l3_Xmmeg{0-q zrxD)3kCUl2h%8f>MK`3%GD>v7{96u<4hB9{Okq-eWC_WD?U8P35$;_<9XO@=tCp^V zUaq479PCOg9(i?pdeI@g_~Gux9^s~6B@Z-nH`CXM8RVhSP{~f-N{J%S$p&gBf61xt z(e375^M}Q~%}&F;?PG7zL1|L4ouVbOeFqH>0Cux!o?JckV>;r4!##D7W{JO|g|-Wh zI!y0MhfA$=MGpJJF*;6qsuhk~jr(z`+3x+`;bvz$`Iu3)+|B%cf{vijhbQSB5#^O{ zC{jczZl^xd;GJ_6BAV|#PZ7Z=c&?LfiaOt2qao5L=mr%*JiFSPWASO2giUxnRV|^m zPYB0Iz;mJ8D0n-JJ-raHax;c1r(<7;|M&E#!r*0GM!^V#;0=(0Q)Q&a4Mb00{Ud|bU<1L#{-UL z@HD{vixjdJu?2Qv5kf38Vlj^c%U{e1U>6r_!q_GJB8>G*_!WR(CeH)Bp2@QSpJws` zz~H3{HA^`O%#p>309O{f0H?ClH+C7Pg6&zx$$BMa`p<$NtYeKpTO$d1)|>7X?L%Pd%fp;*RVqnZV+)$X}#`3)HVT+36C5M7|Y zngXQjMw==93RSGP3;9{FKMHvwVAMJ_Zd#{#SZ1Ow)|d#{{H98P{pB>HigauhMJ%PT z!km@G7^`Jsig^Mo?qYr!aIILUG_8bR1>0MqEPOpr1#_*(%q;WudTo49sSY`#R2zR* z%5m^H%lJ7&VslC4p)w8Wy#YqcByHeHfPEYI1%%w~MnB6eb%R-^&CQcxj40Pe%gR-? zCcDf2} zEc0Nsc3WPJhv_nBt96VMYxpIw_iHrEuNJ*66JLw^mie$2m$Xb^ouQs4b z6sHN_$WOvFWuxk~bE77EZ_=A$rO111+oYEov{^5OH6oI>-GNAt`f@$$YxC$yqPFN@ zR&CMe_4^hrHf}544yLvB&a}2_Lfke5&mF+3a2mgQeBAhg>vcLC>s22|gWfH^L3?8- Gy!Jn~J{0`` diff --git a/src/com/massivecraft/factions/Board.java b/src/com/massivecraft/factions/Board.java index 2d037619..b5fa9504 100644 --- a/src/com/massivecraft/factions/Board.java +++ b/src/com/massivecraft/factions/Board.java @@ -23,8 +23,9 @@ import com.massivecraft.factions.util.DiscUtil; import com.massivecraft.factions.util.TextUtil; -public class Board { - private static transient File file = new File(Factions.instance.getDataFolder(), "board.json"); +public class Board +{ + private static transient File file = new File(P.p.getDataFolder(), "board.json"); private static transient HashMap flocationIds = new HashMap(); //----------------------------------------------// @@ -114,7 +115,7 @@ public class Board { while (iter.hasNext()) { Entry entry = iter.next(); if ( ! Faction.exists(entry.getValue())) { - Factions.log("Board cleaner removed "+entry.getValue()+" from "+entry.getKey()); + P.log("Board cleaner removed "+entry.getValue()+" from "+entry.getKey()); iter.remove(); } } @@ -282,10 +283,10 @@ public class Board { //Factions.log("Saving board to disk"); try { - DiscUtil.write(file, Factions.instance.gson.toJson(dumpAsSaveFormat())); + DiscUtil.write(file, P.p.gson.toJson(dumpAsSaveFormat())); } catch (Exception e) { e.printStackTrace(); - Factions.log("Failed to save the board to disk."); + P.log("Failed to save the board to disk."); return false; } @@ -293,35 +294,35 @@ public class Board { } public static boolean load() { - Factions.log("Loading board from disk"); + P.log("Loading board from disk"); if ( ! file.exists()) { if ( ! loadOld()) - Factions.log("No board to load from disk. Creating new file."); + P.log("No board to load from disk. Creating new file."); save(); return true; } try { Type type = new TypeToken>>(){}.getType(); - Map> worldCoordIds = Factions.instance.gson.fromJson(DiscUtil.read(file), type); + Map> worldCoordIds = P.p.gson.fromJson(DiscUtil.read(file), type); loadFromSaveFormat(worldCoordIds); } catch (Exception e) { e.printStackTrace(); - Factions.log("Failed to load the board from disk."); + P.log("Failed to load the board from disk."); return false; } return true; } - private static boolean loadOld() { - File folderBoard = new File(Factions.instance.getDataFolder(), "board"); + /*private static boolean loadOld() { + File folderBoard = new File(P.p.getDataFolder(), "board"); if ( ! folderBoard.isDirectory()) return false; - Factions.log("Board file doesn't exist, attempting to load old pre-1.1 data."); + P.log("Board file doesn't exist, attempting to load old pre-1.1 data."); String ext = ".json"; @@ -350,14 +351,14 @@ public class Board { int factionId = coordDat.get(1).getAsInt(); flocationIds.put(new FLocation(name, coordX, coordZ), factionId); } - Factions.log("loaded pre-1.1 board "+name); + P.log("loaded pre-1.1 board "+name); } catch (Exception e) { e.printStackTrace(); - Factions.log(Level.WARNING, "failed to load board "+name); + P.log(Level.WARNING, "failed to load board "+name); } } return true; - } + }*/ } diff --git a/src/com/massivecraft/factions/Conf.java b/src/com/massivecraft/factions/Conf.java index fbb6ddc4..b5357dff 100644 --- a/src/com/massivecraft/factions/Conf.java +++ b/src/com/massivecraft/factions/Conf.java @@ -1,27 +1,28 @@ package com.massivecraft.factions; -import java.io.File; import java.util.*; import org.bukkit.*; import org.bukkit.entity.CreatureType; -import com.massivecraft.factions.util.DiscUtil; - - -public class Conf { - public static final transient File file = new File(Factions.instance.getDataFolder(), "conf.json"); +public class Conf +{ + // track players with admin access who have enabled "admin bypass" mode, and should therefore be able to build anywhere + // not worth saving between server restarts, I think + public static transient Set adminBypassPlayers = Collections.synchronizedSet(new HashSet()); // Colors public static ChatColor colorMember = ChatColor.GREEN; public static ChatColor colorAlly = ChatColor.LIGHT_PURPLE; public static ChatColor colorNeutral = ChatColor.WHITE; public static ChatColor colorEnemy = ChatColor.RED; - + /* public static ChatColor colorSystem = ChatColor.YELLOW; public static ChatColor colorChrome = ChatColor.GOLD; public static ChatColor colorCommand = ChatColor.AQUA; public static ChatColor colorParameter = ChatColor.DARK_AQUA; + */ + // Power public static double powerPlayerMax = 10.0; public static double powerPlayerMin = -10.0; @@ -39,10 +40,10 @@ public class Conf { public static int factionTagLengthMax = 10; public static boolean factionTagForceUpperCase = false; - public static boolean newFactionsDefaultOpen = true; + public static boolean newFactionsDefaultOpen = false; // what faction ID to start new players in when they first join the server; default is 0, "no faction" - public static int newPlayerStartingFactionID = 0; + public static String newPlayerStartingFactionID = "0"; public static boolean showMapFactionKey = true; public static boolean showNeutralFactionsOnMap = true; @@ -183,7 +184,7 @@ public class Conf { // Spout features public static boolean spoutFactionTagsOverNames = true; public static boolean spoutFactionTitlesOverNames = true; - public static boolean spoutFactionAdminCapes = true; + public static boolean spoutFactionAdminCapes = true; // TODO: What are these for? public static boolean spoutFactionModeratorCapes = true; public static int spoutTerritoryDisplayPosition = 3; public static String capeAlly = "https://github.com/MassiveCraft/Factions/raw/master/capes/ally.png"; @@ -236,7 +237,8 @@ public class Conf { public static transient int mapWidth = 39; public static transient char[] mapKeyChrs = "\\/#?$%=&^ABCDEFGHJKLMNOPQRSTUVWXYZ1234567890abcdeghjmnopqrsuvwxyz".toCharArray(); - static { + static + { territoryEnemyDenyCommands.add("home"); territoryEnemyDenyCommands.add("sethome"); territoryEnemyDenyCommands.add("spawn"); @@ -281,45 +283,17 @@ public class Conf { safeZoneNerfedCreatureTypes.add(CreatureType.ZOMBIE); } - // track players with admin access who have enabled "admin bypass" mode, and should therefore be able to build anywhere - // not worth saving between server restarts, I think - public static transient Set adminBypassPlayers = Collections.synchronizedSet(new HashSet()); - // -------------------------------------------- // // Persistance // -------------------------------------------- // - - public static boolean save() { - //Factions.log("Saving config to disk."); - - try { - DiscUtil.write(file, Factions.instance.gson.toJson(new Conf())); - } catch (Exception e) { - e.printStackTrace(); - Factions.log("Failed to save the config to disk."); - return false; - } - return true; + private static transient Conf i = new Conf(); + public static void load() + { + P.p.persist.loadOrSaveDefault(i, Conf.class, "conf"); } - - public static boolean load() { - Factions.log("Loading conf from disk"); - - if ( ! file.exists()) { - Factions.log("No conf to load from disk. Creating new file."); - save(); - return true; - } - - try { - Factions.instance.gson.fromJson(DiscUtil.read(file), Conf.class); - } catch (Exception e) { - e.printStackTrace(); - Factions.log("Failed to load the config from disk."); - return false; - } - - return true; + public static void save() + { + P.p.persist.save(i); } } diff --git a/src/com/massivecraft/factions/FPlayer.java b/src/com/massivecraft/factions/FPlayer.java index 813aed4d..cd60d21c 100644 --- a/src/com/massivecraft/factions/FPlayer.java +++ b/src/com/massivecraft/factions/FPlayer.java @@ -17,7 +17,8 @@ import com.massivecraft.factions.integration.Worldguard; import com.massivecraft.factions.struct.ChatMode; import com.massivecraft.factions.struct.Relation; import com.massivecraft.factions.struct.Role; -import com.massivecraft.factions.util.DiscUtil; +import com.massivecraft.factions.zcore.persist.Entity; +import com.massivecraft.factions.zcore.persist.PlayerEntity; /** @@ -31,19 +32,20 @@ import com.massivecraft.factions.util.DiscUtil; * This means you can use the == operator. No .equals method necessary. */ -public class FPlayer { +public class FPlayer extends PlayerEntity +{ // -------------------------------------------- // // Fields // -------------------------------------------- // - private static transient TreeMap instances = new TreeMap(String.CASE_INSENSITIVE_ORDER); - private static transient File file = new File(Factions.instance.getDataFolder(), "players.json"); + //private static transient TreeMap instances = new TreeMap(String.CASE_INSENSITIVE_ORDER); + //private static transient File file = new File(P.p.getDataFolder(), "players.json"); - private transient String playerName; + //private transient String playerName; private transient FLocation lastStoodAt = new FLocation(); // Where did this player stand the last time we checked? - private int factionId; + private String factionId; private Role role; private String title; private double power; @@ -62,7 +64,8 @@ public class FPlayer { // -------------------------------------------- // // GSON need this noarg constructor. - public FPlayer() { + public FPlayer() + { this.resetFactionData(); this.power = this.getPowerMax(); this.lastPowerUpdateTime = System.currentTimeMillis(); @@ -74,165 +77,175 @@ public class FPlayer { this.loginPvpDisabled = (Conf.noPVPDamageToOthersForXSecondsAfterLogin > 0) ? true : false; this.deleteMe = false; - if (Conf.newPlayerStartingFactionID > 0 && Faction.exists(Conf.newPlayerStartingFactionID)) { + if ( ! Conf.newPlayerStartingFactionID.equals("0") && Factions.i.exists(Conf.newPlayerStartingFactionID)) + { this.factionId = Conf.newPlayerStartingFactionID; } } - public void resetFactionData() { + public void resetFactionData() + { // clean up any territory ownership in old faction, if there is one - if (this.factionId > 0 && Faction.exists(this.factionId)) { - Faction.get(factionId).clearClaimOwnership(playerName); + if (this.factionId > 0 && Factions.i.exists(this.factionId)) + { + // TODO: Get faction function... + Factions.i.get(factionId).clearClaimOwnership(this.getId()); } - this.factionId = 0; // The default neutral faction + this.factionId = "0"; // The default neutral faction this.chatMode = ChatMode.PUBLIC; this.role = Role.NORMAL; this.title = ""; this.autoClaimEnabled = false; - if (playerName != null && !playerName.isEmpty()) { - SpoutFeatures.updateAppearances(this.getPlayer()); - } - } - - // -------------------------------------------- // - // Minecraft Player - // -------------------------------------------- // - - public Player getPlayer() { - return Factions.instance.getServer().getPlayer(playerName); - } - - public String getPlayerName() { - return this.playerName; - } - - public boolean isOnline() { - return Factions.instance.getServer().getPlayer(playerName) != null; - } - - public boolean isOffline() { - return ! isOnline(); + SpoutFeatures.updateAppearances(this.getPlayer()); } // -------------------------------------------- // // Getters And Setters // -------------------------------------------- // - public Faction getFaction() { + public Faction getFaction() + { return Faction.get(factionId); } - public int getFactionId() { - return factionId; + public String getFactionId() + { + return this.factionId; } - public void setFaction(Faction faction) { + public void setFaction(Faction faction) + { this.factionId = faction.getId(); SpoutFeatures.updateAppearances(this.getPlayer()); } - public boolean hasFaction() { - return factionId != 0; + public boolean hasFaction() + { + return ! factionId.equals("0"); } - public Role getRole() { + public Role getRole() + { return this.role; } - public void setRole(Role role) { + public void setRole(Role role) + { this.role = role; SpoutFeatures.updateAppearances(this.getPlayer()); } - public ChatMode getChatMode() { - if(this.factionId == 0 ) { + public ChatMode getChatMode() + { + if(this.factionId.equals("0")) + { return ChatMode.PUBLIC; } return chatMode; } - public void setChatMode(ChatMode chatMode) { + public void setChatMode(ChatMode chatMode) + { this.chatMode = chatMode; } - public long getLastLoginTime() { + public long getLastLoginTime() + { return lastLoginTime; } - public boolean autoClaimEnabled() { - if (this.factionId == 0) - return false; + public boolean autoClaimEnabled() + { + if (this.factionId.equals("0")) return false; return autoClaimEnabled; } - public void enableAutoClaim(boolean enabled) { + public void enableAutoClaim(boolean enabled) + { this.autoClaimEnabled = enabled; - if (enabled) { + if (enabled) + { this.autoSafeZoneEnabled = false; this.autoWarZoneEnabled = false; } } - public boolean autoSafeZoneEnabled() { + public boolean autoSafeZoneEnabled() + { return autoSafeZoneEnabled; } - public void enableAutoSafeZone(boolean enabled) { + public void enableAutoSafeZone(boolean enabled) + { this.autoSafeZoneEnabled = enabled; - if (enabled) { + if (enabled) + { this.autoClaimEnabled = false; this.autoWarZoneEnabled = false; } } - public boolean autoWarZoneEnabled() { + public boolean autoWarZoneEnabled() + { return autoWarZoneEnabled; } - public void enableAutoWarZone(boolean enabled) { + public void enableAutoWarZone(boolean enabled) + { this.autoWarZoneEnabled = enabled; - if (enabled) { + if (enabled) + { this.autoClaimEnabled = false; this.autoSafeZoneEnabled = false; } } - public void setLastLoginTime(long lastLoginTime) { + public void setLastLoginTime(long lastLoginTime) + { losePowerFromBeingOffline(); this.lastLoginTime = lastLoginTime; this.lastPowerUpdateTime = lastLoginTime; - if (Conf.noPVPDamageToOthersForXSecondsAfterLogin > 0) { + if (Conf.noPVPDamageToOthersForXSecondsAfterLogin > 0) + { this.loginPvpDisabled = true; } } - public boolean isMapAutoUpdating() { + public boolean isMapAutoUpdating() + { return mapAutoUpdating; } - public void setMapAutoUpdating(boolean mapAutoUpdating) { + public void setMapAutoUpdating(boolean mapAutoUpdating) + { this.mapAutoUpdating = mapAutoUpdating; } - public boolean hasLoginPvpDisabled() { - if (!loginPvpDisabled) { + public boolean hasLoginPvpDisabled() + { + if (!loginPvpDisabled) + { return false; } - if (this.lastLoginTime + (Conf.noPVPDamageToOthersForXSecondsAfterLogin * 1000) < System.currentTimeMillis()) { + if (this.lastLoginTime + (Conf.noPVPDamageToOthersForXSecondsAfterLogin * 1000) < System.currentTimeMillis()) + { this.loginPvpDisabled = false; return false; } return true; } - public FLocation getLastStoodAt() { + public FLocation getLastStoodAt() + { return this.lastStoodAt; } - public void setLastStoodAt(FLocation flocation) { + public void setLastStoodAt(FLocation flocation) + { this.lastStoodAt = flocation; } - public void markForDeletion(boolean delete) { + public void markForDeletion(boolean delete) + { deleteMe = delete; } @@ -242,20 +255,25 @@ public class FPlayer { // Base: - public String getTitle() { + public String getTitle() + { return this.title; } - public void setTitle(String title) { + public void setTitle(String title) + { this.title = title; } - public String getName() { - return this.playerName; + public String getName() + { + return this.getId(); // TODO: ... display name or remove completeley } - public String getTag() { - if ( ! this.hasFaction()) { + public String getTag() + { + if ( ! this.hasFaction()) + { return ""; } return this.getFaction().getTag(); @@ -263,7 +281,8 @@ public class FPlayer { // Base concatenations: - public String getNameAndSomething(String something) { + public String getNameAndSomething(String something) + { String ret = this.role.getPrefix(); if (something.length() > 0) { ret += something+" "; @@ -272,32 +291,39 @@ public class FPlayer { return ret; } - public String getNameAndTitle() { + public String getNameAndTitle() + { return this.getNameAndSomething(this.getTitle()); } - public String getNameAndTag() { + public String getNameAndTag() + { return this.getNameAndSomething(this.getTag()); } // Colored concatenations: // These are used in information messages - public String getNameAndTitle(Faction faction) { + public String getNameAndTitle(Faction faction) + { return this.getRelationColor(faction)+this.getNameAndTitle(); } - public String getNameAndTitle(FPlayer fplayer) { + public String getNameAndTitle(FPlayer fplayer) + { return this.getRelationColor(fplayer)+this.getNameAndTitle(); } - public String getNameAndTag(Faction faction) { + public String getNameAndTag(Faction faction) + { return this.getRelationColor(faction)+this.getNameAndTag(); } - public String getNameAndTag(FPlayer fplayer) { + public String getNameAndTag(FPlayer fplayer) + { return this.getRelationColor(fplayer)+this.getNameAndTag(); } - public String getNameAndRelevant(Faction faction) { + public String getNameAndRelevant(Faction faction) + { // Which relation? Relation rel = this.getRelation(faction); @@ -309,14 +335,16 @@ public class FPlayer { // For non members we show tag return rel.getColor() + this.getNameAndTag(); } - public String getNameAndRelevant(FPlayer fplayer) { + public String getNameAndRelevant(FPlayer fplayer) + { return getNameAndRelevant(fplayer.getFaction()); } // Chat Tag: // These are injected into the format of global chat messages. - public String getChatTag() { + public String getChatTag() + { if ( ! this.hasFaction()) { return ""; } @@ -325,14 +353,17 @@ public class FPlayer { } // Colored Chat Tag - public String getChatTag(Faction faction) { + public String getChatTag(Faction faction) + { if ( ! this.hasFaction()) { return ""; } return this.getRelation(faction).getColor()+getChatTag(); } - public String getChatTag(FPlayer fplayer) { + + public String getChatTag(FPlayer fplayer) + { if ( ! this.hasFaction()) { return ""; } @@ -344,23 +375,28 @@ public class FPlayer { // Relation and relation colors // ------------------------------- - public Relation getRelation(Faction faction) { + public Relation getRelation(Faction faction) + { return faction.getRelation(this); } - public Relation getRelation(FPlayer fplayer) { + public Relation getRelation(FPlayer fplayer) + { return this.getFaction().getRelation(fplayer); } - public Relation getRelationToLocation() { + public Relation getRelationToLocation() + { return Board.getFactionAt(new FLocation(this)).getRelation(this); } - public ChatColor getRelationColor(Faction faction) { + public ChatColor getRelationColor(Faction faction) + { return faction.getRelationColor(this); } - public ChatColor getRelationColor(FPlayer fplayer) { + public ChatColor getRelationColor(FPlayer fplayer) + { return this.getRelation(fplayer).getColor(); } @@ -368,9 +404,11 @@ public class FPlayer { //----------------------------------------------// // Health //----------------------------------------------// - public void heal(int amnt) { + public void heal(int amnt) + { Player player = this.getPlayer(); - if (player == null) { + if (player == null) + { return; } player.setHealth(player.getHealth() + amnt); @@ -380,45 +418,57 @@ public class FPlayer { //----------------------------------------------// // Power //----------------------------------------------// - public double getPower() { + public double getPower() + { this.updatePower(); return this.power; } - protected void alterPower(double delta) { + protected void alterPower(double delta) + { this.power += delta; - if (this.power > this.getPowerMax()) { + if (this.power > this.getPowerMax()) + { this.power = this.getPowerMax(); - } else if (this.power < this.getPowerMin()) { + } else if (this.power < this.getPowerMin()) + { this.power = this.getPowerMin(); } //Log.debug("Power of "+this.getName()+" is now: "+this.power); } - public double getPowerMax() { + public double getPowerMax() + { return Conf.powerPlayerMax; } - public double getPowerMin() { + public double getPowerMin() + { return Conf.powerPlayerMin; } - public int getPowerRounded() { + public int getPowerRounded() + { return (int) Math.round(this.getPower()); } - public int getPowerMaxRounded() { + public int getPowerMaxRounded() + { return (int) Math.round(this.getPowerMax()); } - public int getPowerMinRounded() { + public int getPowerMinRounded() + { return (int) Math.round(this.getPowerMin()); } - protected void updatePower() { - if (this.isOffline()) { + protected void updatePower() + { + if (this.isOffline()) + { losePowerFromBeingOffline(); - if (!Conf.powerRegenOffline) { + if (!Conf.powerRegenOffline) + { return; } } @@ -430,21 +480,25 @@ public class FPlayer { this.alterPower(millisPassed * Conf.powerPerMinute / millisPerMinute); } - protected void losePowerFromBeingOffline() { - if (Conf.powerOfflineLossPerDay > 0.0 && this.power > Conf.powerOfflineLossLimit) { + protected void losePowerFromBeingOffline() + { + if (Conf.powerOfflineLossPerDay > 0.0 && this.power > Conf.powerOfflineLossLimit) + { long now = System.currentTimeMillis(); long millisPassed = now - this.lastPowerUpdateTime; this.lastPowerUpdateTime = now; double loss = millisPassed * Conf.powerOfflineLossPerDay / (24*60*60*1000); - if (this.power - loss < Conf.powerOfflineLossLimit) { + if (this.power - loss < Conf.powerOfflineLossLimit) + { loss = this.power; } this.alterPower(-loss); } } - public void onDeath() { + public void onDeath() + { this.updatePower(); this.alterPower(-Conf.powerPerDeath); } @@ -452,34 +506,42 @@ public class FPlayer { //----------------------------------------------// // Territory //----------------------------------------------// - public boolean isInOwnTerritory() { + public boolean isInOwnTerritory() + { return Board.getFactionAt(new FLocation(this)) == this.getFaction(); } - public boolean isInOthersTerritory() { + public boolean isInOthersTerritory() + { int idHere = Board.getIdAt(new FLocation(this)); return idHere > 0 && idHere != this.factionId; } - public boolean isInAllyTerritory() { + public boolean isInAllyTerritory() + { return Board.getFactionAt(new FLocation(this)).getRelation(this).isAlly(); } - public boolean isInNeutralTerritory() { + public boolean isInNeutralTerritory() + { return Board.getFactionAt(new FLocation(this)).getRelation(this).isNeutral(); } - public boolean isInEnemyTerritory() { + public boolean isInEnemyTerritory() + { return Board.getFactionAt(new FLocation(this)).getRelation(this).isEnemy(); } - public void sendFactionHereMessage() { - if (SpoutFeatures.updateTerritoryDisplay(this)) { + public void sendFactionHereMessage() + { + if (SpoutFeatures.updateTerritoryDisplay(this)) + { return; } Faction factionHere = Board.getFactionAt(new FLocation(this)); - String msg = Conf.colorSystem+" ~ "+factionHere.getTag(this); - if (factionHere.getDescription().length() > 0) { + String msg = P.p.txt.parse("")+" ~ "+factionHere.getTag(this); + if (factionHere.getDescription().length() > 0) + { msg += " - "+factionHere.getDescription(); } this.sendMessage(msg); @@ -489,22 +551,26 @@ public class FPlayer { // Actions // ------------------------------- - public void leave(boolean makePay) { + public void leave(boolean makePay) + { Faction myFaction = this.getFaction(); boolean perm = myFaction.isPermanent(); - if (!perm && this.getRole() == Role.ADMIN && myFaction.getFPlayers().size() > 1) { + if (!perm && this.getRole() == Role.ADMIN && myFaction.getFPlayers().size() > 1) + { sendMessage("You must give the admin role to someone else first."); return; } - if (!Conf.CanLeaveWithNegativePower && this.getPower() < 0) { + if (!Conf.CanLeaveWithNegativePower && this.getPower() < 0) + { sendMessage("You cannot leave until your power is positive."); return; } // if economy is enabled and they're not on the bypass list, make 'em pay - if (makePay && Econ.enabled() && !Conf.adminBypassPlayers.contains(this.playerName)) { + if (makePay && Econ.enabled() && !Conf.adminBypassPlayers.contains(this.getId())) + { double cost = Conf.econCostLeave; // pay up if (cost > 0.0) { @@ -516,29 +582,35 @@ public class FPlayer { sendMessage("You have paid "+costString+" to leave your faction."); } // wait... we pay you to leave? - else if (cost < 0.0) { + else if (cost < 0.0) + { String costString = Econ.moneyString(-cost); Econ.addMoney(this.getName(), -cost); sendMessage("You have been paid "+costString+" for leaving your faction."); } } - if (myFaction.isNormal()) { - myFaction.sendMessage(this.getNameAndRelevant(myFaction) + Conf.colorSystem + " left your faction."); + if (myFaction.isNormal()) + { + myFaction.sendMessage(P.p.txt.parse(this.getNameAndRelevant(myFaction) + " left your faction.")); } this.resetFactionData(); - if (myFaction.isNormal() && !perm && myFaction.getFPlayers().isEmpty()) { + if (myFaction.isNormal() && !perm && myFaction.getFPlayers().isEmpty()) + { // Remove this faction - for (FPlayer fplayer : FPlayer.getAllOnline()) { - fplayer.sendMessage("The faction "+myFaction.getTag(fplayer)+Conf.colorSystem+" was disbanded."); + for (FPlayer fplayer : FPlayers.i.getOnline()) + { + fplayer.sendMessage(P.p.txt.parse("The faction "+myFaction.getTag(fplayer)+" was disbanded.")); } - Faction.delete(myFaction.getId()); + //Faction.delete(myFaction.getId()); + this.detach(); } } - public boolean attemptClaim(boolean notifyFailure) { + public boolean attemptClaim(boolean notifyFailure) + { // notifyFailure is false if called by auto-claim; no need to notify on every failure for it // return value is false on failure, true on success @@ -547,63 +619,74 @@ public class FPlayer { FLocation flocation = new FLocation(loc); Faction otherFaction = Board.getFactionAt(flocation); - if (Conf.worldGuardChecking && Worldguard.checkForRegionsInChunk(loc)) { + if (Conf.worldGuardChecking && Worldguard.checkForRegionsInChunk(loc)) + { // Checks for WorldGuard regions in the chunk attempting to be claimed sendMessage("This land is protected"); return false; } - if (myFaction == otherFaction) { + if (myFaction == otherFaction) + { if (notifyFailure) sendMessage("You already own this land."); return false; } - if (this.getRole().value < Role.MODERATOR.value) { + if (this.getRole().value < Role.MODERATOR.value) + { sendMessage("You must be "+Role.MODERATOR+" to claim land."); return false; } - if (myFaction.getFPlayers().size() < Conf.claimsRequireMinFactionMembers && !Conf.adminBypassPlayers.contains(this.playerName)) { + if (myFaction.getFPlayers().size() < Conf.claimsRequireMinFactionMembers && !Conf.adminBypassPlayers.contains(this.getId())) + { sendMessage("Your faction must have at least "+Conf.claimsRequireMinFactionMembers+" members to claim land."); return false; } - if (Conf.worldsNoClaiming.contains(flocation.getWorldName())) { + if (Conf.worldsNoClaiming.contains(flocation.getWorldName())) + { sendMessage("Sorry, this world has land claiming disabled."); return false; } - - if (otherFaction.isSafeZone()) { + + if (otherFaction.isSafeZone()) + { if (notifyFailure) sendMessage("You can not claim a Safe Zone."); return false; } - else if (otherFaction.isWarZone()) { + else if (otherFaction.isWarZone()) + { if (notifyFailure) sendMessage("You can not claim a War Zone."); return false; } int ownedLand = myFaction.getLandRounded(); - if (ownedLand >= myFaction.getPowerRounded()) { + if (ownedLand >= myFaction.getPowerRounded()) + { sendMessage("You can't claim more land! You need more power!"); return false; } - if (otherFaction.getRelation(this) == Relation.ALLY) { + if (otherFaction.getRelation(this) == Relation.ALLY) + { if (notifyFailure) sendMessage("You can't claim the land of your allies."); return false; } - if ( - Conf.claimsMustBeConnected - && !Conf.adminBypassPlayers.contains(this.playerName) - && myFaction.getLandRoundedInWorld(flocation.getWorldName()) > 0 - && !Board.isConnectedLocation(flocation, myFaction) - && (!Conf.claimsCanBeUnconnectedIfOwnedByOtherFaction || !otherFaction.isNormal()) - ) { + if + ( + Conf.claimsMustBeConnected + && !Conf.adminBypassPlayers.contains(this.getId()) + && myFaction.getLandRoundedInWorld(flocation.getWorldName()) > 0 + && !Board.isConnectedLocation(flocation, myFaction) + && (!Conf.claimsCanBeUnconnectedIfOwnedByOtherFaction || !otherFaction.isNormal()) + ) + { if (Conf.claimsCanBeUnconnectedIfOwnedByOtherFaction) sendMessage("You can only claim additional land which is connected to your first claim or controlled by another faction!"); else @@ -611,44 +694,58 @@ public class FPlayer { return false; } - if (otherFaction.isNormal()) { - if (myFaction.isPeaceful()) { - sendMessage(this.getRelationColor(otherFaction)+otherFaction.getTag()+Conf.colorSystem+" owns this land. Your faction is peaceful, so you cannot claim land from other factions."); + if (otherFaction.isNormal()) + { + if (myFaction.isPeaceful()) + { + sendMessage(P.p.txt.parse(this.getRelationColor(otherFaction)+otherFaction.getTag()+" owns this land. Your faction is peaceful, so you cannot claim land from other factions.")); return false; } - if (otherFaction.isPeaceful()) { - sendMessage(this.getRelationColor(otherFaction)+otherFaction.getTag()+Conf.colorSystem+" owns this land, and is a peaceful faction. You cannot claim land from them."); + + if (otherFaction.isPeaceful()) + { + sendMessage(P.p.txt.parse(this.getRelationColor(otherFaction)+otherFaction.getTag()+" owns this land, and is a peaceful faction. You cannot claim land from them.")); return false; } - if ( ! otherFaction.hasLandInflation()) { + if ( ! otherFaction.hasLandInflation()) + { // TODO more messages WARN current faction most importantly - sendMessage(this.getRelationColor(otherFaction)+otherFaction.getTag()+Conf.colorSystem+" owns this land and is strong enough to keep it."); + sendMessage(P.p.txt.parse(this.getRelationColor(otherFaction)+otherFaction.getTag()+" owns this land and is strong enough to keep it.")); return false; } - if ( ! Board.isBorderLocation(flocation)) { + if ( ! Board.isBorderLocation(flocation)) + { sendMessage("You must start claiming land at the border of the territory."); return false; } } // if economy is enabled and they're not on the bypass list, make 'em pay - if (Econ.enabled() && !Conf.adminBypassPlayers.contains(this.playerName)) { + if (Econ.enabled() && !Conf.adminBypassPlayers.contains(this.getId())) + { double cost = Econ.calculateClaimCost(ownedLand, otherFaction.isNormal()); String costString = Econ.moneyString(cost); - if(Conf.bankFactionPaysLandCosts && this.hasFaction()) { + if(Conf.bankFactionPaysLandCosts && this.hasFaction()) + { Faction faction = this.getFaction(); - if(!faction.removeMoney(cost)) { + if(!faction.removeMoney(cost)) + { sendMessage("It costs "+costString+" to claim this land, which your faction can't currently afford."); return false; - } else { + } + else + { sendMessage(faction.getTag()+" has paid "+costString+" to claim some land."); } - } else { - if (!Econ.deductMoney(this.playerName, cost)) { + } + else + { + if (!Econ.deductMoney(this.getId(), cost)) + { sendMessage("Claiming this land will cost "+costString+", which you can't currently afford."); return false; } @@ -657,45 +754,30 @@ public class FPlayer { } // announce success - if (otherFaction.isNormal()) { + if (otherFaction.isNormal()) + { // ASDF claimed some of your land 450 blocks NNW of you. // ASDf claimed some land from FACTION NAME - otherFaction.sendMessage(this.getNameAndRelevant(otherFaction)+Conf.colorSystem+" stole some of your land :O"); - myFaction.sendMessage(this.getNameAndRelevant(myFaction)+Conf.colorSystem+" claimed some land from "+otherFaction.getTag(myFaction)); + otherFaction.sendMessage(P.p.txt.parse(this.getNameAndRelevant(otherFaction)+" stole some of your land :O")); + myFaction.sendMessage(P.p.txt.parse(this.getNameAndRelevant(myFaction)+" claimed some land from "+otherFaction.getTag(myFaction))); } - else { - myFaction.sendMessage(this.getNameAndRelevant(myFaction)+Conf.colorSystem+" claimed some new land :D"); + else + { + myFaction.sendMessage(P.p.txt.parse(this.getNameAndRelevant(myFaction)+" claimed some new land :D")); } Board.setFactionAt(myFaction, flocation); return true; } - - // -------------------------------------------- // - // Messages - // -------------------------------------------- // - public void sendMessage(String message) { - if (this.getPlayer() != null) - this.getPlayer().sendMessage(Conf.colorSystem + message); - } - - public void sendMessage(List messages) { - for(String message : messages) { - this.sendMessage(message); - } - } // -------------------------------------------- // // Get and search // -------------------------------------------- // - // You should use this one to be sure you do not spell the player name wrong. - public static FPlayer get(Player player) { - return get(player.getName()); - } - - private static FPlayer get(String playerName) { - if (instances.containsKey(playerName)) { + /*private static FPlayer get(String playerName) + { + if (instances.containsKey(playerName)) + { return instances.get(playerName); } @@ -704,38 +786,21 @@ public class FPlayer { instances.put(playerName, vplayer); return vplayer; - } - - public static Set getAllOnline() { - Set fplayers = new HashSet(); - for (Player player : Factions.instance.getServer().getOnlinePlayers()) { - fplayers.add(FPlayer.get(player)); - } - return fplayers; - } - - public static Collection getAll() { - return instances.values(); - } - - public static FPlayer find(String playername) { - for (Entry entry : instances.entrySet()) { - if (entry.getKey().equalsIgnoreCase(playername) || entry.getKey().startsWith(playername)) { - return entry.getValue(); - } - } - return null; - } + }*/ // -------------------------------------------- // // Persistance // -------------------------------------------- // - public boolean shouldBeSaved() { - return !deleteMe; + @Override + public boolean shouldBeSaved() + { + return ! this.deleteMe; } - public static boolean save() { + /* + public static boolean save() + { //Factions.log("Saving players to disk"); // We only wan't to save the players with non default values @@ -747,32 +812,32 @@ public class FPlayer { } try { - DiscUtil.write(file, Factions.instance.gson.toJson(playersToSave)); + DiscUtil.write(file, P.p.gson.toJson(playersToSave)); } catch (Exception e) { e.printStackTrace(); - Factions.log("Failed to save the players to disk."); + P.log("Failed to save the players to disk."); return false; } return true; } public static boolean load() { - Factions.log("Loading players from disk"); + P.log("Loading players from disk"); if ( ! file.exists()) { if ( ! loadOld()) - Factions.log("No players to load from disk. Creating new file."); + P.log("No players to load from disk. Creating new file."); save(); return true; } try { Type type = new TypeToken>(){}.getType(); - Map instancesFromFile = Factions.instance.gson.fromJson(DiscUtil.read(file), type); + Map instancesFromFile = P.p.gson.fromJson(DiscUtil.read(file), type); instances.clear(); instances.putAll(instancesFromFile); } catch (Exception e) { e.printStackTrace(); - Factions.log("Failed to load the players from disk."); + P.log("Failed to load the players from disk."); return false; } @@ -786,45 +851,24 @@ public class FPlayer { entry.getValue().playerName = entry.getKey(); } } + */ - public static void clean() { - for (FPlayer fplayer : instances.values()) { - if ( ! Faction.exists(fplayer.getFactionId())) { - Factions.log("Reset faction data (invalid faction) for player "+fplayer.getName()); - fplayer.resetFactionData(); - } - } - } - - public static void autoLeaveOnInactivityRoutine() { - if (Conf.autoLeaveAfterDaysOfInactivity <= 0.0) { - return; - } - long now = System.currentTimeMillis(); - double toleranceMillis = Conf.autoLeaveAfterDaysOfInactivity * 24 * 60 * 60 * 1000; - - for (FPlayer fplayer : FPlayer.getAll()) { - if (now - fplayer.getLastLoginTime() > toleranceMillis) { - fplayer.leave(false); - fplayer.markForDeletion(true); - } - } - } + /*private static boolean loadOld() + { + File folderFollower = new File(P.p.getDataFolder(), "follower"); - private static boolean loadOld() { - File folderFollower = new File(Factions.instance.getDataFolder(), "follower"); + if ( ! folderFollower.isDirectory()) return false; - if ( ! folderFollower.isDirectory()) - return false; - - Factions.log("Players file doesn't exist, attempting to load old pre-1.1 data."); + p.log("Players file doesn't exist, attempting to load old pre-1.1 data."); String ext = ".json"; - class jsonFileFilter implements FileFilter { + class jsonFileFilter implements FileFilter + { @Override - public boolean accept(File file) { + public boolean accept(File file) + { return (file.getName().toLowerCase().endsWith(".json") && file.isFile()); } } @@ -836,16 +880,16 @@ public class FPlayer { String name = jsonFile.getName(); name = name.substring(0, name.length() - ext.length()); try { - FPlayer follower = Factions.instance.gson.fromJson(DiscUtil.read(jsonFile), FPlayer.class); + FPlayer follower = P.p.gson.fromJson(DiscUtil.read(jsonFile), FPlayer.class); follower.playerName = name; follower.lastLoginTime = System.currentTimeMillis(); instances.put(follower.playerName, follower); - Factions.log("loaded pre-1.1 follower "+name); + P.log("loaded pre-1.1 follower "+name); } catch (Exception e) { e.printStackTrace(); - Factions.log(Level.WARNING, "failed to load follower "+name); + P.log(Level.WARNING, "failed to load follower "+name); } } return true; - } + }*/ } \ No newline at end of file diff --git a/src/com/massivecraft/factions/FPlayers.java b/src/com/massivecraft/factions/FPlayers.java new file mode 100644 index 00000000..165d222a --- /dev/null +++ b/src/com/massivecraft/factions/FPlayers.java @@ -0,0 +1,110 @@ +package com.massivecraft.factions; + +import java.io.File; +import java.lang.reflect.Type; +import java.util.Map; +import java.util.concurrent.ConcurrentSkipListMap; +import java.util.concurrent.CopyOnWriteArrayList; + +import com.google.gson.reflect.TypeToken; +import com.massivecraft.factions.zcore.persist.PlayerEntityCollection; + +public class FPlayers extends PlayerEntityCollection +{ + public static FPlayers i = new FPlayers(); + + P p = P.p; + + private FPlayers() + { + super + ( + FPlayer.class, + new CopyOnWriteArrayList(), + new ConcurrentSkipListMap(String.CASE_INSENSITIVE_ORDER), + new File(P.p.getDataFolder(), "players.json"), + P.p.gson + ); + + this.setCreative(true); + } + + @Override + public Type getMapType() + { + return new TypeToken>(){}.getType(); + } + + public void clean() + { + for (FPlayer fplayer : this.get()) + { + if ( ! Factions.i.exists(fplayer.getFactionId())) + { + p.log("Reset faction data (invalid faction) for player "+fplayer.getName()); + fplayer.resetFactionData(); + } + } + } + + public void autoLeaveOnInactivityRoutine() + { + if (Conf.autoLeaveAfterDaysOfInactivity <= 0.0) + { + return; + } + + long now = System.currentTimeMillis(); + double toleranceMillis = Conf.autoLeaveAfterDaysOfInactivity * 24 * 60 * 60 * 1000; + + for (FPlayer fplayer : FPlayers.i.get()) + { + if (now - fplayer.getLastLoginTime() > toleranceMillis) + { + fplayer.leave(false); + fplayer.markForDeletion(true); + } + } + } + + + // TODO: Intressant.... denna skulle jag kanske behöva undersöka lite mer... lägga till i core? + // En form av match player name... + public FPlayer find(String playername) + { + for (FPlayer fplayer : this.get()) + { + if (fplayer.getId().equalsIgnoreCase(playername) || fplayer.getId().startsWith(playername)) + { + return fplayer; + } + } + return null; + } + + /*public Set findAllOnlineInfected() + { + Set vplayers = new HashSet(); + for (VPlayer vplayer : this.getOnline()) + { + if (vplayer.isInfected()) + { + vplayers.add(vplayer); + } + } + return vplayers; + } + + public Set findAllOnlineVampires() + { + Set vplayers = new HashSet(); + for (VPlayer vplayer : this.getOnline()) + { + if (vplayer.isVampire()) + { + vplayers.add(vplayer); + } + } + return vplayers; + }*/ +} diff --git a/src/com/massivecraft/factions/Faction.java b/src/com/massivecraft/factions/Faction.java index 3e83f91a..0d5d34d4 100644 --- a/src/com/massivecraft/factions/Faction.java +++ b/src/com/massivecraft/factions/Faction.java @@ -15,38 +15,129 @@ import com.google.gson.reflect.TypeToken; import com.massivecraft.factions.struct.Relation; import com.massivecraft.factions.struct.Role; import com.massivecraft.factions.util.*; +import com.massivecraft.factions.zcore.persist.Entity; -public class Faction { +public class Faction extends Entity +{ + // FIELD: relationWish + private Map relationWish; - // -------------------------------------------- // - // Fields - // -------------------------------------------- // - - private static transient Map instances = new HashMap(); - private static transient File file = new File(Factions.instance.getDataFolder(), "factions.json"); - private static transient int nextId; - - private transient int id; - private Map relationWish; + // FIELD: claimOwnership private Map> claimOwnership = new ConcurrentHashMap>(); - private Set invites; // Where string is a lowercase player name + + // FIELD: invites + // Where string is a lowercase player name + private Set invites; + public void invite(FPlayer fplayer) { this.invites.add(fplayer.getName().toLowerCase()); } + public void deinvite(FPlayer fplayer) { this.invites.remove(fplayer.getName().toLowerCase()); } + public boolean isInvited(FPlayer fplayer) { return this.invites.contains(fplayer.getName().toLowerCase()); } + + // FIELD: open private boolean open; + public boolean getOpen() { return open; } + public void setOpen(boolean isOpen) { open = isOpen; } + + // FIELD: peaceful + // "peaceful" status can only be set by server admins/moderators/ops, and prevents PvP and land capture to/from the faction private boolean peaceful; + public boolean isPeaceful() { return this.peaceful; } + public void setPeaceful(boolean isPeaceful) { this.peaceful = isPeaceful; } + + // FIELD: peacefulExplosionsEnabled private boolean peacefulExplosionsEnabled; + public void setPeacefulExplosions(boolean disable) { peacefulExplosionsEnabled = disable; } //TODO: Convert to argswitch in command!! + public void setPeacefulExplosions() { setPeacefulExplosions(!peacefulExplosionsEnabled); } + + // FIELD: permanent + // "permanent" status can only be set by server admins/moderators/ops, and allows the faction to remain even with 0 members private boolean permanent; + public boolean isPermanent() { return permanent; } + public void setPermanent(boolean isPermanent) { permanent = isPermanent; } + + // FIELD: tag private String tag; + public String getTag() { return this.tag; } + public String getTag(String prefix) { return prefix+this.tag; } + public String getTag(Faction otherFaction) + { + if (otherFaction == null) + { + return getTag(); + } + return this.getTag(otherFaction.getRelationColor(this).toString()); + } + public String getTag(FPlayer otherFplayer) { + if (otherFplayer == null) + { + return getTag(); + } + return this.getTag(otherFplayer.getRelationColor(this).toString()); + } + public void setTag(String str) + { + if (Conf.factionTagForceUpperCase) + { + str = str.toUpperCase(); + } + this.tag = str; + } + public String getComparisonTag() { return MiscUtil.getComparisonString(this.tag); } + + // FIELD: description private String description; + public String getDescription() { return this.description; } + public void setDescription(String value) { this.description = value; } + + // FIELD: home private Location home; + public void setHome(Location home) { this.home = home; } + public Location getHome() { confirmValidHome(); return home; } + public boolean hasHome() { return this.getHome() != null; } + public void confirmValidHome() + { + if (!Conf.homesMustBeInClaimedTerritory || this.home == null || Board.getFactionAt(new FLocation(this.home)) == this) + { + return; + } + + sendMessage("Your faction home has been un-set since it is no longer in your territory."); + this.home = null; + } + + // FIELD: lastPlayerLoggedOffTime private transient long lastPlayerLoggedOffTime; + + // FIELD: money + // Bank functions private double money; + public double getMoney() { return this.money; } + public boolean addMoney(double amount) + { + if ( amount > 0.0 ) + { + this.money += amount; + return true; + } + return false; + } + public boolean removeMoney( double amount ) + { + if (amount <= 0.0 ) return false; + + if (amount > this.money ) return false; + + this.money -= amount; + return true; + } // -------------------------------------------- // // Construct // -------------------------------------------- // - public Faction() { - this.relationWish = new HashMap(); + public Faction() + { + this.relationWish = new HashMap(); this.invites = new HashSet(); this.open = Conf.newFactionsDefaultOpen; this.tag = "???"; @@ -59,188 +150,97 @@ public class Faction { } // -------------------------------------------- // - // Getters And Setters + // Extra Getters And Setters // -------------------------------------------- // - - public int getId() { - return this.id; - } - - public boolean getOpen() { - return open; - } - - public void setOpen(boolean isOpen) { - open = isOpen; - } - - public String getTag() { - return this.getTag(""); - } - public String getTag(String prefix) { - return prefix+this.tag; - } - public String getTag(Faction otherFaction) { - if (otherFaction == null) - return getTag(); - else - return this.getTag(otherFaction.getRelationColor(this).toString()); - } - public String getTag(FPlayer otherFplayer) { - if (otherFplayer == null) - return getTag(); - else - return this.getTag(otherFplayer.getRelationColor(this).toString()); - } - public void setTag(String str) { - if (Conf.factionTagForceUpperCase) { - str = str.toUpperCase(); - } - this.tag = str; - } - - public String getDescription() { - return this.description; - } - - public void setDescription(String value) { - this.description = value; - } - - public void setHome(Location home) { - this.home = home; - } - public Location getHome() { - confirmValidHome(); - return home; - } - - public boolean hasHome() { - confirmValidHome(); - return this.home != null; - } + public boolean noPvPInTerritory() { return isSafeZone() || (peaceful && Conf.peacefulTerritoryDisablePVP); } - public void confirmValidHome() { - if (!Conf.homesMustBeInClaimedTerritory || this.home == null || Board.getFactionAt(new FLocation(this.home)) == this) { - return; - } + public boolean noMonstersInTerritory() { return isSafeZone() || (peaceful && Conf.peacefulTerritoryDisableMonsters); } - sendMessage("Your faction home has been un-set since it is no longer in your territory."); - this.home = null; - } - - // "peaceful" status can only be set by server admins/moderators/ops, and prevents PvP and land capture to/from the faction - public boolean isPeaceful() { - return peaceful; - } - public void setPeaceful(boolean isPeaceful) { - peaceful = isPeaceful; - } - - // "permanent" status can only be set by server admins/moderators/ops, and allows the faction to remain even with 0 members - public boolean isPermanent() { - return permanent; - } - public void setPermanent(boolean isPermanent) { - permanent = isPermanent; - } - - public void setPeacefulExplosions(boolean disable) { - peacefulExplosionsEnabled = disable; - } - public void setPeacefulExplosions() { - setPeacefulExplosions(!peacefulExplosionsEnabled); - } - - public boolean noPvPInTerritory() { - return isSafeZone() || (peaceful && Conf.peacefulTerritoryDisablePVP); - } - - public boolean noMonstersInTerritory() { - return isSafeZone() || (peaceful && Conf.peacefulTerritoryDisableMonsters); - } - - public boolean noExplosionsInTerritory() { - return peaceful && !peacefulExplosionsEnabled; - } + public boolean noExplosionsInTerritory() { return peaceful && !peacefulExplosionsEnabled; } // ------------------------------- // Understand the types // ------------------------------- - public boolean isNormal() { - return this.getId() > 0; + public boolean isNormal() + { + return ! (this.isNone() || this.isSafeZone() || this.isWarZone()); } - public boolean isNone() { - return this.getId() == 0; + public boolean isNone() + { + return this.getId().equals("0"); } - public boolean isSafeZone() { - return this.getId() == -1; + public boolean isSafeZone() + { + return this.getId().equals("-1"); } - public boolean isWarZone() { - return this.getId() == -2; + public boolean isWarZone() + { + return this.getId().equals("-2"); } - // ------------------------------- - // Invites - uses lowercase name - // ------------------------------- - - public void invite(FPlayer fplayer) { - this.invites.add(fplayer.getName().toLowerCase()); - } - - public void deinvite(FPlayer fplayer) { - this.invites.remove(fplayer.getName().toLowerCase()); - } - - public boolean isInvited(FPlayer fplayer) { - return this.invites.contains(fplayer.getName().toLowerCase()); - } // ------------------------------- // Relation and relation colors TODO // ------------------------------- - public Relation getRelationWish(Faction otherFaction) { - if (this.relationWish.containsKey(otherFaction.getId())){ + public Relation getRelationWish(Faction otherFaction) + { + if (this.relationWish.containsKey(otherFaction.getId())) + { return this.relationWish.get(otherFaction.getId()); } return Relation.NEUTRAL; } - public void setRelationWish(Faction otherFaction, Relation relation) { - if (this.relationWish.containsKey(otherFaction.getId()) && relation.equals(Relation.NEUTRAL)){ + public void setRelationWish(Faction otherFaction, Relation relation) + { + if (this.relationWish.containsKey(otherFaction.getId()) && relation.equals(Relation.NEUTRAL)) + { this.relationWish.remove(otherFaction.getId()); - } else { + } + else + { this.relationWish.put(otherFaction.getId(), relation); } } - public Relation getRelation(Faction otherFaction) { + public Relation getRelation(Faction otherFaction) + { return getRelation(otherFaction, false); } - public Relation getRelation(Faction otherFaction, boolean ignorePeaceful) { - if (!otherFaction.isNormal() || !this.isNormal()) { + public Relation getRelation(Faction otherFaction, boolean ignorePeaceful) + { + if (!otherFaction.isNormal() || !this.isNormal()) + { return Relation.NEUTRAL; } - if (otherFaction.equals(this)) { + + if (otherFaction.equals(this)) + { return Relation.MEMBER; } - if (!ignorePeaceful && (this.peaceful || otherFaction.isPeaceful())) { + + if (!ignorePeaceful && (this.peaceful || otherFaction.isPeaceful())) + { return Relation.NEUTRAL; } - if(this.getRelationWish(otherFaction).value >= otherFaction.getRelationWish(this).value) { + + if(this.getRelationWish(otherFaction).value >= otherFaction.getRelationWish(this).value) + { return otherFaction.getRelationWish(this); } + return this.getRelationWish(otherFaction); } - public Relation getRelation(FPlayer fplayer) { + public Relation getRelation(FPlayer fplayer) + { if (fplayer == null) return Relation.NEUTRAL; else @@ -250,7 +250,8 @@ public class Faction { //----------------------------------------------// // Power //----------------------------------------------// - public double getPower() { + public double getPower() + { double ret = 0; for (FPlayer fplayer : this.getFPlayers()) { ret += fplayer.getPower(); @@ -296,13 +297,15 @@ public class Faction { // Fplayers // ------------------------------- - public ArrayList getFPlayers() { + public ArrayList getFPlayers() + { ArrayList ret = new ArrayList(); - if (id < 0) - return ret; + if (id < 0) return ret; - for (FPlayer fplayer : FPlayer.getAll()) { - if (fplayer.getFaction() == this) { + for (FPlayer fplayer : FPlayers.i.get()) + { + if (fplayer.getFaction() == this) + { ret.add(fplayer); } } @@ -310,13 +313,15 @@ public class Faction { return ret; } - public ArrayList getFPlayersWhereOnline(boolean online) { + public ArrayList getFPlayersWhereOnline(boolean online) + { ArrayList ret = new ArrayList(); - if (id < 0) - return ret; + if (id < 0) return ret; - for (FPlayer fplayer : FPlayer.getAll()) { - if (fplayer.getFaction() == this && fplayer.isOnline() == online) { + for (FPlayer fplayer : FPlayers.i.get()) + { + if (fplayer.getFaction() == this && fplayer.isOnline() == online) + { ret.add(fplayer); } } @@ -324,12 +329,14 @@ public class Faction { return ret; } - public FPlayer getFPlayerAdmin() { - if (id <= 0) - return null; + public FPlayer getFPlayerAdmin() + { + if (id <= 0) return null; - for (FPlayer fplayer : FPlayer.getAll()) { - if (fplayer.getFaction() == this && fplayer.getRole() == Role.ADMIN) { + for (FPlayer fplayer : FPlayers.i.get()) + { + if (fplayer.getFaction() == this && fplayer.getRole() == Role.ADMIN) + { return fplayer; } } @@ -338,10 +345,9 @@ public class Faction { public ArrayList getFPlayersWhereRole(Role role) { ArrayList ret = new ArrayList(); - if (id <= 0) - return ret; + if (id <= 0) return ret; - for (FPlayer fplayer : FPlayer.getAll()) { + for (FPlayer fplayer : FPlayers.i.get()) { if (fplayer.getFaction() == this && fplayer.getRole() == role) { ret.add(fplayer); } @@ -350,14 +356,16 @@ public class Faction { return ret; } - public ArrayList getOnlinePlayers() { + public ArrayList getOnlinePlayers() + { ArrayList ret = new ArrayList(); - if (id < 0) - return ret; + if (id < 0) return ret; - for (Player player: Factions.instance.getServer().getOnlinePlayers()) { - FPlayer fplayer = FPlayer.get(player); - if (fplayer.getFaction() == this) { + for (Player player: P.p.getServer().getOnlinePlayers()) + { + FPlayer fplayer = FPlayers.i.get(player); + if (fplayer.getFaction() == this) + { ret.add(player); } } @@ -366,85 +374,53 @@ public class Faction { } // slightly faster check than getOnlinePlayers() if you just want to see if there are any players online - public boolean hasPlayersOnline() { + public boolean hasPlayersOnline() + { // only real factions can have players online, not safe zone / war zone - if (id < 0) - return false; + if (id < 0) return false; - for (Player player: Factions.instance.getServer().getOnlinePlayers()) { - FPlayer fplayer = FPlayer.get(player); - if (fplayer.getFaction() == this) { + for (Player player: P.p.getServer().getOnlinePlayers()) + { + FPlayer fplayer = FPlayers.i.get(player); + if (fplayer.getFaction() == this) + { return true; } } // even if all players are technically logged off, maybe someone was on recently enough to not consider them officially offline yet - if (Conf.considerFactionsReallyOfflineAfterXMinutes > 0 && - System.currentTimeMillis() < lastPlayerLoggedOffTime + (Conf.considerFactionsReallyOfflineAfterXMinutes * 60000)) { + if (Conf.considerFactionsReallyOfflineAfterXMinutes > 0 && System.currentTimeMillis() < lastPlayerLoggedOffTime + (Conf.considerFactionsReallyOfflineAfterXMinutes * 60000)) + { return true; } return false; } - public void memberLoggedOff() { - if (this.isNormal()) { + public void memberLoggedOff() + { + if (this.isNormal()) + { lastPlayerLoggedOffTime = System.currentTimeMillis(); } } - //----------------------------------------------// - // Faction tag - //----------------------------------------------// - public String getComparisonTag() { - return TextUtil.getComparisonString(this.tag); - } - - public static ArrayList validateTag(String str) { - ArrayList errors = new ArrayList(); - - if(TextUtil.getComparisonString(str).length() < Conf.factionTagLengthMin) { - errors.add(Conf.colorSystem+"The faction tag can't be shorter than "+Conf.factionTagLengthMin+ " chars."); - } - - if(str.length() > Conf.factionTagLengthMax) { - errors.add(Conf.colorSystem+"The faction tag can't be longer than "+Conf.factionTagLengthMax+ " chars."); - } - - for (char c : str.toCharArray()) { - if ( ! TextUtil.substanceChars.contains(String.valueOf(c))) { - errors.add(Conf.colorSystem+"Faction tag must be alphanumeric. \""+c+"\" is not allowed."); - } - } - - return errors; - } - - public static Faction findByTag(String str) { - String compStr = TextUtil.getComparisonString(str); - for (Faction faction : Faction.getAll()) { - if (faction.getComparisonTag().equals(compStr)) { - return faction; - } - } - return null; - } - - public static boolean isTagTaken(String str) { - return Faction.findByTag(str) != null; - } //----------------------------------------------// // Messages //----------------------------------------------// - public void sendMessage(String message) { - for (FPlayer fplayer : this.getFPlayersWhereOnline(true)) { + public void sendMessage(String message) + { + for (FPlayer fplayer : this.getFPlayersWhereOnline(true)) + { fplayer.sendMessage(message); } } - public void sendMessage(List messages) { - for (FPlayer fplayer : this.getFPlayersWhereOnline(true)) { + public void sendMessage(List messages) + { + for (FPlayer fplayer : this.getFPlayersWhereOnline(true)) + { fplayer.sendMessage(messages); } } @@ -453,11 +429,13 @@ public class Faction { // Mudd TODO //----------------------------------------------// - public ChatColor getRelationColor(Faction otherFaction) { + public ChatColor getRelationColor(Faction otherFaction) + { return this.getRelation(otherFaction).getColor(); } - public ChatColor getRelationColor(FPlayer fplayer) { + public ChatColor getRelationColor(FPlayer fplayer) + { return this.getRelation(fplayer).getColor(); } @@ -465,78 +443,96 @@ public class Faction { // Ownership of specific claims //----------------------------------------------// - public void clearAllClaimOwnership() { + public void clearAllClaimOwnership() + { claimOwnership.clear(); } - public void clearClaimOwnership(FLocation loc) { + public void clearClaimOwnership(FLocation loc) + { claimOwnership.remove(loc); } - public void clearClaimOwnership(String playerName) { - if (playerName == null || playerName.isEmpty()) { + public void clearClaimOwnership(String playerName) + { + if (playerName == null || playerName.isEmpty()) + { return; } Set ownerData; String player = playerName.toLowerCase(); - for (Entry> entry : claimOwnership.entrySet()) { + for (Entry> entry : claimOwnership.entrySet()) + { ownerData = entry.getValue(); - if (ownerData == null) { - continue; - } + if (ownerData == null) continue; Iterator iter = ownerData.iterator(); - while (iter.hasNext()) { - if (iter.next().equals(player)) { + while (iter.hasNext()) + { + if (iter.next().equals(player)) + { iter.remove(); } } - if (ownerData.isEmpty()) { + if (ownerData.isEmpty()) + { claimOwnership.remove(entry.getKey()); } } } - public int getCountOfClaimsWithOwners() { + public int getCountOfClaimsWithOwners() + { return claimOwnership.isEmpty() ? 0 : claimOwnership.size(); } - public boolean doesLocationHaveOwnersSet(FLocation loc) { - if (claimOwnership.isEmpty() || !claimOwnership.containsKey(loc)) { + public boolean doesLocationHaveOwnersSet(FLocation loc) + { + if (claimOwnership.isEmpty() || !claimOwnership.containsKey(loc)) + { return false; } + Set ownerData = claimOwnership.get(loc); return ownerData != null && !ownerData.isEmpty(); } - public boolean isPlayerInOwnerList(String playerName, FLocation loc) { - if (claimOwnership.isEmpty()) { + public boolean isPlayerInOwnerList(String playerName, FLocation loc) + { + if (claimOwnership.isEmpty()) + { return false; } Set ownerData = claimOwnership.get(loc); - if (ownerData == null) { + if (ownerData == null) + { return false; } - if (ownerData.contains(playerName.toLowerCase())) { + if (ownerData.contains(playerName.toLowerCase())) + { return true; } + return false; } - public void setPlayerAsOwner(String playerName, FLocation loc) { + public void setPlayerAsOwner(String playerName, FLocation loc) + { Set ownerData = claimOwnership.get(loc); - if (ownerData == null) { + if (ownerData == null) + { ownerData = new HashSet(); } ownerData.add(playerName.toLowerCase()); claimOwnership.put(loc, ownerData); } - public void removePlayerAsOwner(String playerName, FLocation loc) { + public void removePlayerAsOwner(String playerName, FLocation loc) + { Set ownerData = claimOwnership.get(loc); if (ownerData == null) { return; @@ -545,13 +541,16 @@ public class Faction { claimOwnership.put(loc, ownerData); } - public Set getOwnerList(FLocation loc) { + public Set getOwnerList(FLocation loc) + { return claimOwnership.get(loc); } - public String getOwnerListString(FLocation loc) { + public String getOwnerListString(FLocation loc) + { Set ownerData = claimOwnership.get(loc); - if (ownerData == null || ownerData.isEmpty()) { + if (ownerData == null || ownerData.isEmpty()) + { return ""; } @@ -559,7 +558,8 @@ public class Faction { Iterator iter = ownerData.iterator(); while (iter.hasNext()) { - if (!ownerList.isEmpty()) { + if (!ownerList.isEmpty()) + { ownerList += ", "; } ownerList += iter.next(); @@ -567,19 +567,23 @@ public class Faction { return ownerList; } - public boolean playerHasOwnershipRights(FPlayer fplayer, FLocation loc) { + public boolean playerHasOwnershipRights(FPlayer fplayer, FLocation loc) + { // different faction? - if (fplayer.getFactionId() != id) { + if (fplayer.getFactionId() != this.getId()) + { return false; } // sufficient role to bypass ownership? - if (fplayer.getRole().isAtLeast(Conf.ownedAreaModeratorsBypass ? Role.MODERATOR : Role.ADMIN)) { + if (fplayer.getRole().isAtLeast(Conf.ownedAreaModeratorsBypass ? Role.MODERATOR : Role.ADMIN)) + { return true; } // make sure claimOwnership is initialized - if (claimOwnership.isEmpty()) { + if (claimOwnership.isEmpty()) + { return true; } @@ -587,7 +591,8 @@ public class Faction { Set ownerData = claimOwnership.get(loc); // if no owner list, owner list is empty, or player is in owner list, they're allowed - if (ownerData == null || ownerData.isEmpty() || ownerData.contains(fplayer.getName().toLowerCase())) { + if (ownerData == null || ownerData.isEmpty() || ownerData.contains(fplayer.getName().toLowerCase())) + { return true; } @@ -595,74 +600,48 @@ public class Faction { } - //----------------------------------------------// - // Bank functions - //----------------------------------------------// - public double getMoney() { - return this.money; - } - - public boolean addMoney(double amount) { - if ( amount > 0.0 ) - { - this.money += amount; - return true; - } - return false; - } - - public boolean removeMoney( double amount ) { - if (amount <= 0.0 ) - return false; - - if (amount > this.money ) - return false; - - this.money -= amount; - return true; - } //----------------------------------------------// // Persistance and entity management //----------------------------------------------// - public static boolean save() { + /*public static boolean save() { //Factions.log("Saving factions to disk"); try { - DiscUtil.write(file, Factions.instance.gson.toJson(instances)); + DiscUtil.write(file, P.p.gson.toJson(instances)); } catch (IOException e) { e.printStackTrace(); - Factions.log("Failed to save the factions to disk due to I/O exception."); + P.log("Failed to save the factions to disk due to I/O exception."); return false; } catch (Exception e) { e.printStackTrace(); - Factions.log("Failed to save the factions to disk."); + P.log("Failed to save the factions to disk."); return false; } return true; } - - public static boolean load() { - Factions.log("Loading factions from disk"); + */ + /*public static boolean load() { + P.log("Loading factions from disk"); if ( ! file.exists()) { if ( ! loadOld()) - Factions.log("No factions to load from disk. Creating new file."); + P.log("No factions to load from disk. Creating new file."); save(); } try { Type type = new TypeToken>(){}.getType(); - Map instancesFromFile = Factions.instance.gson.fromJson(DiscUtil.read(file), type); + Map instancesFromFile = P.p.gson.fromJson(DiscUtil.read(file), type); instances.clear(); instances.putAll(instancesFromFile); } catch (Exception e) { e.printStackTrace(); - Factions.log("Failed to load the factions from disk."); + P.log("Failed to load the factions from disk."); return false; } @@ -696,28 +675,31 @@ public class Faction { } return true; + }*/ + + + @Override + public void postDetach() + { + // Clean the board + Board.clean(); + + // Clean the fplayers + FPlayers.i.clean(); } - public static void fillIds() { - nextId = 1; - for(Entry entry : instances.entrySet()) { - entry.getValue().id = entry.getKey(); - if (nextId < entry.getKey()) { - nextId = entry.getKey(); - } - } - nextId += 1; // make it the next id and not the current highest. - } - - public static Faction get(Integer factionId) { - if ( ! instances.containsKey(factionId)) { - Factions.log(Level.WARNING, "Non existing factionId "+factionId+" requested! Issuing cleaning!"); + /*public static Faction get(Integer factionId) + { + if ( ! instances.containsKey(factionId)) + { + P.log(Level.WARNING, "Non existing factionId "+factionId+" requested! Issuing cleaning!"); Board.clean(); FPlayer.clean(); } return instances.get(factionId); - } + }*/ + /* public static Faction getNone() { return instances.get(0); } @@ -728,23 +710,22 @@ public class Faction { public static Faction getWarZone() { return instances.get(-2); - } + }*/ + /* public static boolean exists(Integer factionId) { return instances.containsKey(factionId); } - public static Collection getAll() { - return instances.values(); - } //TODO ta parametrar här. All info som behövs ska matas in här och så sparar vi i denna method. - public static Faction create() { + public static Faction create() + { Faction faction = new Faction(); faction.id = nextId; nextId += 1; instances.put(faction.id, faction); - Factions.log("created new faction "+faction.id); + P.log("created new faction "+faction.id); //faction.save(); return faction; } @@ -761,12 +742,12 @@ public class Faction { } private static boolean loadOld() { - File folderFaction = new File(Factions.instance.getDataFolder(), "faction"); + File folderFaction = new File(P.p.getDataFolder(), "faction"); if ( ! folderFaction.isDirectory()) return false; - Factions.log("Factions file doesn't exist, attempting to load old pre-1.1 data."); + P.log("Factions file doesn't exist, attempting to load old pre-1.1 data."); String ext = ".json"; @@ -785,15 +766,15 @@ public class Faction { int id = Integer.parseInt(name); try { - Faction faction = Factions.instance.gson.fromJson(DiscUtil.read(jsonFile), Faction.class); + Faction faction = P.p.gson.fromJson(DiscUtil.read(jsonFile), Faction.class); faction.id = id; instances.put(faction.id, faction); - Factions.log("loaded pre-1.1 faction "+id); + P.log("loaded pre-1.1 faction "+id); } catch (Exception e) { e.printStackTrace(); - Factions.log(Level.WARNING, "Failed to load faction "+id); + P.log(Level.WARNING, "Failed to load faction "+id); } } return true; - } + }*/ } diff --git a/src/com/massivecraft/factions/Factions.java b/src/com/massivecraft/factions/Factions.java index 384111d4..399fc4a2 100644 --- a/src/com/massivecraft/factions/Factions.java +++ b/src/com/massivecraft/factions/Factions.java @@ -1,538 +1,157 @@ -package com.massivecraft.factions; - -import java.io.File; -import java.lang.reflect.Modifier; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.bukkit.block.Block; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.event.Event; -import org.bukkit.event.player.PlayerChatEvent; -import org.bukkit.plugin.Plugin; -import org.bukkit.plugin.PluginManager; -import org.bukkit.plugin.java.JavaPlugin; - -import com.massivecraft.factions.commands.*; -import com.massivecraft.factions.integration.Econ; -import com.massivecraft.factions.integration.SpoutFeatures; -import com.massivecraft.factions.integration.Worldguard; -import com.massivecraft.factions.listeners.FactionsBlockListener; -import com.massivecraft.factions.listeners.FactionsChatEarlyListener; -import com.massivecraft.factions.listeners.FactionsEntityListener; -import com.massivecraft.factions.listeners.FactionsPlayerListener; -import com.massivecraft.factions.struct.ChatMode; -import com.massivecraft.factions.util.JarLoader; -import com.massivecraft.factions.util.MapFLocToStringSetTypeAdapter; -import com.massivecraft.factions.util.MyLocationTypeAdapter; - -import com.nijiko.permissions.PermissionHandler; -import com.nijikokun.bukkit.Permissions.Permissions; -import com.earth2me.essentials.chat.EssentialsChat; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.reflect.TypeToken; -import com.massivecraft.factions.integration.EssentialsFeatures; - -/** - * The data is saved to disk every 30min and on plugin disable. - */ -public class Factions extends JavaPlugin { - // -------------------------------------------- // - // Fields - // -------------------------------------------- // - public static Factions instance; - private Integer saveTask = null; - - public Gson gson; - - private final FactionsPlayerListener playerListener = new FactionsPlayerListener(); - private final FactionsChatEarlyListener chatEarlyListener = new FactionsChatEarlyListener(); - private final FactionsEntityListener entityListener = new FactionsEntityListener(); - private final FactionsBlockListener blockListener = new FactionsBlockListener(); - - public static PermissionHandler Permissions; - private static EssentialsChat essChat; - - // Commands - public List commands = new ArrayList(); - - private String baseCommand; - - public Factions() { - Factions.instance = this; - } - - - @Override - public void onEnable() { - log("=== ENABLE START ==="); - long timeInitStart = System.currentTimeMillis(); - - // Load the gson library we require - File gsonfile = new File("./lib/gson.jar"); - if ( ! JarLoader.load(gsonfile)) { - log(Level.SEVERE, "Disabling myself as "+gsonfile.getPath()+" is missing from the root Minecraft server folder."); - this.getServer().getPluginManager().disablePlugin(this); - return; - } - - Type mapFLocToStringSetType = new TypeToken>>(){}.getType(); - - gson = new GsonBuilder() - .setPrettyPrinting() - .excludeFieldsWithModifiers(Modifier.TRANSIENT, Modifier.VOLATILE) - .registerTypeAdapter(Location.class, new MyLocationTypeAdapter()) - .registerTypeAdapter(mapFLocToStringSetType, new MapFLocToStringSetTypeAdapter()) - .create(); - - // Add the commands - commands.add(new FCommandHelp()); - commands.add(new FCommandAdmin()); - commands.add(new FCommandAutoClaim()); - commands.add(new FCommandAutoSafeclaim()); - commands.add(new FCommandAutoWarclaim()); - commands.add(new FCommandBalance()); - commands.add(new FCommandBypass()); - commands.add(new FCommandChat()); - commands.add(new FCommandClaim()); - commands.add(new FCommandConfig()); - commands.add(new FCommandCreate()); - commands.add(new FCommandDeinvite()); - commands.add(new FCommandDeposit()); - commands.add(new FCommandDescription()); - commands.add(new FCommandDisband()); - commands.add(new FCommandHome()); - commands.add(new FCommandInvite()); - commands.add(new FCommandJoin()); - commands.add(new FCommandKick()); - commands.add(new FCommandLeave()); - commands.add(new FCommandList()); - commands.add(new FCommandLock()); - commands.add(new FCommandMap()); - commands.add(new FCommandMod()); - commands.add(new FCommandNoBoom()); - commands.add(new FCommandOpen()); - commands.add(new FCommandOwner()); - commands.add(new FCommandOwnerList()); - commands.add(new FCommandPay()); - commands.add(new FCommandPower()); - commands.add(new FCommandPeaceful()); - commands.add(new FCommandPermanent()); - commands.add(new FCommandRelationAlly()); - commands.add(new FCommandRelationEnemy()); - commands.add(new FCommandRelationNeutral()); - commands.add(new FCommandReload()); - commands.add(new FCommandSafeclaim()); - commands.add(new FCommandSafeunclaimall()); - commands.add(new FCommandSaveAll()); - commands.add(new FCommandSethome()); - commands.add(new FCommandShow()); - commands.add(new FCommandTag()); - commands.add(new FCommandTitle()); - commands.add(new FCommandUnclaim()); - commands.add(new FCommandUnclaimall()); - commands.add(new FCommandVersion()); - commands.add(new FCommandWarclaim()); - commands.add(new FCommandWarunclaimall()); - commands.add(new FCommandWithdraw()); - - // Ensure base folder exists! - this.getDataFolder().mkdirs(); - - Conf.load(); - FPlayer.load(); - Faction.load(); - Board.load(); - - setupPermissions(); - integrateEssentialsChat(); - setupSpout(this); - Econ.setup(this); - Econ.monitorPlugins(); - - if(Conf.worldGuardChecking) { - Worldguard.init(this); - } - - // Register events - PluginManager pm = this.getServer().getPluginManager(); - pm.registerEvent(Event.Type.PLAYER_CHAT, this.playerListener, Event.Priority.Highest, this); - pm.registerEvent(Event.Type.PLAYER_CHAT, this.chatEarlyListener, Event.Priority.Lowest, this); - pm.registerEvent(Event.Type.PLAYER_COMMAND_PREPROCESS, this.playerListener, Event.Priority.Normal, this); - pm.registerEvent(Event.Type.PLAYER_INTERACT, this.playerListener, Event.Priority.Normal, this); - pm.registerEvent(Event.Type.PLAYER_MOVE, this.playerListener, Event.Priority.Normal, this); - pm.registerEvent(Event.Type.PLAYER_JOIN, this.playerListener, Event.Priority.Normal, this); - pm.registerEvent(Event.Type.PLAYER_QUIT, this.playerListener, Event.Priority.Normal, this); - pm.registerEvent(Event.Type.PLAYER_RESPAWN, this.playerListener, Event.Priority.High, this); - pm.registerEvent(Event.Type.PLAYER_BUCKET_EMPTY, this.playerListener, Event.Priority.Normal, this); - pm.registerEvent(Event.Type.PLAYER_BUCKET_FILL, this.playerListener, Event.Priority.Normal, this); - pm.registerEvent(Event.Type.PLAYER_KICK, this.playerListener, Event.Priority.Normal, this); - pm.registerEvent(Event.Type.ENDERMAN_PICKUP, this.entityListener, Event.Priority.Normal, this); - pm.registerEvent(Event.Type.ENDERMAN_PLACE, this.entityListener, Event.Priority.Normal, this); - pm.registerEvent(Event.Type.ENTITY_DEATH, this.entityListener, Event.Priority.Normal, this); - pm.registerEvent(Event.Type.ENTITY_DAMAGE, this.entityListener, Event.Priority.Normal, this); - pm.registerEvent(Event.Type.ENTITY_EXPLODE, this.entityListener, Event.Priority.Normal, this); - pm.registerEvent(Event.Type.CREATURE_SPAWN, this.entityListener, Event.Priority.Normal, this); - pm.registerEvent(Event.Type.ENTITY_TARGET, this.entityListener, Event.Priority.Normal, this); - pm.registerEvent(Event.Type.PAINTING_BREAK, this.entityListener, Event.Priority.Normal, this); - pm.registerEvent(Event.Type.PAINTING_PLACE, this.entityListener, Event.Priority.Normal, this); - pm.registerEvent(Event.Type.BLOCK_BREAK, this.blockListener, Event.Priority.Normal, this); - pm.registerEvent(Event.Type.BLOCK_DAMAGE, this.blockListener, Event.Priority.Normal, this); - pm.registerEvent(Event.Type.BLOCK_PLACE, this.blockListener, Event.Priority.Normal, this); - pm.registerEvent(Event.Type.BLOCK_PISTON_EXTEND, this.blockListener, Event.Priority.Normal, this); - pm.registerEvent(Event.Type.BLOCK_PISTON_RETRACT, this.blockListener, Event.Priority.Normal, this); - - // Register recurring tasks - long saveTicks = 20 * 60 * 30; // Approximately every 30 min - if (saveTask == null) - saveTask = this.getServer().getScheduler().scheduleSyncRepeatingTask(this, new SaveTask(), saveTicks, saveTicks); - - log("=== ENABLE DONE (Took "+(System.currentTimeMillis()-timeInitStart)+"ms) ==="); - } - - @Override - public void onDisable() { - if (saveTask != null) { - this.getServer().getScheduler().cancelTask(saveTask); - saveTask = null; - } - if (gson != null) { - saveAll(); - } - unhookEssentialsChat(); - } - - // -------------------------------------------- // - // Integration with other plugins - // -------------------------------------------- // - - private void setupPermissions() { - if (Permissions != null) { - return; - } - - Plugin test = this.getServer().getPluginManager().getPlugin("Permissions"); - - if (test != null) { - Permissions = ((Permissions)test).getHandler(); - Factions.log("Found and will use "+test.getDescription().getFullName()+" for permissions"); - } else { - Factions.log("Permissions plugin not detected, defaulting to Bukkit superperms system"); - } - } - - private void setupSpout(Factions factions) { - Plugin test = factions.getServer().getPluginManager().getPlugin("Spout"); - - if (test != null && test.isEnabled()) { - SpoutFeatures.setAvailable(true, test.getDescription().getFullName()); - } - } - - private void integrateEssentialsChat() { - if (essChat != null) { - return; - } - - Plugin test = this.getServer().getPluginManager().getPlugin("EssentialsChat"); - - if (test != null && test.isEnabled()) { - essChat = (EssentialsChat)test; - EssentialsFeatures.integrateChat(essChat); - } - } - private void unhookEssentialsChat() { - if (essChat != null) { - EssentialsFeatures.unhookChat(); - } - } - - // -------------------------------------------- // - // Functions for other plugins to hook into - // -------------------------------------------- // - - // This value will be updated whenever new hooks are added - public int hookSupportVersion() { - return 3; - } - - // If another plugin is handling insertion of chat tags, this should be used to notify Factions - public void handleFactionTagExternally(boolean notByFactions) { - Conf.chatTagHandledByAnotherPlugin = notByFactions; - } - - // Simply put, should this chat event be left for Factions to handle? For now, that means players with Faction Chat - // enabled or use of the Factions f command without a slash; combination of isPlayerFactionChatting() and isFactionsCommand() - public boolean shouldLetFactionsHandleThisChat(PlayerChatEvent event) { - if (event == null) - return false; - return (isPlayerFactionChatting(event.getPlayer()) || isFactionsCommand(event.getMessage())); - } - - // Does player have Faction Chat enabled? If so, chat plugins should preferably not do channels, - // local chat, or anything else which targets individual recipients, so Faction Chat can be done - public boolean isPlayerFactionChatting(Player player) { - if (player == null) - return false; - FPlayer me = FPlayer.get(player); - if (me == null) - return false; - return me.getChatMode().isAtLeast(ChatMode.ALLIANCE); - } - - // Is this chat message actually a Factions command, and thus should be left alone by other plugins? - public boolean isFactionsCommand(String check) { - if (check == null || check.isEmpty()) - return false; - return (Conf.allowNoSlashCommand && (check.startsWith(instance.getBaseCommand()+" ") || check.equals(instance.getBaseCommand()))); - } - - // Get a player's faction tag (faction name), mainly for usage by chat plugins for local/channel chat - public String getPlayerFactionTag(Player player) { - return getPlayerFactionTagRelation(player, null); - } - - // Same as above, but with relation (enemy/neutral/ally) coloring potentially added to the tag - public String getPlayerFactionTagRelation(Player speaker, Player listener) { - String tag = "~"; - - if (speaker == null) - return tag; - - FPlayer me = FPlayer.get(speaker); - if (me == null) - return tag; - - // if listener isn't set, or config option is disabled, give back uncolored tag - if (listener == null || !Conf.chatTagRelationColored) { - tag = me.getChatTag().trim(); - } else { - FPlayer you = FPlayer.get(listener); - if (you == null) - tag = me.getChatTag().trim(); - else // everything checks out, give the colored tag - tag = me.getChatTag(you).trim(); - } - if (tag.isEmpty()) - tag = "~"; - - return tag; - } - - // Get a player's title within their faction, mainly for usage by chat plugins for local/channel chat - public String getPlayerTitle(Player player) { - if (player == null) - return ""; - - FPlayer me = FPlayer.get(player); - if (me == null) - return ""; - - return me.getTitle().trim(); - } - - // Get a list of all faction tags (names) - public Set getFactionTags() { - Set tags = new HashSet(); - for (Faction faction : Faction.getAll()) { - tags.add(faction.getTag()); - } - return tags; - } - - // Get a list of all players in the specified faction - public Set getPlayersInFaction(String factionTag) { - Set players = new HashSet(); - Faction faction = Faction.findByTag(factionTag); - if (faction != null) { - for (FPlayer fplayer : faction.getFPlayers()) { - players.add(fplayer.getName()); - } - } - return players; - } - - // Get a list of all online players in the specified faction - public Set getOnlinePlayersInFaction(String factionTag) { - Set players = new HashSet(); - Faction faction = Faction.findByTag(factionTag); - if (faction != null) { - for (FPlayer fplayer : faction.getFPlayersWhereOnline(true)) { - players.add(fplayer.getName()); - } - } - return players; - } - - // check if player is allowed to build/destroy in a particular location - public boolean isPlayerAllowedToBuildHere(Player player, Location location) { - return FactionsBlockListener.playerCanBuildDestroyBlock(player, location, "", true); - } - - // check if player is allowed to interact with the specified block (doors/chests/whatever) - public boolean isPlayerAllowedToInteractWith(Player player, Block block) { - return FactionsPlayerListener.canPlayerUseBlock(player, block, true); - } - - // check if player is allowed to use a specified item (flint&steel, buckets, etc) in a particular location - public boolean isPlayerAllowedToUseThisHere(Player player, Location location, Material material) { - return FactionsPlayerListener.playerCanUseItemHere(player, location, material, true); - } - - // -------------------------------------------- // - // Test rights - // -------------------------------------------- // - - public static boolean hasPermParticipate(CommandSender sender) { - return hasPerm(sender, "factions.participate"); - } - - public static boolean hasPermCreate(CommandSender sender) { - return hasPerm(sender, "factions.create"); - } - - public static boolean hasPermManageSafeZone(CommandSender sender) { - return hasPerm(sender, "factions.manageSafeZone"); - } - - public static boolean hasPermManageWarZone(CommandSender sender) { - return hasPerm(sender, "factions.manageWarZone"); - } - - public static boolean hasPermAdminBypass(CommandSender sender) { - return hasPerm(sender, "factions.adminBypass"); - } - - public static boolean hasPermReload(CommandSender sender) { - return hasPerm(sender, "factions.reload"); - } - - public static boolean hasPermSaveAll(CommandSender sender) { - return hasPerm(sender, "factions.saveall"); - } - - public static boolean hasPermLock(CommandSender sender) { - return hasPerm(sender, "factions.lock"); - } - - public static boolean hasPermConfigure(CommandSender sender) { - return hasPerm(sender, "factions.config"); - } - - public static boolean hasPermDisband(CommandSender sender) { - return hasPerm(sender, "factions.disband"); - } - - public static boolean hasPermViewAnyPower(CommandSender sender) { - return hasPerm(sender, "factions.viewAnyPower"); - } - - public static boolean hasPermOwnershipBypass(CommandSender sender) { - return hasPerm(sender, "factions.ownershipBypass"); - } - - public static boolean hasPermSetPeaceful(CommandSender sender) { - return hasPerm(sender, "factions.setPeaceful"); - } - - public static boolean hasPermSetPermanent(CommandSender sender) { - return hasPerm(sender, "factions.setPermanent"); - } - - public static boolean hasPermPeacefulExplosionToggle(CommandSender sender) { - return hasPerm(sender, "factions.peacefulExplosionToggle"); - } - - public static boolean hasPermViewAnyFactionBalance(CommandSender sender) { - return hasPerm(sender, "factions.viewAnyFactionBalance"); - } - - public static boolean isCommandDisabled(CommandSender sender, String command) { - return (hasPerm(sender, "factions.commandDisable."+command) && !hasPerm(sender, "factions.commandDisable.none")); - } - - private static boolean hasPerm(CommandSender sender, String permNode) { - if (Factions.Permissions == null || ! (sender instanceof Player)) { - return sender.isOp() || sender.hasPermission(permNode); - } - - Player player = (Player)sender; - return Factions.Permissions.has(player, permNode); - } - - // -------------------------------------------- // - // Commands - // -------------------------------------------- // - - @SuppressWarnings("unchecked") - public String getBaseCommand() { - if (this.baseCommand != null) { - return this.baseCommand; - } - - Map Commands = (Map)this.getDescription().getCommands(); - this.baseCommand = Commands.keySet().iterator().next(); - return this.baseCommand; - } - - @Override - public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args) { - List parameters = new ArrayList(Arrays.asList(args)); - this.handleCommand(sender, parameters); - return true; - } - - public void handleCommand(CommandSender sender, List parameters) { - if (parameters.size() == 0) { - this.commands.get(0).execute(sender, parameters); - return; - } - - String commandName = parameters.get(0).toLowerCase(); - parameters.remove(0); - - for (FBaseCommand fcommand : this.commands) { - if (fcommand.getAliases().contains(commandName)) { - fcommand.execute(sender, parameters); - return; - } - } - - sender.sendMessage(Conf.colorSystem+"Unknown faction command \""+commandName+"\". Try "+Conf.colorCommand+"/"+this.getBaseCommand()+" help"); - } - - // -------------------------------------------- // - // Logging - // -------------------------------------------- // - public static void log(String msg) { - log(Level.INFO, msg); - } - - public static void log(Level level, String msg) { - Logger.getLogger("Minecraft").log(level, "["+instance.getDescription().getFullName()+"] "+msg); - } - - // -------------------------------------------- // - // Save all - // -------------------------------------------- // - - public static void saveAll() { - FPlayer.save(); - Faction.save(); - Board.save(); - Conf.save(); - } - -} +package com.massivecraft.factions; + +import java.io.File; +import java.lang.reflect.Type; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.logging.Level; + +import org.bukkit.ChatColor; + +import com.google.gson.reflect.TypeToken; +import com.massivecraft.factions.util.MiscUtil; +import com.massivecraft.factions.zcore.persist.EntityCollection; + +public class Factions extends EntityCollection +{ + public static Factions i = new Factions(); + + P p = P.p; + + private Factions() + { + super + ( + Faction.class, + new CopyOnWriteArrayList(), + new ConcurrentHashMap(), + new File(P.p.getDataFolder(), "factions.json"), + P.p.gson + ); + } + + @Override + public Type getMapType() + { + return new TypeToken>(){}.getType(); + } + + @Override + public boolean loadFromDisc() + { + if ( ! super.loadFromDisc()) return false; + + // Make sure the default neutral faction exists + if ( ! this.exists("0")) + { + Faction faction = this.create("0"); + faction.setTag(ChatColor.DARK_GREEN+"Wilderness"); + faction.setDescription(""); + } + + // Make sure the safe zone faction exists + if ( ! this.exists("-1")) + { + Faction faction = this.create("-1"); + faction.setTag(ChatColor.GOLD+"Safe Zone"); + faction.setDescription("Free from PVP and monsters"); + } + + // Make sure the war zone faction exists + if ( ! this.exists("-2")) + { + Faction faction = this.create("-2"); + faction.setTag(ChatColor.DARK_RED+"War Zone"); + faction.setDescription("Not the safest place to be"); + } + + return true; + } + + + //----------------------------------------------// + // GET + //----------------------------------------------// + + @Override + public Faction get(String id) + { + if ( ! this.exists(id)) + { + p.log(Level.WARNING, "Non existing factionId "+id+" requested! Issuing cleaning!"); + Board.clean(); + FPlayers.i.clean(); + } + + return super.get(id); + } + + public Faction getNone() + { + return this.get("0"); + } + + public Faction getSafeZone() + { + return this.get("-1"); + } + + public Faction getWarZone() + { + return this.get("-2"); + } + + + + + + + //----------------------------------------------// + // Faction tag + //----------------------------------------------// + + public static ArrayList validateTag(String str) + { + ArrayList errors = new ArrayList(); + + if(MiscUtil.getComparisonString(str).length() < Conf.factionTagLengthMin) + { + errors.add(P.p.txt.parse("The faction tag can't be shorter than "+Conf.factionTagLengthMin+ " chars.")); + } + + if(str.length() > Conf.factionTagLengthMax) + { + errors.add(P.p.txt.parse("The faction tag can't be longer than "+Conf.factionTagLengthMax+ " chars.")); + } + + for (char c : str.toCharArray()) + { + if ( ! MiscUtil.substanceChars.contains(String.valueOf(c))) + { + errors.add(P.p.txt.parse("Faction tag must be alphanumeric. \""+c+"\" is not allowed.")); + } + } + + return errors; + } + + public Faction findByTag(String str) + { + String compStr = MiscUtil.getComparisonString(str); + for (Faction faction : this.get()) + { + if (faction.getComparisonTag().equals(compStr)) + { + return faction; + } + } + return null; + } + + public boolean isTagTaken(String str) + { + return this.findByTag(str) != null; + } + +} diff --git a/src/com/massivecraft/factions/P.java b/src/com/massivecraft/factions/P.java new file mode 100644 index 00000000..d2739bf7 --- /dev/null +++ b/src/com/massivecraft/factions/P.java @@ -0,0 +1,512 @@ +package com.massivecraft.factions; + +import java.io.File; +import java.lang.reflect.Modifier; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.bukkit.block.Block; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.event.Event; +import org.bukkit.event.player.PlayerChatEvent; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginManager; + +import com.massivecraft.factions.commands.*; +import com.massivecraft.factions.integration.Econ; +import com.massivecraft.factions.integration.SpoutFeatures; +import com.massivecraft.factions.integration.Worldguard; +import com.massivecraft.factions.listeners.FactionsBlockListener; +import com.massivecraft.factions.listeners.FactionsChatEarlyListener; +import com.massivecraft.factions.listeners.FactionsEntityListener; +import com.massivecraft.factions.listeners.FactionsPlayerListener; +import com.massivecraft.factions.struct.ChatMode; +import com.massivecraft.factions.util.MapFLocToStringSetTypeAdapter; +import com.massivecraft.factions.util.MyLocationTypeAdapter; +import com.massivecraft.factions.zcore.MPlugin; + +import com.nijiko.permissions.PermissionHandler; +import com.nijikokun.bukkit.Permissions.Permissions; +import com.earth2me.essentials.chat.EssentialsChat; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; +import com.massivecraft.factions.integration.EssentialsFeatures; + +/** + * The data is saved to disk every 30min and on plugin disable. + */ +public class P extends MPlugin +{ + // Our single plugin instance + public static P p; + + // Listeners + public final FactionsPlayerListener playerListener; + public final FactionsChatEarlyListener chatEarlyListener; + public final FactionsEntityListener entityListener; + public final FactionsBlockListener blockListener; + + public P() + { + p = this; + this.playerListener = new FactionsPlayerListener(this); + this.chatEarlyListener = new FactionsChatEarlyListener(this); + this.entityListener = new FactionsEntityListener(this); + this.blockListener = new FactionsBlockListener(this); + } + + public static PermissionHandler Permissions; + private static EssentialsChat essChat; + + + @Override + public void onEnable() + { + if ( ! preEnable()) return; + + // Load Conf from disk + Conf.load(); + FPlayers.i.loadFromDisc(); + Factions.i.loadFromDisc(); + Board.load(); + + //setupPermissions(); + integrateEssentialsChat(); + setupSpout(this); + Econ.setup(this); + Econ.monitorPlugins(); + + if(Conf.worldGuardChecking) + { + Worldguard.init(this); + } + + Type mapFLocToStringSetType = new TypeToken>>(){}.getType(); + + // Add the commands + commands.add(new FCommandHelp()); + commands.add(new FCommandAdmin()); + commands.add(new FCommandAutoClaim()); + commands.add(new FCommandAutoSafeclaim()); + commands.add(new FCommandAutoWarclaim()); + commands.add(new FCommandBalance()); + commands.add(new FCommandBypass()); + commands.add(new FCommandChat()); + commands.add(new FCommandClaim()); + commands.add(new FCommandConfig()); + commands.add(new FCommandCreate()); + commands.add(new FCommandDeinvite()); + commands.add(new FCommandDeposit()); + commands.add(new FCommandDescription()); + commands.add(new FCommandDisband()); + commands.add(new FCommandHome()); + commands.add(new FCommandInvite()); + commands.add(new FCommandJoin()); + commands.add(new FCommandKick()); + commands.add(new FCommandLeave()); + commands.add(new FCommandList()); + commands.add(new FCommandLock()); + commands.add(new FCommandMap()); + commands.add(new FCommandMod()); + commands.add(new FCommandNoBoom()); + commands.add(new FCommandOpen()); + commands.add(new FCommandOwner()); + commands.add(new FCommandOwnerList()); + commands.add(new FCommandPay()); + commands.add(new FCommandPower()); + commands.add(new FCommandPeaceful()); + commands.add(new FCommandPermanent()); + commands.add(new FCommandRelationAlly()); + commands.add(new FCommandRelationEnemy()); + commands.add(new FCommandRelationNeutral()); + commands.add(new FCommandReload()); + commands.add(new FCommandSafeclaim()); + commands.add(new FCommandSafeunclaimall()); + commands.add(new FCommandSaveAll()); + commands.add(new FCommandSethome()); + commands.add(new FCommandShow()); + commands.add(new FCommandTag()); + commands.add(new FCommandTitle()); + commands.add(new FCommandUnclaim()); + commands.add(new FCommandUnclaimall()); + commands.add(new FCommandVersion()); + commands.add(new FCommandWarclaim()); + commands.add(new FCommandWarunclaimall()); + commands.add(new FCommandWithdraw()); + + // Register events + PluginManager pm = this.getServer().getPluginManager(); + pm.registerEvent(Event.Type.PLAYER_CHAT, this.playerListener, Event.Priority.Highest, this); + pm.registerEvent(Event.Type.PLAYER_CHAT, this.chatEarlyListener, Event.Priority.Lowest, this); + pm.registerEvent(Event.Type.PLAYER_COMMAND_PREPROCESS, this.playerListener, Event.Priority.Normal, this); + pm.registerEvent(Event.Type.PLAYER_INTERACT, this.playerListener, Event.Priority.Normal, this); + pm.registerEvent(Event.Type.PLAYER_MOVE, this.playerListener, Event.Priority.Normal, this); + pm.registerEvent(Event.Type.PLAYER_JOIN, this.playerListener, Event.Priority.Normal, this); + pm.registerEvent(Event.Type.PLAYER_QUIT, this.playerListener, Event.Priority.Normal, this); + pm.registerEvent(Event.Type.PLAYER_RESPAWN, this.playerListener, Event.Priority.High, this); + pm.registerEvent(Event.Type.PLAYER_BUCKET_EMPTY, this.playerListener, Event.Priority.Normal, this); + pm.registerEvent(Event.Type.PLAYER_BUCKET_FILL, this.playerListener, Event.Priority.Normal, this); + pm.registerEvent(Event.Type.PLAYER_KICK, this.playerListener, Event.Priority.Normal, this); + pm.registerEvent(Event.Type.ENDERMAN_PICKUP, this.entityListener, Event.Priority.Normal, this); + pm.registerEvent(Event.Type.ENDERMAN_PLACE, this.entityListener, Event.Priority.Normal, this); + pm.registerEvent(Event.Type.ENTITY_DEATH, this.entityListener, Event.Priority.Normal, this); + pm.registerEvent(Event.Type.ENTITY_DAMAGE, this.entityListener, Event.Priority.Normal, this); + pm.registerEvent(Event.Type.ENTITY_EXPLODE, this.entityListener, Event.Priority.Normal, this); + pm.registerEvent(Event.Type.CREATURE_SPAWN, this.entityListener, Event.Priority.Normal, this); + pm.registerEvent(Event.Type.ENTITY_TARGET, this.entityListener, Event.Priority.Normal, this); + pm.registerEvent(Event.Type.PAINTING_BREAK, this.entityListener, Event.Priority.Normal, this); + pm.registerEvent(Event.Type.PAINTING_PLACE, this.entityListener, Event.Priority.Normal, this); + pm.registerEvent(Event.Type.BLOCK_BREAK, this.blockListener, Event.Priority.Normal, this); + pm.registerEvent(Event.Type.BLOCK_DAMAGE, this.blockListener, Event.Priority.Normal, this); + pm.registerEvent(Event.Type.BLOCK_PLACE, this.blockListener, Event.Priority.Normal, this); + pm.registerEvent(Event.Type.BLOCK_PISTON_EXTEND, this.blockListener, Event.Priority.Normal, this); + pm.registerEvent(Event.Type.BLOCK_PISTON_RETRACT, this.blockListener, Event.Priority.Normal, this); + + // Register recurring tasks + /*long saveTicks = 20 * 60 * 30; // Approximately every 30 min + if (saveTask == null) + saveTask = this.getServer().getScheduler().scheduleSyncRepeatingTask(this, new SaveTask(), saveTicks, saveTicks); + */ + postEnable(); + } + + @Override + public GsonBuilder getGsonBuilder() + { + return new GsonBuilder() + .setPrettyPrinting() + .disableHtmlEscaping() + .excludeFieldsWithModifiers(Modifier.TRANSIENT, Modifier.VOLATILE); + } + + @Override + public void onDisable() + { + unhookEssentialsChat(); + super.onDisable(); + } + + // -------------------------------------------- // + // Integration with other plugins + // -------------------------------------------- // + + private void setupSpout(P factions) + { + Plugin test = factions.getServer().getPluginManager().getPlugin("Spout"); + + if (test != null && test.isEnabled()) + { + SpoutFeatures.setAvailable(true, test.getDescription().getFullName()); + } + } + + private void integrateEssentialsChat() + { + if (essChat != null) return; + + Plugin test = this.getServer().getPluginManager().getPlugin("EssentialsChat"); + + if (test != null && test.isEnabled()) + { + essChat = (EssentialsChat)test; + EssentialsFeatures.integrateChat(essChat); + } + } + + private void unhookEssentialsChat() + { + if (essChat != null) { + EssentialsFeatures.unhookChat(); + } + } + + // -------------------------------------------- // + // Functions for other plugins to hook into + // -------------------------------------------- // + + // This value will be updated whenever new hooks are added + public int hookSupportVersion() + { + return 3; + } + + // If another plugin is handling insertion of chat tags, this should be used to notify Factions + public void handleFactionTagExternally(boolean notByFactions) + { + Conf.chatTagHandledByAnotherPlugin = notByFactions; + } + + // Simply put, should this chat event be left for Factions to handle? For now, that means players with Faction Chat + // enabled or use of the Factions f command without a slash; combination of isPlayerFactionChatting() and isFactionsCommand() + public boolean shouldLetFactionsHandleThisChat(PlayerChatEvent event) + { + if (event == null) return false; + return (isPlayerFactionChatting(event.getPlayer()) || isFactionsCommand(event.getMessage())); + } + + // Does player have Faction Chat enabled? If so, chat plugins should preferably not do channels, + // local chat, or anything else which targets individual recipients, so Faction Chat can be done + public boolean isPlayerFactionChatting(Player player) + { + if (player == null) return false; + FPlayer me = FPlayers.i.get(player); + + if (me == null)return false; + return me.getChatMode().isAtLeast(ChatMode.ALLIANCE); + } + + // Is this chat message actually a Factions command, and thus should be left alone by other plugins? + public boolean isFactionsCommand(String check) + { + if (check == null || check.isEmpty()) return false; + return (Conf.allowNoSlashCommand && (check.startsWith(p.getBaseCommand()+" ") || check.equals(p.getBaseCommand()))); + } + + // Get a player's faction tag (faction name), mainly for usage by chat plugins for local/channel chat + public String getPlayerFactionTag(Player player) + { + return getPlayerFactionTagRelation(player, null); + } + + // Same as above, but with relation (enemy/neutral/ally) coloring potentially added to the tag + public String getPlayerFactionTagRelation(Player speaker, Player listener) + { + String tag = "~"; + + if (speaker == null) + return tag; + + FPlayer me = FPlayers.i.get(speaker); + if (me == null) + return tag; + + // if listener isn't set, or config option is disabled, give back uncolored tag + if (listener == null || !Conf.chatTagRelationColored) { + tag = me.getChatTag().trim(); + } else { + FPlayer you = FPlayers.i.get(listener); + if (you == null) + tag = me.getChatTag().trim(); + else // everything checks out, give the colored tag + tag = me.getChatTag(you).trim(); + } + if (tag.isEmpty()) + tag = "~"; + + return tag; + } + + // Get a player's title within their faction, mainly for usage by chat plugins for local/channel chat + public String getPlayerTitle(Player player) + { + if (player == null) + return ""; + + FPlayer me = FPlayers.i.get(player); + if (me == null) + return ""; + + return me.getTitle().trim(); + } + + // Get a list of all faction tags (names) + public Set getFactionTags() + { + Set tags = new HashSet(); + for (Faction faction : Factions.i.get()) + { + tags.add(faction.getTag()); + } + return tags; + } + + // Get a list of all players in the specified faction + public Set getPlayersInFaction(String factionTag) + { + Set players = new HashSet(); + Faction faction = Factions.i.findByTag(factionTag); + if (faction != null) + { + for (FPlayer fplayer : faction.getFPlayers()) + { + players.add(fplayer.getName()); + } + } + return players; + } + + // Get a list of all online players in the specified faction + public Set getOnlinePlayersInFaction(String factionTag) + { + Set players = new HashSet(); + Faction faction = Factions.i.findByTag(factionTag); + if (faction != null) + { + for (FPlayer fplayer : faction.getFPlayersWhereOnline(true)) + { + players.add(fplayer.getName()); + } + } + return players; + } + + // check if player is allowed to build/destroy in a particular location + public boolean isPlayerAllowedToBuildHere(Player player, Location location) + { + return FactionsBlockListener.playerCanBuildDestroyBlock(player, location, "", true); + } + + // check if player is allowed to interact with the specified block (doors/chests/whatever) + public boolean isPlayerAllowedToInteractWith(Player player, Block block) + { + return FactionsPlayerListener.canPlayerUseBlock(player, block, true); + } + + // check if player is allowed to use a specified item (flint&steel, buckets, etc) in a particular location + public boolean isPlayerAllowedToUseThisHere(Player player, Location location, Material material) + { + return FactionsPlayerListener.playerCanUseItemHere(player, location, material, true); + } + + // -------------------------------------------- // + // Test rights + // -------------------------------------------- // + + public static boolean hasPermParticipate(CommandSender sender) { + return hasPerm(sender, "factions.participate"); + } + + public static boolean hasPermCreate(CommandSender sender) { + return hasPerm(sender, "factions.create"); + } + + public static boolean hasPermManageSafeZone(CommandSender sender) { + return hasPerm(sender, "factions.manageSafeZone"); + } + + public static boolean hasPermManageWarZone(CommandSender sender) { + return hasPerm(sender, "factions.manageWarZone"); + } + + public static boolean hasPermAdminBypass(CommandSender sender) { + return hasPerm(sender, "factions.adminBypass"); + } + + public static boolean hasPermReload(CommandSender sender) { + return hasPerm(sender, "factions.reload"); + } + + public static boolean hasPermSaveAll(CommandSender sender) { + return hasPerm(sender, "factions.saveall"); + } + + public static boolean hasPermLock(CommandSender sender) { + return hasPerm(sender, "factions.lock"); + } + + public static boolean hasPermConfigure(CommandSender sender) { + return hasPerm(sender, "factions.config"); + } + + public static boolean hasPermDisband(CommandSender sender) { + return hasPerm(sender, "factions.disband"); + } + + public static boolean hasPermViewAnyPower(CommandSender sender) { + return hasPerm(sender, "factions.viewAnyPower"); + } + + public static boolean hasPermOwnershipBypass(CommandSender sender) { + return hasPerm(sender, "factions.ownershipBypass"); + } + + public static boolean hasPermSetPeaceful(CommandSender sender) { + return hasPerm(sender, "factions.setPeaceful"); + } + + public static boolean hasPermSetPermanent(CommandSender sender) { + return hasPerm(sender, "factions.setPermanent"); + } + + public static boolean hasPermPeacefulExplosionToggle(CommandSender sender) { + return hasPerm(sender, "factions.peacefulExplosionToggle"); + } + + public static boolean hasPermViewAnyFactionBalance(CommandSender sender) { + return hasPerm(sender, "factions.viewAnyFactionBalance"); + } + + public static boolean isCommandDisabled(CommandSender sender, String command) { + return (hasPerm(sender, "factions.commandDisable."+command) && !hasPerm(sender, "factions.commandDisable.none")); + } + + private static boolean hasPerm(CommandSender sender, String permNode) { + if (P.Permissions == null || ! (sender instanceof Player)) { + return sender.isOp() || sender.hasPermission(permNode); + } + + Player player = (Player)sender; + return P.Permissions.has(player, permNode); + } + + // -------------------------------------------- // + // Commands + // -------------------------------------------- // + /* + @Override + public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args) + { + List parameters = new ArrayList(Arrays.asList(args)); + this.handleCommand(sender, parameters); + return true; + } + + public void handleCommand(CommandSender sender, List parameters) + { + if (parameters.size() == 0) + { + this.commands.get(0).execute(sender, parameters); + return; + } + + String commandName = parameters.get(0).toLowerCase(); + parameters.remove(0); + + for (FBaseCommand fcommand : this.commands) + { + if (fcommand.getAliases().contains(commandName)) + { + fcommand.execute(sender, parameters); + return; + } + } + + sender.sendMessage(Conf.colorSystem+"Unknown faction command \""+commandName+"\". Try "+Conf.colorCommand+"/"+this.getBaseCommand()+" help"); + } + */ + // -------------------------------------------- // + // Save all + // -------------------------------------------- // + + // TODO: Add a hook to this?? + public static void saveAll() + { + // FPlayer.save(); + // Faction.save(); + Board.save(); + Conf.save(); + } + +} diff --git a/src/com/massivecraft/factions/SaveTask.java b/src/com/massivecraft/factions/SaveTask.java index 38b1d5fc..c187be1a 100644 --- a/src/com/massivecraft/factions/SaveTask.java +++ b/src/com/massivecraft/factions/SaveTask.java @@ -6,7 +6,7 @@ public class SaveTask implements Runnable { @Override public void run() { - Factions.saveAll(); + P.saveAll(); } } diff --git a/src/com/massivecraft/factions/commands/FBaseCommand.java b/src/com/massivecraft/factions/commands/FBaseCommand.java index 9bf0aabc..b008b5d9 100644 --- a/src/com/massivecraft/factions/commands/FBaseCommand.java +++ b/src/com/massivecraft/factions/commands/FBaseCommand.java @@ -12,7 +12,7 @@ import com.massivecraft.factions.Conf; import com.massivecraft.factions.integration.Econ; import com.massivecraft.factions.FPlayer; import com.massivecraft.factions.Faction; -import com.massivecraft.factions.Factions; +import com.massivecraft.factions.P; import com.massivecraft.factions.struct.Role; import com.massivecraft.factions.util.TextUtil; @@ -99,7 +99,7 @@ public class FBaseCommand { // make sure player doesn't have their access to the command revoked Iterator iter = aliases.iterator(); while (iter.hasNext()) { - if (Factions.isCommandDisabled(sender, iter.next())) { + if (P.isCommandDisabled(sender, iter.next())) { sendMessage("You lack the permissions to "+this.helpDescription.toLowerCase()+"."); return false; } @@ -114,7 +114,7 @@ public class FBaseCommand { } public boolean hasPermission(CommandSender sender) { - return Factions.hasPermParticipate(sender); + return P.hasPermParticipate(sender); } // -------------------------------------------- // @@ -126,7 +126,7 @@ public class FBaseCommand { ret += Conf.colorCommand; - ret += Factions.instance.getBaseCommand()+ " " +TextUtil.implode(this.getAliases(), ",")+" "; + ret += P.p.getBaseCommand()+ " " +TextUtil.implode(this.getAliases(), ",")+" "; List parts = new ArrayList(); diff --git a/src/com/massivecraft/factions/commands/FCommandAutoSafeclaim.java b/src/com/massivecraft/factions/commands/FCommandAutoSafeclaim.java index 6125d7c0..4f309389 100644 --- a/src/com/massivecraft/factions/commands/FCommandAutoSafeclaim.java +++ b/src/com/massivecraft/factions/commands/FCommandAutoSafeclaim.java @@ -5,7 +5,7 @@ import org.bukkit.command.CommandSender; import com.massivecraft.factions.Board; import com.massivecraft.factions.FLocation; import com.massivecraft.factions.Faction; -import com.massivecraft.factions.Factions; +import com.massivecraft.factions.P; public class FCommandAutoSafeclaim extends FBaseCommand { @@ -19,7 +19,7 @@ public class FCommandAutoSafeclaim extends FBaseCommand { @Override public boolean hasPermission(CommandSender sender) { - return Factions.hasPermManageSafeZone(sender); + return P.hasPermManageSafeZone(sender); } @Override diff --git a/src/com/massivecraft/factions/commands/FCommandAutoWarclaim.java b/src/com/massivecraft/factions/commands/FCommandAutoWarclaim.java index 4276866c..bd3b1388 100644 --- a/src/com/massivecraft/factions/commands/FCommandAutoWarclaim.java +++ b/src/com/massivecraft/factions/commands/FCommandAutoWarclaim.java @@ -5,7 +5,7 @@ import org.bukkit.command.CommandSender; import com.massivecraft.factions.Board; import com.massivecraft.factions.FLocation; import com.massivecraft.factions.Faction; -import com.massivecraft.factions.Factions; +import com.massivecraft.factions.P; public class FCommandAutoWarclaim extends FBaseCommand { @@ -19,7 +19,7 @@ public class FCommandAutoWarclaim extends FBaseCommand { @Override public boolean hasPermission(CommandSender sender) { - return Factions.hasPermManageWarZone(sender); + return P.hasPermManageWarZone(sender); } @Override diff --git a/src/com/massivecraft/factions/commands/FCommandBalance.java b/src/com/massivecraft/factions/commands/FCommandBalance.java index d511ba65..79af8df2 100644 --- a/src/com/massivecraft/factions/commands/FCommandBalance.java +++ b/src/com/massivecraft/factions/commands/FCommandBalance.java @@ -3,7 +3,7 @@ package com.massivecraft.factions.commands; import com.massivecraft.factions.Conf; import com.massivecraft.factions.integration.Econ; import com.massivecraft.factions.Faction; -import com.massivecraft.factions.Factions; +import com.massivecraft.factions.P; public class FCommandBalance extends FBaseCommand { @@ -30,7 +30,7 @@ public class FCommandBalance extends FBaseCommand { Faction faction; if (parameters.size() > 0) { - if (!Factions.hasPermViewAnyFactionBalance(sender)) { + if (!P.hasPermViewAnyFactionBalance(sender)) { sendMessage("You do not have sufficient permissions to view the bank balance of other factions."); return; } diff --git a/src/com/massivecraft/factions/commands/FCommandBypass.java b/src/com/massivecraft/factions/commands/FCommandBypass.java index b033b42f..bb35c511 100644 --- a/src/com/massivecraft/factions/commands/FCommandBypass.java +++ b/src/com/massivecraft/factions/commands/FCommandBypass.java @@ -3,7 +3,7 @@ package com.massivecraft.factions.commands; import org.bukkit.command.CommandSender; import com.massivecraft.factions.Conf; -import com.massivecraft.factions.Factions; +import com.massivecraft.factions.P; public class FCommandBypass extends FBaseCommand { @@ -16,7 +16,7 @@ public class FCommandBypass extends FBaseCommand { @Override public boolean hasPermission(CommandSender sender) { - return Factions.hasPermAdminBypass(sender); + return P.hasPermAdminBypass(sender); } @Override @@ -24,11 +24,11 @@ public class FCommandBypass extends FBaseCommand { if ( ! Conf.adminBypassPlayers.contains(player.getName())) { Conf.adminBypassPlayers.add(player.getName()); me.sendMessage("You have enabled admin bypass mode. You will be able to build or destroy anywhere."); - Factions.log(player.getName() + " has ENABLED admin bypass mode."); + P.log(player.getName() + " has ENABLED admin bypass mode."); } else { Conf.adminBypassPlayers.remove(player.getName()); me.sendMessage("You have disabled admin bypass mode."); - Factions.log(player.getName() + " DISABLED admin bypass mode."); + P.log(player.getName() + " DISABLED admin bypass mode."); } } } diff --git a/src/com/massivecraft/factions/commands/FCommandConfig.java b/src/com/massivecraft/factions/commands/FCommandConfig.java index 5aa5fd45..7ba1ace7 100644 --- a/src/com/massivecraft/factions/commands/FCommandConfig.java +++ b/src/com/massivecraft/factions/commands/FCommandConfig.java @@ -12,7 +12,7 @@ import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import com.massivecraft.factions.Conf; -import com.massivecraft.factions.Factions; +import com.massivecraft.factions.P; import com.massivecraft.factions.integration.SpoutFeatures; public class FCommandConfig extends FBaseCommand { @@ -32,7 +32,7 @@ public class FCommandConfig extends FBaseCommand { @Override public boolean hasPermission(CommandSender sender) { - return Factions.hasPermConfigure(sender); + return P.hasPermConfigure(sender); } @Override @@ -222,7 +222,7 @@ public class FCommandConfig extends FBaseCommand { if (!success.isEmpty()) { sendMessage(success); if (sender instanceof Player) { - Factions.log(success + " Command was run by "+player.getName()+"."); + P.log(success + " Command was run by "+player.getName()+"."); } } // save change to disk diff --git a/src/com/massivecraft/factions/commands/FCommandCreate.java b/src/com/massivecraft/factions/commands/FCommandCreate.java index bba362e4..5cac75a9 100644 --- a/src/com/massivecraft/factions/commands/FCommandCreate.java +++ b/src/com/massivecraft/factions/commands/FCommandCreate.java @@ -7,7 +7,7 @@ import org.bukkit.command.CommandSender; import com.massivecraft.factions.Conf; import com.massivecraft.factions.FPlayer; import com.massivecraft.factions.Faction; -import com.massivecraft.factions.Factions; +import com.massivecraft.factions.P; import com.massivecraft.factions.struct.Role; @@ -23,7 +23,7 @@ public class FCommandCreate extends FBaseCommand { @Override public boolean hasPermission(CommandSender sender) { - return Factions.hasPermCreate(sender); + return P.hasPermCreate(sender); } @Override diff --git a/src/com/massivecraft/factions/commands/FCommandDeposit.java b/src/com/massivecraft/factions/commands/FCommandDeposit.java index 900124af..43546e00 100644 --- a/src/com/massivecraft/factions/commands/FCommandDeposit.java +++ b/src/com/massivecraft/factions/commands/FCommandDeposit.java @@ -3,7 +3,7 @@ package com.massivecraft.factions.commands; import com.massivecraft.factions.Conf; import com.massivecraft.factions.integration.Econ; import com.massivecraft.factions.Faction; -import com.massivecraft.factions.Factions; +import com.massivecraft.factions.P; import com.massivecraft.factions.FPlayer; @@ -49,7 +49,7 @@ public class FCommandDeposit extends FBaseCommand { faction.addMoney(amount); sendMessage("You have deposited "+amountString+" into "+faction.getTag()+"'s bank."); sendMessage(faction.getTag()+" now has "+Econ.moneyString(faction.getMoney())); - Factions.log(player.getName() + " deposited "+amountString+" into "+faction.getTag()+"'s bank."); + P.log(player.getName() + " deposited "+amountString+" into "+faction.getTag()+"'s bank."); for (FPlayer fplayer : FPlayer.getAllOnline()) { if (fplayer.getFaction() == faction) { diff --git a/src/com/massivecraft/factions/commands/FCommandDisband.java b/src/com/massivecraft/factions/commands/FCommandDisband.java index 688decec..72d78841 100644 --- a/src/com/massivecraft/factions/commands/FCommandDisband.java +++ b/src/com/massivecraft/factions/commands/FCommandDisband.java @@ -3,7 +3,7 @@ package com.massivecraft.factions.commands; import com.massivecraft.factions.Conf; import com.massivecraft.factions.integration.Econ; import com.massivecraft.factions.Faction; -import com.massivecraft.factions.Factions; +import com.massivecraft.factions.P; import com.massivecraft.factions.FPlayer; import com.massivecraft.factions.integration.SpoutFeatures; import com.massivecraft.factions.struct.Role; @@ -34,7 +34,7 @@ public class FCommandDisband extends FBaseCommand { return; } - if ( ! Factions.hasPermDisband(sender)) { + if ( ! P.hasPermDisband(sender)) { if (me.getFaction() == faction) { parameters.clear(); } @@ -54,7 +54,7 @@ public class FCommandDisband extends FBaseCommand { faction = me.getFaction(); - if (faction.isPermanent() && !Factions.hasPermDisband(sender)) { + if (faction.isPermanent() && !P.hasPermDisband(sender)) { sendMessage("Your faction is designated as permanent, so you cannot disband it."); return; } @@ -75,7 +75,7 @@ public class FCommandDisband extends FBaseCommand { if (amount > 0.0) { String amountString = Econ.moneyString(amount); sendMessage("You have been given the disbanded faction's bank, totaling "+amountString+"."); - Factions.log(player.getName() + " has been given bank holdings of "+amountString+" from disbanding "+faction.getTag()+"."); + P.log(player.getName() + " has been given bank holdings of "+amountString+" from disbanding "+faction.getTag()+"."); } } diff --git a/src/com/massivecraft/factions/commands/FCommandKick.java b/src/com/massivecraft/factions/commands/FCommandKick.java index a342456f..b68f4fb4 100644 --- a/src/com/massivecraft/factions/commands/FCommandKick.java +++ b/src/com/massivecraft/factions/commands/FCommandKick.java @@ -3,7 +3,7 @@ package com.massivecraft.factions.commands; import com.massivecraft.factions.Conf; import com.massivecraft.factions.FPlayer; import com.massivecraft.factions.Faction; -import com.massivecraft.factions.Factions; +import com.massivecraft.factions.P; public class FCommandKick extends FBaseCommand { @@ -40,7 +40,7 @@ public class FCommandKick extends FBaseCommand { Faction myFaction = me.getFaction(); // players with admin-level "disband" permission can bypass these requirements - if (!Factions.hasPermDisband(sender)) { + if (!P.hasPermDisband(sender)) { if (yourFaction != myFaction) { sendMessage(you.getNameAndRelevant(me)+Conf.colorSystem+" is not a member of "+myFaction.getTag(me)); return; diff --git a/src/com/massivecraft/factions/commands/FCommandLock.java b/src/com/massivecraft/factions/commands/FCommandLock.java index 4b690a54..5bfcd172 100644 --- a/src/com/massivecraft/factions/commands/FCommandLock.java +++ b/src/com/massivecraft/factions/commands/FCommandLock.java @@ -2,7 +2,7 @@ package com.massivecraft.factions.commands; import org.bukkit.command.CommandSender; -import com.massivecraft.factions.Factions; +import com.massivecraft.factions.P; public class FCommandLock extends FBaseCommand { @@ -18,7 +18,7 @@ public class FCommandLock extends FBaseCommand { @Override public boolean hasPermission(CommandSender sender) { - return Factions.hasPermLock(sender); + return P.hasPermLock(sender); } @Override diff --git a/src/com/massivecraft/factions/commands/FCommandNoBoom.java b/src/com/massivecraft/factions/commands/FCommandNoBoom.java index 38a7fc41..645cfa91 100644 --- a/src/com/massivecraft/factions/commands/FCommandNoBoom.java +++ b/src/com/massivecraft/factions/commands/FCommandNoBoom.java @@ -4,7 +4,7 @@ import org.bukkit.command.CommandSender; import com.massivecraft.factions.Conf; import com.massivecraft.factions.Faction; -import com.massivecraft.factions.Factions; +import com.massivecraft.factions.P; import com.massivecraft.factions.struct.Role; public class FCommandNoBoom extends FBaseCommand { @@ -17,7 +17,7 @@ public class FCommandNoBoom extends FBaseCommand { @Override public boolean hasPermission(CommandSender sender) { - return Factions.hasPermPeacefulExplosionToggle(sender); + return P.hasPermPeacefulExplosionToggle(sender); } @Override diff --git a/src/com/massivecraft/factions/commands/FCommandOwner.java b/src/com/massivecraft/factions/commands/FCommandOwner.java index 0b1b5860..d36d0fd2 100644 --- a/src/com/massivecraft/factions/commands/FCommandOwner.java +++ b/src/com/massivecraft/factions/commands/FCommandOwner.java @@ -4,7 +4,7 @@ import com.massivecraft.factions.Board; import com.massivecraft.factions.Conf; import com.massivecraft.factions.FLocation; import com.massivecraft.factions.Faction; -import com.massivecraft.factions.Factions; +import com.massivecraft.factions.P; import com.massivecraft.factions.FPlayer; import com.massivecraft.factions.struct.Role; @@ -21,7 +21,7 @@ public class FCommandOwner extends FBaseCommand { @Override public void perform() { - boolean hasBypass = Factions.hasPermAdminBypass(player); + boolean hasBypass = P.hasPermAdminBypass(player); if ( ! hasBypass && ! assertHasFaction()) { return; diff --git a/src/com/massivecraft/factions/commands/FCommandOwnerList.java b/src/com/massivecraft/factions/commands/FCommandOwnerList.java index 6e0bc8a1..f71f0ad3 100644 --- a/src/com/massivecraft/factions/commands/FCommandOwnerList.java +++ b/src/com/massivecraft/factions/commands/FCommandOwnerList.java @@ -7,7 +7,7 @@ import com.massivecraft.factions.Board; import com.massivecraft.factions.Conf; import com.massivecraft.factions.FLocation; import com.massivecraft.factions.Faction; -import com.massivecraft.factions.Factions; +import com.massivecraft.factions.P; public class FCommandOwnerList extends FBaseCommand { @@ -20,7 +20,7 @@ public class FCommandOwnerList extends FBaseCommand { @Override public void perform() { - boolean hasBypass = Factions.hasPermAdminBypass(player); + boolean hasBypass = P.hasPermAdminBypass(player); if ( ! hasBypass && ! assertHasFaction()) { return; diff --git a/src/com/massivecraft/factions/commands/FCommandPay.java b/src/com/massivecraft/factions/commands/FCommandPay.java index 24e4e9b0..808b7d8a 100644 --- a/src/com/massivecraft/factions/commands/FCommandPay.java +++ b/src/com/massivecraft/factions/commands/FCommandPay.java @@ -3,7 +3,7 @@ package com.massivecraft.factions.commands; import com.massivecraft.factions.Conf; import com.massivecraft.factions.integration.Econ; import com.massivecraft.factions.Faction; -import com.massivecraft.factions.Factions; +import com.massivecraft.factions.P; import com.massivecraft.factions.FPlayer; import com.massivecraft.factions.struct.Role; @@ -63,7 +63,7 @@ public class FCommandPay extends FBaseCommand { them.addMoney(amount); sendMessage("You have paid "+amountString+" from "+us.getTag()+"'s bank to "+them.getTag()+"'s bank."); sendMessage(us.getTag()+" now has "+Econ.moneyString(us.getMoney())); - Factions.log(player.getName() + " paid "+amountString+" from "+us.getTag()+"'s bank to "+them.getTag()+"'s bank."); + P.log(player.getName() + " paid "+amountString+" from "+us.getTag()+"'s bank to "+them.getTag()+"'s bank."); for (FPlayer fplayer : FPlayer.getAllOnline()) { if (fplayer.getFaction() == us || fplayer.getFaction() == them) { diff --git a/src/com/massivecraft/factions/commands/FCommandPeaceful.java b/src/com/massivecraft/factions/commands/FCommandPeaceful.java index aeeac60f..dda5c80f 100644 --- a/src/com/massivecraft/factions/commands/FCommandPeaceful.java +++ b/src/com/massivecraft/factions/commands/FCommandPeaceful.java @@ -4,7 +4,7 @@ import org.bukkit.command.CommandSender; import com.massivecraft.factions.Conf; import com.massivecraft.factions.Faction; -import com.massivecraft.factions.Factions; +import com.massivecraft.factions.P; import com.massivecraft.factions.FPlayer; import com.massivecraft.factions.integration.SpoutFeatures; @@ -22,7 +22,7 @@ public class FCommandPeaceful extends FBaseCommand { @Override public boolean hasPermission(CommandSender sender) { - return Factions.hasPermSetPeaceful(sender); + return P.hasPermSetPeaceful(sender); } @Override diff --git a/src/com/massivecraft/factions/commands/FCommandPermanent.java b/src/com/massivecraft/factions/commands/FCommandPermanent.java index 680c3cab..460525c4 100644 --- a/src/com/massivecraft/factions/commands/FCommandPermanent.java +++ b/src/com/massivecraft/factions/commands/FCommandPermanent.java @@ -4,7 +4,7 @@ import org.bukkit.command.CommandSender; import com.massivecraft.factions.Conf; import com.massivecraft.factions.Faction; -import com.massivecraft.factions.Factions; +import com.massivecraft.factions.P; import com.massivecraft.factions.FPlayer; @@ -22,7 +22,7 @@ public class FCommandPermanent extends FBaseCommand { @Override public boolean hasPermission(CommandSender sender) { - return Factions.hasPermSetPermanent(sender); + return P.hasPermSetPermanent(sender); } @Override diff --git a/src/com/massivecraft/factions/commands/FCommandPower.java b/src/com/massivecraft/factions/commands/FCommandPower.java index a8d29dfe..da53504a 100644 --- a/src/com/massivecraft/factions/commands/FCommandPower.java +++ b/src/com/massivecraft/factions/commands/FCommandPower.java @@ -4,7 +4,7 @@ import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import com.massivecraft.factions.Conf; -import com.massivecraft.factions.Factions; +import com.massivecraft.factions.P; import com.massivecraft.factions.FPlayer; @@ -30,7 +30,7 @@ public class FCommandPower extends FBaseCommand { public void perform() { FPlayer target; if (parameters.size() > 0) { - if (!Factions.hasPermViewAnyPower(player)) { + if (!P.hasPermViewAnyPower(player)) { me.sendMessage("You do not have the appropriate permission to view another player's power level."); return; } diff --git a/src/com/massivecraft/factions/commands/FCommandReload.java b/src/com/massivecraft/factions/commands/FCommandReload.java index e78cf511..a6c3cb02 100644 --- a/src/com/massivecraft/factions/commands/FCommandReload.java +++ b/src/com/massivecraft/factions/commands/FCommandReload.java @@ -6,7 +6,7 @@ import com.massivecraft.factions.Board; import com.massivecraft.factions.Conf; import com.massivecraft.factions.FPlayer; import com.massivecraft.factions.Faction; -import com.massivecraft.factions.Factions; +import com.massivecraft.factions.P; public class FCommandReload extends FBaseCommand { @@ -22,12 +22,12 @@ public class FCommandReload extends FBaseCommand { @Override public boolean hasPermission(CommandSender sender) { - return Factions.hasPermReload(sender); + return P.hasPermReload(sender); } @Override public void perform() { - Factions.log("=== RELOAD START ==="); + P.log("=== RELOAD START ==="); long timeInitStart = System.currentTimeMillis(); String fileName = "s"; @@ -35,33 +35,33 @@ public class FCommandReload extends FBaseCommand { if (parameters.size() > 0) { String file = parameters.get(0); if (file.equalsIgnoreCase("conf") || file.equalsIgnoreCase("conf.json")) { - Factions.log("RELOADING CONF.JSON"); + P.log("RELOADING CONF.JSON"); Conf.load(); fileName = " conf.json"; } else if (file.equalsIgnoreCase("board") || file.equalsIgnoreCase("board.json")) { - Factions.log("RELOADING BOARD.JSON"); + P.log("RELOADING BOARD.JSON"); Board.load(); fileName = " board.json"; } else if (file.equalsIgnoreCase("factions") || file.equalsIgnoreCase("factions.json")) { - Factions.log("RELOADING FACTIONS.JSON"); + P.log("RELOADING FACTIONS.JSON"); Faction.load(); fileName = " factions.json"; } else if (file.equalsIgnoreCase("players") || file.equalsIgnoreCase("players.json")) { - Factions.log("RELOADING PLAYERS.JSON"); + P.log("RELOADING PLAYERS.JSON"); FPlayer.load(); fileName = " players.json"; } else { - Factions.log("RELOAD CANCELLED - SPECIFIED FILE INVALID"); + P.log("RELOAD CANCELLED - SPECIFIED FILE INVALID"); sendMessage("Invalid file specified. Valid files: conf, board, factions, players."); return; } } else { - Factions.log("RELOADING ALL FILES"); + P.log("RELOADING ALL FILES"); Conf.load(); FPlayer.load(); Faction.load(); @@ -69,7 +69,7 @@ public class FCommandReload extends FBaseCommand { } long timeReload = (System.currentTimeMillis()-timeInitStart); - Factions.log("=== RELOAD DONE (Took "+timeReload+"ms) ==="); + P.log("=== RELOAD DONE (Took "+timeReload+"ms) ==="); sendMessage("Factions file" + fileName + " reloaded from disk, took " + timeReload + "ms"); } diff --git a/src/com/massivecraft/factions/commands/FCommandSafeclaim.java b/src/com/massivecraft/factions/commands/FCommandSafeclaim.java index 7ca8d110..b650fc0e 100644 --- a/src/com/massivecraft/factions/commands/FCommandSafeclaim.java +++ b/src/com/massivecraft/factions/commands/FCommandSafeclaim.java @@ -5,7 +5,7 @@ import org.bukkit.command.CommandSender; import com.massivecraft.factions.Board; import com.massivecraft.factions.FLocation; import com.massivecraft.factions.Faction; -import com.massivecraft.factions.Factions; +import com.massivecraft.factions.P; public class FCommandSafeclaim extends FBaseCommand { @@ -20,7 +20,7 @@ public class FCommandSafeclaim extends FBaseCommand { @Override public boolean hasPermission(CommandSender sender) { - return Factions.hasPermManageSafeZone(sender); + return P.hasPermManageSafeZone(sender); } @Override diff --git a/src/com/massivecraft/factions/commands/FCommandSafeunclaimall.java b/src/com/massivecraft/factions/commands/FCommandSafeunclaimall.java index 2f977640..a70b3429 100644 --- a/src/com/massivecraft/factions/commands/FCommandSafeunclaimall.java +++ b/src/com/massivecraft/factions/commands/FCommandSafeunclaimall.java @@ -4,7 +4,7 @@ import org.bukkit.command.CommandSender; import com.massivecraft.factions.Board; import com.massivecraft.factions.Faction; -import com.massivecraft.factions.Factions; +import com.massivecraft.factions.P; public class FCommandSafeunclaimall extends FBaseCommand { @@ -17,7 +17,7 @@ public class FCommandSafeunclaimall extends FBaseCommand { @Override public boolean hasPermission(CommandSender sender) { - return Factions.hasPermManageSafeZone(sender); + return P.hasPermManageSafeZone(sender); } @Override diff --git a/src/com/massivecraft/factions/commands/FCommandSaveAll.java b/src/com/massivecraft/factions/commands/FCommandSaveAll.java index aafc19ab..d5a494ba 100644 --- a/src/com/massivecraft/factions/commands/FCommandSaveAll.java +++ b/src/com/massivecraft/factions/commands/FCommandSaveAll.java @@ -2,7 +2,7 @@ package com.massivecraft.factions.commands; import org.bukkit.command.CommandSender; -import com.massivecraft.factions.Factions; +import com.massivecraft.factions.P; public class FCommandSaveAll extends FBaseCommand { @@ -17,12 +17,12 @@ public class FCommandSaveAll extends FBaseCommand { @Override public boolean hasPermission(CommandSender sender) { - return Factions.hasPermSaveAll(sender); + return P.hasPermSaveAll(sender); } @Override public void perform() { - Factions.saveAll(); + P.saveAll(); sendMessage("Factions saved to disk!"); } diff --git a/src/com/massivecraft/factions/commands/FCommandSethome.java b/src/com/massivecraft/factions/commands/FCommandSethome.java index 9a1d77d8..0882b1b8 100644 --- a/src/com/massivecraft/factions/commands/FCommandSethome.java +++ b/src/com/massivecraft/factions/commands/FCommandSethome.java @@ -2,7 +2,7 @@ package com.massivecraft.factions.commands; import com.massivecraft.factions.Conf; import com.massivecraft.factions.Faction; -import com.massivecraft.factions.Factions; +import com.massivecraft.factions.P; import com.massivecraft.factions.struct.Role; public class FCommandSethome extends FBaseCommand { @@ -38,7 +38,7 @@ public class FCommandSethome extends FBaseCommand { Faction myFaction = me.getFaction(); if (parameters.size() > 0) { - if (!Factions.hasPermAdminBypass(player)) { + if (!P.hasPermAdminBypass(player)) { me.sendMessage("You cannot set the home of another faction without adminBypass permission."); return; } @@ -51,7 +51,7 @@ public class FCommandSethome extends FBaseCommand { } } - if (Conf.homesMustBeInClaimedTerritory && !me.isInOwnTerritory() && !Factions.hasPermAdminBypass(player)) { + if (Conf.homesMustBeInClaimedTerritory && !me.isInOwnTerritory() && !P.hasPermAdminBypass(player)) { me.sendMessage("Sorry, your faction home can only be set inside your own claimed territory."); return; } diff --git a/src/com/massivecraft/factions/commands/FCommandUnclaim.java b/src/com/massivecraft/factions/commands/FCommandUnclaim.java index d7991a96..673a690a 100644 --- a/src/com/massivecraft/factions/commands/FCommandUnclaim.java +++ b/src/com/massivecraft/factions/commands/FCommandUnclaim.java @@ -5,7 +5,7 @@ import com.massivecraft.factions.Conf; import com.massivecraft.factions.integration.Econ; import com.massivecraft.factions.FLocation; import com.massivecraft.factions.Faction; -import com.massivecraft.factions.Factions; +import com.massivecraft.factions.P; import com.massivecraft.factions.struct.Role; public class FCommandUnclaim extends FBaseCommand { @@ -29,7 +29,7 @@ public class FCommandUnclaim extends FBaseCommand { Faction otherFaction = Board.getFactionAt(flocation); if (otherFaction.isSafeZone()) { - if (Factions.hasPermManageSafeZone(sender)) { + if (P.hasPermManageSafeZone(sender)) { Board.removeAt(flocation); sendMessage("Safe zone was unclaimed."); } else { @@ -38,7 +38,7 @@ public class FCommandUnclaim extends FBaseCommand { return; } else if (otherFaction.isWarZone()) { - if (Factions.hasPermManageWarZone(sender)) { + if (P.hasPermManageWarZone(sender)) { Board.removeAt(flocation); sendMessage("War zone was unclaimed."); } else { diff --git a/src/com/massivecraft/factions/commands/FCommandVersion.java b/src/com/massivecraft/factions/commands/FCommandVersion.java index e38e3cd0..80219901 100644 --- a/src/com/massivecraft/factions/commands/FCommandVersion.java +++ b/src/com/massivecraft/factions/commands/FCommandVersion.java @@ -2,7 +2,7 @@ package com.massivecraft.factions.commands; import org.bukkit.command.CommandSender; -import com.massivecraft.factions.Factions; +import com.massivecraft.factions.P; public class FCommandVersion extends FBaseCommand { @@ -22,7 +22,7 @@ public class FCommandVersion extends FBaseCommand { @Override public void perform() { - sendMessage("You are running "+Factions.instance.getDescription().getFullName()); + sendMessage("You are running "+P.p.getDescription().getFullName()); } } diff --git a/src/com/massivecraft/factions/commands/FCommandWarclaim.java b/src/com/massivecraft/factions/commands/FCommandWarclaim.java index 2643430a..aaa6e3d3 100644 --- a/src/com/massivecraft/factions/commands/FCommandWarclaim.java +++ b/src/com/massivecraft/factions/commands/FCommandWarclaim.java @@ -5,7 +5,7 @@ import org.bukkit.command.CommandSender; import com.massivecraft.factions.Board; import com.massivecraft.factions.FLocation; import com.massivecraft.factions.Faction; -import com.massivecraft.factions.Factions; +import com.massivecraft.factions.P; public class FCommandWarclaim extends FBaseCommand { @@ -20,7 +20,7 @@ public class FCommandWarclaim extends FBaseCommand { @Override public boolean hasPermission(CommandSender sender) { - return Factions.hasPermManageWarZone(sender); + return P.hasPermManageWarZone(sender); } public void perform() { diff --git a/src/com/massivecraft/factions/commands/FCommandWarunclaimall.java b/src/com/massivecraft/factions/commands/FCommandWarunclaimall.java index ad38d203..165a1bed 100644 --- a/src/com/massivecraft/factions/commands/FCommandWarunclaimall.java +++ b/src/com/massivecraft/factions/commands/FCommandWarunclaimall.java @@ -4,7 +4,7 @@ import org.bukkit.command.CommandSender; import com.massivecraft.factions.Board; import com.massivecraft.factions.Faction; -import com.massivecraft.factions.Factions; +import com.massivecraft.factions.P; public class FCommandWarunclaimall extends FBaseCommand { @@ -17,7 +17,7 @@ public class FCommandWarunclaimall extends FBaseCommand { @Override public boolean hasPermission(CommandSender sender) { - return Factions.hasPermManageWarZone(sender); + return P.hasPermManageWarZone(sender); } @Override diff --git a/src/com/massivecraft/factions/commands/FCommandWithdraw.java b/src/com/massivecraft/factions/commands/FCommandWithdraw.java index 5272e057..eaf73e22 100644 --- a/src/com/massivecraft/factions/commands/FCommandWithdraw.java +++ b/src/com/massivecraft/factions/commands/FCommandWithdraw.java @@ -3,7 +3,7 @@ package com.massivecraft.factions.commands; import com.massivecraft.factions.Conf; import com.massivecraft.factions.integration.Econ; import com.massivecraft.factions.Faction; -import com.massivecraft.factions.Factions; +import com.massivecraft.factions.P; import com.massivecraft.factions.FPlayer; import com.massivecraft.factions.struct.Role; @@ -55,7 +55,7 @@ public class FCommandWithdraw extends FBaseCommand { Econ.addMoney(me.getName(), amount); sendMessage("You have withdrawn "+amountString+" from "+faction.getTag()+"'s bank."); sendMessage(faction.getTag()+" now has "+Econ.moneyString(faction.getMoney())); - Factions.log(player.getName() + " withdrew "+amountString+" from "+faction.getTag()+"'s bank."); + P.log(player.getName() + " withdrew "+amountString+" from "+faction.getTag()+"'s bank."); for (FPlayer fplayer : FPlayer.getAllOnline()) { if (fplayer.getFaction() == faction) { diff --git a/src/com/massivecraft/factions/commands/FRelationCommand.java b/src/com/massivecraft/factions/commands/FRelationCommand.java index 7993904d..d0a90bda 100644 --- a/src/com/massivecraft/factions/commands/FRelationCommand.java +++ b/src/com/massivecraft/factions/commands/FRelationCommand.java @@ -4,7 +4,7 @@ import org.bukkit.ChatColor; import com.massivecraft.factions.Conf; import com.massivecraft.factions.Faction; -import com.massivecraft.factions.Factions; +import com.massivecraft.factions.P; import com.massivecraft.factions.integration.SpoutFeatures; import com.massivecraft.factions.struct.Relation; import com.massivecraft.factions.struct.Role; @@ -62,7 +62,7 @@ public class FRelationCommand extends FBaseCommand { myFaction.sendMessage(Conf.colorSystem+"Your faction is now "+currentRelationColor+whishedRelation.toString()+Conf.colorSystem+" to "+currentRelationColor+otherFaction.getTag()); } else { otherFaction.sendMessage(currentRelationColor+myFaction.getTag()+Conf.colorSystem+ " wishes to be your "+whishedRelation.getColor()+whishedRelation.toString()); - otherFaction.sendMessage(Conf.colorSystem+"Type "+Conf.colorCommand+Factions.instance.getBaseCommand()+" "+whishedRelation+" "+myFaction.getTag()+Conf.colorSystem+" to accept."); + otherFaction.sendMessage(Conf.colorSystem+"Type "+Conf.colorCommand+P.p.getBaseCommand()+" "+whishedRelation+" "+myFaction.getTag()+Conf.colorSystem+" to accept."); myFaction.sendMessage(currentRelationColor+otherFaction.getTag()+Conf.colorSystem+ " were informed that you wish to be "+whishedRelation.getColor()+whishedRelation); } if (!whishedRelation.isNeutral() && otherFaction.isPeaceful()) { diff --git a/src/com/massivecraft/factions/integration/Econ.java b/src/com/massivecraft/factions/integration/Econ.java index 31f0d369..7307c393 100644 --- a/src/com/massivecraft/factions/integration/Econ.java +++ b/src/com/massivecraft/factions/integration/Econ.java @@ -12,7 +12,7 @@ import com.nijikokun.register.payment.Method.MethodAccount; import com.iConomy.*; import com.iConomy.system.*; import com.massivecraft.factions.Conf; -import com.massivecraft.factions.Factions; +import com.massivecraft.factions.P; public class Econ { @@ -21,11 +21,11 @@ public class Econ { private static boolean essEcoUse = false; public static void monitorPlugins() { - Factions.instance.getServer().getPluginManager().registerEvent(Event.Type.PLUGIN_ENABLE, new FactionsServerListener(), Event.Priority.Monitor, Factions.instance); - Factions.instance.getServer().getPluginManager().registerEvent(Event.Type.PLUGIN_DISABLE, new FactionsServerListener(), Event.Priority.Monitor, Factions.instance); + P.p.getServer().getPluginManager().registerEvent(Event.Type.PLUGIN_ENABLE, new FactionsServerListener(P.p), Event.Priority.Monitor, P.p); + P.p.getServer().getPluginManager().registerEvent(Event.Type.PLUGIN_DISABLE, new FactionsServerListener(P.p), Event.Priority.Monitor, P.p); } - public static void setup(Factions factions) { + public static void setup(P factions) { if (enabled()) { return; } @@ -50,80 +50,96 @@ public class Econ { } } - public static void registerSet(boolean enable) { + public static void registerSet(boolean enable) + { registerUse = enable; if (enable) { - Factions.log("Register hook available, "+(Conf.econRegisterEnabled ? "and interface is enabled" : "but disabled (\"econRegisterEnabled\": false)")+"."); + P.p.log("Register hook available, "+(Conf.econRegisterEnabled ? "and interface is enabled" : "but disabled (\"econRegisterEnabled\": false)")+"."); } else { - Factions.log("Un-hooked from Register."); + P.p.log("Un-hooked from Register."); } FCommandHelp.updateHelp(); } - public static void iConomySet(boolean enable) { + public static void iConomySet(boolean enable) + { iConomyUse = enable; if (enable && !registerUse) { - Factions.log("iConomy hook available, "+(Conf.econIConomyEnabled ? "and interface is enabled" : "but disabled (\"econIConomyEnabled\": false)")+"."); + P.p.log("iConomy hook available, "+(Conf.econIConomyEnabled ? "and interface is enabled" : "but disabled (\"econIConomyEnabled\": false)")+"."); } else { - Factions.log("Un-hooked from iConomy."); + P.p.log("Un-hooked from iConomy."); } FCommandHelp.updateHelp(); } - public static void essentialsEcoSet(boolean enable) { + public static void essentialsEcoSet(boolean enable) + { essEcoUse = enable; - if (enable && !registerUse) { - Factions.log("EssentialsEco hook available, "+(Conf.econEssentialsEcoEnabled ? "and interface is enabled" : "but disabled (\"econEssentialsEcoEnabled\": false)")+"."); + if (enable && !registerUse) + { + P.p.log("EssentialsEco hook available, "+(Conf.econEssentialsEcoEnabled ? "and interface is enabled" : "but disabled (\"econEssentialsEcoEnabled\": false)")+"."); } - else { - Factions.log("Un-hooked from EssentialsEco."); + else + { + P.p.log("Un-hooked from EssentialsEco."); } FCommandHelp.updateHelp(); } - public static boolean registerHooked() { + public static boolean registerHooked() + { return registerUse; } - public static boolean iConomyHooked() { + public static boolean iConomyHooked() + { return iConomyUse; } - public static boolean essentialsEcoHooked() { + public static boolean essentialsEcoHooked() + { return essEcoUse; } - public static boolean registerAvailable() { + public static boolean registerAvailable() + { return Conf.econRegisterEnabled && registerUse && Methods.hasMethod(); } // If economy is enabled in conf.json, and we're successfully hooked into an economy plugin - public static boolean enabled() { - return (Conf.econRegisterEnabled && registerUse && Methods.hasMethod()) + public static boolean enabled() + { + return (Conf.econRegisterEnabled && registerUse && Methods.hasMethod()) || (Conf.econIConomyEnabled && iConomyUse) || (Conf.econEssentialsEcoEnabled && essEcoUse); } // mainly for internal use, for a little less code repetition - public static Holdings getIconomyHoldings(String playerName) { - if (!enabled()) { + public static Holdings getIconomyHoldings(String playerName) + { + if ( ! enabled()) + { return null; } Account account = iConomy.getAccount(playerName); - if (account == null) { + if (account == null) + { return null; } Holdings holdings = account.getHoldings(); return holdings; } - public static MethodAccount getRegisterAccount(String playerName) { - if (!enabled()) { + public static MethodAccount getRegisterAccount(String playerName) + { + if (!enabled()) + { return null; } - if (!Methods.getMethod().hasAccount(playerName)) { + if (!Methods.getMethod().hasAccount(playerName)) + { return null; } @@ -133,7 +149,8 @@ public class Econ { // format money string based on server's set currency type, like "24 gold" or "$24.50" - public static String moneyString(double amount) { + public static String moneyString(double amount) + { return registerAvailable() ? Methods.getMethod().format(amount) : (iConomyUse ? iConomy.format(amount) : Economy.format(amount)); } @@ -141,102 +158,129 @@ public class Econ { // whether a player can afford specified amount public static boolean canAfford(String playerName, double amount) { // if Economy support is not enabled, they can certainly afford to pay nothing - if (!enabled()) { + if (!enabled()) + { return true; } - if (registerAvailable()) { + if (registerAvailable()) + { MethodAccount holdings = getRegisterAccount(playerName); - if (holdings == null) { + if (holdings == null) + { return false; } return holdings.hasEnough(amount); } - else if (iConomyUse) { + else if (iConomyUse) + { Holdings holdings = getIconomyHoldings(playerName); - if (holdings == null) { + if (holdings == null) + { return false; } return holdings.hasEnough(amount); } - else { - try { + else + { + try + { return Economy.hasEnough(playerName, amount); } - catch (Exception ex) { + catch (Exception ex) + { return false; } } } // deduct money from their account; returns true if successful - public static boolean deductMoney(String playerName, double amount) { - if (!enabled()) { + public static boolean deductMoney(String playerName, double amount) + { + if (!enabled()) + { return true; } - if (registerAvailable()) { + if (registerAvailable()) + { MethodAccount holdings = getRegisterAccount(playerName); - if (holdings == null || !holdings.hasEnough(amount)) { + if (holdings == null || !holdings.hasEnough(amount)) + { return false; } return holdings.subtract(amount); } - else if (iConomyUse) { + else if (iConomyUse) + { Holdings holdings = getIconomyHoldings(playerName); - if (holdings == null || !holdings.hasEnough(amount)) { + if (holdings == null || !holdings.hasEnough(amount)) + { return false; } holdings.subtract(amount); return true; } - else { - try { - if (!Economy.hasEnough(playerName, amount)) { + else + { + try + { + if (!Economy.hasEnough(playerName, amount)) + { return false; } Economy.subtract(playerName, amount); return true; } - catch (Exception ex) { + catch (Exception ex) + { return false; } } } // add money to their account; returns true if successful - public static boolean addMoney(String playerName, double amount) { - if (!enabled()) { + public static boolean addMoney(String playerName, double amount) + { + if (!enabled()) + { return true; } - if (registerAvailable()) { + if (registerAvailable()) + { MethodAccount holdings = getRegisterAccount(playerName); - if (holdings == null) { + if (holdings == null) + { return false; } return holdings.add(amount); } - else if (iConomyUse) { + else if (iConomyUse) + { Holdings holdings = getIconomyHoldings(playerName); - if (holdings == null) { + if (holdings == null) + { return false; } holdings.add(amount); return true; } - else { - try { + else + { + try + { Economy.add(playerName, amount); return true; } - catch (Exception ex) { + catch (Exception ex) + { return false; } } @@ -244,8 +288,10 @@ public class Econ { // calculate the cost for claiming land - public static double calculateClaimCost(int ownedLand, boolean takingFromAnotherFaction) { - if (!enabled()) { + public static double calculateClaimCost(int ownedLand, boolean takingFromAnotherFaction) + { + if (!enabled()) + { return 0.0; } @@ -256,12 +302,14 @@ public class Econ { } // calculate refund amount for unclaiming land - public static double calculateClaimRefund(int ownedLand) { + public static double calculateClaimRefund(int ownedLand) + { return calculateClaimCost(ownedLand - 1, false) * Conf.econClaimRefundMultiplier; } // calculate value of all owned land - public static double calculateTotalLandValue(int ownedLand) { + public static double calculateTotalLandValue(int ownedLand) + { double amount = 0; for (int x = 0; x < ownedLand; x++) { amount += calculateClaimCost(x, false); @@ -270,7 +318,8 @@ public class Econ { } // calculate refund amount for all owned land - public static double calculateTotalLandRefund(int ownedLand) { + public static double calculateTotalLandRefund(int ownedLand) + { return calculateTotalLandValue(ownedLand) * Conf.econClaimRefundMultiplier; } } diff --git a/src/com/massivecraft/factions/integration/EssentialsFeatures.java b/src/com/massivecraft/factions/integration/EssentialsFeatures.java index cbb212cc..a0901a48 100644 --- a/src/com/massivecraft/factions/integration/EssentialsFeatures.java +++ b/src/com/massivecraft/factions/integration/EssentialsFeatures.java @@ -2,39 +2,45 @@ package com.massivecraft.factions.integration; import org.bukkit.entity.Player; import org.bukkit.event.player.PlayerChatEvent; -import org.bukkit.plugin.Plugin; -import com.massivecraft.factions.Factions; +import com.massivecraft.factions.P; import com.earth2me.essentials.chat.EssentialsChat; import com.earth2me.essentials.chat.IEssentialsChatListener; -public class EssentialsFeatures { +public class EssentialsFeatures +{ private static EssentialsChat essChat; - public static void integrateChat(EssentialsChat instance) { + public static void integrateChat(EssentialsChat instance) + { essChat = instance; - try { - essChat.addEssentialsChatListener("Factions", new IEssentialsChatListener() { + try + { + essChat.addEssentialsChatListener("Factions", new IEssentialsChatListener() + { public boolean shouldHandleThisChat(PlayerChatEvent event) { - return Factions.instance.shouldLetFactionsHandleThisChat(event); + return P.p.shouldLetFactionsHandleThisChat(event); } public String modifyMessage(PlayerChatEvent event, Player target, String message) { - return message.replace("{FACTION}", Factions.instance.getPlayerFactionTagRelation(event.getPlayer(), target)).replace("{FACTION_TITLE}", Factions.instance.getPlayerTitle(event.getPlayer())); + return message.replace("{FACTION}", P.p.getPlayerFactionTagRelation(event.getPlayer(), target)).replace("{FACTION_TITLE}", P.p.getPlayerTitle(event.getPlayer())); } }); - Factions.log("Found and will integrate chat with "+essChat.getDescription().getFullName()); + P.p.log("Found and will integrate chat with "+essChat.getDescription().getFullName()); } - catch (NoSuchMethodError ex) { + catch (NoSuchMethodError ex) + { essChat = null; } } - public static void unhookChat() { - if (essChat != null) { + public static void unhookChat() + { + if (essChat != null) + { essChat.removeEssentialsChatListener("Factions"); } } diff --git a/src/com/massivecraft/factions/integration/SpoutFeatures.java b/src/com/massivecraft/factions/integration/SpoutFeatures.java index d1ff0aa6..4fbc90b6 100644 --- a/src/com/massivecraft/factions/integration/SpoutFeatures.java +++ b/src/com/massivecraft/factions/integration/SpoutFeatures.java @@ -4,8 +4,9 @@ import com.massivecraft.factions.Board; import com.massivecraft.factions.Conf; import com.massivecraft.factions.FLocation; import com.massivecraft.factions.FPlayer; +import com.massivecraft.factions.FPlayers; import com.massivecraft.factions.Faction; -import com.massivecraft.factions.Factions; +import com.massivecraft.factions.P; import java.util.HashMap; import java.util.Map; import java.util.Set; @@ -24,25 +25,30 @@ import org.getspout.spoutapi.SpoutManager; import org.getspout.spoutapi.gui.WidgetAnchor; -public class SpoutFeatures { +public class SpoutFeatures +{ private transient static AppearanceManager spoutApp; private transient static boolean spoutMe = false; private transient static Map territoryLabels = new HashMap(); // set integration availability - public static void setAvailable(boolean enable, String pluginName) { + public static void setAvailable(boolean enable, String pluginName) + { spoutMe = enable; - if (spoutMe) { + if (spoutMe) + { spoutApp = SpoutManager.getAppearanceManager(); - Factions.log("Found and will use features of "+pluginName); + P.p.log("Found and will use features of "+pluginName); } - else { + else + { spoutApp = null; } } // If any Spout feature is enabled in conf.json, and we're successfully hooked into it - public static boolean enabled() { + public static boolean enabled() + { return spoutMe && ( Conf.spoutFactionTagsOverNames || Conf.spoutFactionTitlesOverNames @@ -54,24 +60,30 @@ public class SpoutFeatures { // update displayed current territory for specified player; returns false if unsuccessful - public static boolean updateTerritoryDisplay(FPlayer player) { - if (!spoutMe || Conf.spoutTerritoryDisplayPosition == 0) { + public static boolean updateTerritoryDisplay(FPlayer player) + { + if (!spoutMe || Conf.spoutTerritoryDisplayPosition == 0) + { return false; } SpoutPlayer sPlayer = SpoutManager.getPlayer(player.getPlayer()); - if (!sPlayer.isSpoutCraftEnabled()) { + if (!sPlayer.isSpoutCraftEnabled()) + { return false; } GenericLabel label; - if (territoryLabels.containsKey(player.getName())) { + if (territoryLabels.containsKey(player.getName())) + { label = territoryLabels.get(player.getName()); } - else { + else + { label = new GenericLabel(); - sPlayer.getMainScreen().attachWidget(Factions.instance, label); - switch (Conf.spoutTerritoryDisplayPosition) { + sPlayer.getMainScreen().attachWidget(P.p, label); + switch (Conf.spoutTerritoryDisplayPosition) + { case 1: label.setAlign(WidgetAnchor.TOP_LEFT).setAnchor(WidgetAnchor.TOP_LEFT); break; case 2: label.setAlign(WidgetAnchor.TOP_CENTER).setAnchor(WidgetAnchor.TOP_CENTER); break; default: label.setAlign(WidgetAnchor.TOP_RIGHT).setAnchor(WidgetAnchor.TOP_RIGHT); @@ -81,7 +93,8 @@ public class SpoutFeatures { Faction factionHere = Board.getFactionAt(new FLocation(player)); String msg = factionHere.getTag(); - if (factionHere.getDescription().length() > 0) { + if (factionHere.getDescription().length() > 0) + { msg += " - "+factionHere.getDescription(); } label.setTextColor(getSpoutColor(player.getRelationColor(factionHere), 0)); @@ -91,8 +104,10 @@ public class SpoutFeatures { return true; } - public static void playerDisconnect(FPlayer player) { - if (!enabled()) { + public static void playerDisconnect(FPlayer player) + { + if (!enabled()) + { return; } territoryLabels.remove(player.getName()); @@ -100,33 +115,40 @@ public class SpoutFeatures { // update all appearances between every player - public static void updateAppearances() { - if (!enabled()) { + public static void updateAppearances() + { + if (!enabled()) + { return; } - Set players = FPlayer.getAllOnline(); + Set players = FPlayers.i.getOnline(); Faction factionA; - for (FPlayer playerA : players) { + for (FPlayer playerA : players) + { factionA = playerA.getFaction(); - for (FPlayer playerB : players) { + for (FPlayer playerB : players) + { updateSingle(playerB.getPlayer(), playerA.getPlayer(), factionA.getRelation(playerB), factionA, playerA.getTitle(), playerA.getRole()); } } } // update all appearances related to a specific player - public static void updateAppearances(Player player) { - if (!enabled() || player == null) { + public static void updateAppearances(Player player) + { + if (!enabled() || player == null) + { return; } - Set players = FPlayer.getAllOnline(); - FPlayer playerA = FPlayer.get(player); + Set players = FPlayers.i.getOnline(); + FPlayer playerA = FPlayers.i.get(player); Faction factionA = playerA.getFaction(); - for (FPlayer playerB : players) { + for (FPlayer playerB : players) + { Player player2 = playerB.getPlayer(); Relation rel = factionA.getRelation(playerB); updateSingle(player2, player, rel, factionA, playerA.getTitle(), playerA.getRole()); @@ -135,20 +157,25 @@ public class SpoutFeatures { } // update all appearances related to a single faction - public static void updateAppearances(Faction faction) { - if (!enabled() || faction == null) { + public static void updateAppearances(Faction faction) + { + if (!enabled() || faction == null) + { return; } - Set players = FPlayer.getAllOnline(); + Set players = FPlayers.i.getOnline(); Faction factionA, factionB; - for (FPlayer playerA : players) { + for (FPlayer playerA : players) + { factionA = playerA.getFaction(); - for (FPlayer playerB : players) { + for (FPlayer playerB : players) + { factionB = playerB.getFaction(); - if (factionA != faction && factionB != faction) { + if (factionA != faction && factionB != faction) + { continue; } updateSingle(playerB.getPlayer(), playerA.getPlayer(), factionA.getRelation(factionB), factionA, playerA.getTitle(), playerA.getRole()); @@ -157,13 +184,17 @@ public class SpoutFeatures { } // update all appearances between two factions - public static void updateAppearances(Faction factionA, Faction factionB) { - if (!enabled() || factionA == null || factionB == null) { + public static void updateAppearances(Faction factionA, Faction factionB) + { + if (!enabled() || factionA == null || factionB == null) + { return; } - for (FPlayer playerA : factionA.getFPlayersWhereOnline(true)) { - for (FPlayer playerB : factionB.getFPlayersWhereOnline(true)) { + for (FPlayer playerA : factionA.getFPlayersWhereOnline(true)) + { + for (FPlayer playerB : factionB.getFPlayersWhereOnline(true)) + { Player player1 = playerA.getPlayer(); Player player2 = playerB.getPlayer(); Relation rel = factionA.getRelation(factionB); @@ -175,71 +206,102 @@ public class SpoutFeatures { // update a single appearance; internal use only by above public methods - private static void updateSingle(Player viewer, Player viewed, Relation relation, Faction viewedFaction, String viewedTitle, Role viewedRole) { - if (viewer == null || viewed == null) { + private static void updateSingle(Player viewer, Player viewed, Relation relation, Faction viewedFaction, String viewedTitle, Role viewedRole) + { + if (viewer == null || viewed == null) + { return; } SpoutPlayer sPlayer = SpoutManager.getPlayer(viewer); - if (Conf.spoutFactionTagsOverNames || Conf.spoutFactionTitlesOverNames) { - if (viewedFaction.isNormal()) { + if (Conf.spoutFactionTagsOverNames || Conf.spoutFactionTitlesOverNames) + { + if (viewedFaction.isNormal()) + { String addTag = ""; - if (Conf.spoutFactionTagsOverNames) { + if (Conf.spoutFactionTagsOverNames) + { addTag += viewedFaction.getTag(relation.getColor().toString() + "[") + "]"; } String rolePrefix = viewedRole.getPrefix(); - if (Conf.spoutFactionTitlesOverNames && (!viewedTitle.isEmpty() || !rolePrefix.isEmpty())) { + if (Conf.spoutFactionTitlesOverNames && (!viewedTitle.isEmpty() || !rolePrefix.isEmpty())) + { addTag += (addTag.isEmpty() ? "" : " ") + viewedRole.getPrefix() + viewedTitle; } spoutApp.setPlayerTitle(sPlayer, viewed, addTag + "\n" + viewed.getDisplayName()); } - else { + else + { spoutApp.setPlayerTitle(sPlayer, viewed, viewed.getDisplayName()); } } - if ( - (Conf.spoutFactionAdminCapes && viewedRole.equals(Role.ADMIN)) - || (Conf.spoutFactionModeratorCapes && viewedRole.equals(Role.MODERATOR)) - ) { + if + ( + ( + Conf.spoutFactionAdminCapes + && + viewedRole.equals(Role.ADMIN) + ) + || + ( + Conf.spoutFactionModeratorCapes + && + viewedRole.equals(Role.MODERATOR) + ) + ) + { String cape = ""; - if (!viewedFaction.isNormal()) { + if (!viewedFaction.isNormal()) + { // yeah, no cape if no faction } - else if (viewedFaction.isPeaceful()) { + else if (viewedFaction.isPeaceful()) + { cape = Conf.capePeaceful; } - else if (relation.isNeutral()) { + else if (relation.isNeutral()) + { cape = Conf.capeNeutral; } - else if (relation.isMember()) { + else if (relation.isMember()) + { cape = Conf.capeMember; } - else if (relation.isEnemy()) { + else if (relation.isEnemy()) + { cape = Conf.capeEnemy; } - else if (relation.isAlly()) { + else if (relation.isAlly()) + { cape = Conf.capeAlly; } - if (cape.isEmpty()) { + if (cape.isEmpty()) + { spoutApp.resetPlayerCloak(sPlayer, viewed); - } else { + } + else + { spoutApp.setPlayerCloak(sPlayer, viewed, cape); } } - else if (Conf.spoutFactionAdminCapes || Conf.spoutFactionModeratorCapes) { + else if (Conf.spoutFactionAdminCapes || Conf.spoutFactionModeratorCapes) + { spoutApp.resetPlayerCloak(sPlayer, viewed); } } // method to convert a Bukkit ChatColor to a Spout Color - private static Color getSpoutColor(ChatColor inColor, int alpha) { - if (inColor == null) { + private static Color getSpoutColor(ChatColor inColor, int alpha) + { + if (inColor == null) + { return new Color(191, 191, 191, alpha); } - switch (inColor.getCode()) { + switch (inColor.getCode()) + { case 0x1: return new Color(0, 0, 191, alpha); case 0x2: return new Color(0, 191, 0, alpha); case 0x3: return new Color(0, 191, 191, alpha); diff --git a/src/com/massivecraft/factions/integration/Worldguard.java b/src/com/massivecraft/factions/integration/Worldguard.java index 0bb640bf..3bed62dd 100644 --- a/src/com/massivecraft/factions/integration/Worldguard.java +++ b/src/com/massivecraft/factions/integration/Worldguard.java @@ -1,6 +1,6 @@ package com.massivecraft.factions.integration; -import com.massivecraft.factions.Factions; +import com.massivecraft.factions.P; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -28,24 +28,30 @@ import org.bukkit.entity.Player; * Author: Spathizilla */ -public class Worldguard { +public class Worldguard +{ private static WorldGuardPlugin wg; private static boolean enabled = false; - public static void init(Plugin plugin) { + public static void init(Plugin plugin) + { Plugin wgplug = plugin.getServer().getPluginManager().getPlugin("WorldGuard"); - if (wgplug == null || !(wgplug instanceof WorldGuardPlugin)) { + if (wgplug == null || !(wgplug instanceof WorldGuardPlugin)) + { enabled = false; wg = null; - Factions.log("Could not hook to WorldGuard. WorldGuard checks are disabled."); - } else { + P.p.log("Could not hook to WorldGuard. WorldGuard checks are disabled."); + } + else + { wg = (WorldGuardPlugin) wgplug; enabled = true; - Factions.log("Successfully hooked to WorldGuard."); + P.p.log("Successfully hooked to WorldGuard."); } } - public static boolean isEnabled() { + public static boolean isEnabled() + { return enabled; } @@ -53,8 +59,10 @@ public class Worldguard { // Returns: // True: PVP is allowed // False: PVP is disallowed - public static boolean isPVP(Player player) { - if(!enabled) { + public static boolean isPVP(Player player) + { + if( ! enabled) + { // No WG hooks so we'll always bypass this check. return true; } @@ -72,8 +80,10 @@ public class Worldguard { // Returns: // True: Regions found within chunk // False: No regions found within chunk - public static boolean checkForRegionsInChunk(Location loc) { - if(!enabled) { + public static boolean checkForRegionsInChunk(Location loc) + { + if( ! enabled) + { // No WG hooks so we'll always bypass this check. return false; } @@ -97,14 +107,20 @@ public class Worldguard { List overlaps; boolean foundregions = false; - try { + try + { overlaps = region.getIntersectingRegions(allregionslist); - if(overlaps == null || overlaps.isEmpty()) { + if(overlaps == null || overlaps.isEmpty()) + { foundregions = false; - } else { + } + else + { foundregions = true; } - } catch (UnsupportedIntersectionException e) { + } + catch (UnsupportedIntersectionException e) + { e.printStackTrace(); } diff --git a/src/com/massivecraft/factions/listeners/FactionsBlockListener.java b/src/com/massivecraft/factions/listeners/FactionsBlockListener.java index 9197b31e..9db1b330 100644 --- a/src/com/massivecraft/factions/listeners/FactionsBlockListener.java +++ b/src/com/massivecraft/factions/listeners/FactionsBlockListener.java @@ -15,58 +15,65 @@ import com.massivecraft.factions.Board; import com.massivecraft.factions.Conf; import com.massivecraft.factions.FLocation; import com.massivecraft.factions.FPlayer; +import com.massivecraft.factions.FPlayers; import com.massivecraft.factions.Faction; -import com.massivecraft.factions.Factions; +import com.massivecraft.factions.P; import com.massivecraft.factions.struct.Relation; -public class FactionsBlockListener extends BlockListener { +public class FactionsBlockListener extends BlockListener +{ + public P p; + public FactionsBlockListener(P p) + { + this.p = p; + } @Override - public void onBlockPlace(BlockPlaceEvent event) { - if (event.isCancelled()) { - return; - } - if (!event.canBuild()) { - return; - } + public void onBlockPlace(BlockPlaceEvent event) + { + if (event.isCancelled()) return; + if ( ! event.canBuild()) return; + // special case for flint&steel, which should only be prevented by DenyUsage list - if (event.getBlockPlaced().getType() == Material.FIRE) { + if (event.getBlockPlaced().getType() == Material.FIRE) + { return; } - if ( ! this.playerCanBuildDestroyBlock(event.getPlayer(), event.getBlock().getLocation(), "build", false)) { + if ( ! playerCanBuildDestroyBlock(event.getPlayer(), event.getBlock().getLocation(), "build", false)) + { event.setCancelled(true); } } @Override - public void onBlockBreak(BlockBreakEvent event) { - if (event.isCancelled()) { - return; - } + public void onBlockBreak(BlockBreakEvent event) + { + if (event.isCancelled()) return; - if ( ! this.playerCanBuildDestroyBlock(event.getPlayer(), event.getBlock().getLocation(), "destroy", false)) { + if ( ! playerCanBuildDestroyBlock(event.getPlayer(), event.getBlock().getLocation(), "destroy", false)) + { event.setCancelled(true); } } @Override - public void onBlockDamage(BlockDamageEvent event) { - if (event.isCancelled()) { - return; - } + public void onBlockDamage(BlockDamageEvent event) + { + if (event.isCancelled()) return; - if (event.getInstaBreak() && ! this.playerCanBuildDestroyBlock(event.getPlayer(), event.getBlock().getLocation(), "destroy", false)) { + if (event.getInstaBreak() && ! playerCanBuildDestroyBlock(event.getPlayer(), event.getBlock().getLocation(), "destroy", false)) + { event.setCancelled(true); } } @Override - public void onBlockPistonExtend(BlockPistonExtendEvent event) { - if (event.isCancelled() || !Conf.pistonProtectionThroughDenyBuild) { - return; - } + public void onBlockPistonExtend(BlockPistonExtendEvent event) + { + if (event.isCancelled()) return; + if ( ! Conf.pistonProtectionThroughDenyBuild) return; Faction pistonFaction = Board.getFactionAt(new FLocation(event.getBlock())); @@ -74,7 +81,8 @@ public class FactionsBlockListener extends BlockListener { Block targetBlock = event.getBlock().getRelative(event.getDirection(), event.getLength() + 1); // if potentially pushing into air in another territory, we need to check it out - if (targetBlock.isEmpty() && !canPistonMoveBlock(pistonFaction, targetBlock.getLocation())) { + if (targetBlock.isEmpty() && !canPistonMoveBlock(pistonFaction, targetBlock.getLocation())) + { event.setCancelled(true); return; } @@ -87,49 +95,61 @@ public class FactionsBlockListener extends BlockListener { } @Override - public void onBlockPistonRetract(BlockPistonRetractEvent event) { + public void onBlockPistonRetract(BlockPistonRetractEvent event) + { // if not a sticky piston, retraction should be fine - if (event.isCancelled() || !event.isSticky() || !Conf.pistonProtectionThroughDenyBuild) { + if (event.isCancelled() || !event.isSticky() || !Conf.pistonProtectionThroughDenyBuild) + { return; } Location targetLoc = event.getRetractLocation(); // if potentially retracted block is just air, no worries - if (targetLoc.getBlock().isEmpty()) { + if (targetLoc.getBlock().isEmpty()) + { return; } Faction pistonFaction = Board.getFactionAt(new FLocation(event.getBlock())); - if (!canPistonMoveBlock(pistonFaction, targetLoc)) { + if (!canPistonMoveBlock(pistonFaction, targetLoc)) + { event.setCancelled(true); return; } } - private boolean canPistonMoveBlock(Faction pistonFaction, Location target) { + private boolean canPistonMoveBlock(Faction pistonFaction, Location target) + { Faction otherFaction = Board.getFactionAt(new FLocation(target)); - if (pistonFaction == otherFaction) { + if (pistonFaction == otherFaction) + { return true; } - if (otherFaction.isNone()) { - if (!Conf.wildernessDenyBuild || Conf.worldsNoWildernessProtection.contains(target.getWorld().getName())) { + if (otherFaction.isNone()) + { + if (!Conf.wildernessDenyBuild || Conf.worldsNoWildernessProtection.contains(target.getWorld().getName())) + { return true; } return false; } - else if (otherFaction.isSafeZone()) { - if (!Conf.safeZoneDenyBuild) { + else if (otherFaction.isSafeZone()) + { + if ( ! Conf.safeZoneDenyBuild) + { return true; } return false; } - else if (otherFaction.isWarZone()) { - if (!Conf.warZoneDenyBuild) { + else if (otherFaction.isWarZone()) + { + if ( ! Conf.warZoneDenyBuild) + { return true; } return false; @@ -138,37 +158,47 @@ public class FactionsBlockListener extends BlockListener { Relation rel = pistonFaction.getRelation(otherFaction); boolean online = otherFaction.hasPlayersOnline(); - if ( - (online && (rel.isEnemy() ? Conf.territoryEnemyDenyBuild : (rel.isAlly() ? Conf.territoryAllyDenyBuild : Conf.territoryDenyBuild))) - || (!online && (rel.isEnemy() ? Conf.territoryEnemyDenyBuildWhenOffline : (rel.isAlly() ? Conf.territoryAllyDenyBuildWhenOffline : Conf.territoryDenyBuildWhenOffline))) - ) { + if + ( + (online && (rel.isEnemy() ? Conf.territoryEnemyDenyBuild : (rel.isAlly() ? Conf.territoryAllyDenyBuild : Conf.territoryDenyBuild))) + || + (!online && (rel.isEnemy() ? Conf.territoryEnemyDenyBuildWhenOffline : (rel.isAlly() ? Conf.territoryAllyDenyBuildWhenOffline : Conf.territoryDenyBuildWhenOffline))) + ) + { return false; } return true; } - public static boolean playerCanBuildDestroyBlock(Player player, Location location, String action, boolean justCheck) { + public static boolean playerCanBuildDestroyBlock(Player player, Location location, String action, boolean justCheck) + { - if (Conf.adminBypassPlayers.contains(player.getName())) { + if (Conf.adminBypassPlayers.contains(player.getName())) + { return true; } FLocation loc = new FLocation(location); Faction otherFaction = Board.getFactionAt(loc); - FPlayer me = FPlayer.get(player); + FPlayer me = FPlayers.i.get(player); - if (otherFaction.isNone()) { - if (!Conf.wildernessDenyBuild || Factions.hasPermAdminBypass(player) || Conf.worldsNoWildernessProtection.contains(location.getWorld().getName())) { + if (otherFaction.isNone()) + { + if (!Conf.wildernessDenyBuild || P.hasPermAdminBypass(player) || Conf.worldsNoWildernessProtection.contains(location.getWorld().getName())) + { return true; // This is not faction territory. Use whatever you like here. } - if (!justCheck) { + if (!justCheck) + { me.sendMessage("You can't "+action+" in the wilderness."); } return false; } - else if (otherFaction.isSafeZone()) { - if (!Conf.safeZoneDenyBuild || Factions.hasPermManageSafeZone(player)) { + else if (otherFaction.isSafeZone()) + { + if (!Conf.safeZoneDenyBuild || P.hasPermManageSafeZone(player)) + { return true; } if (!justCheck) { @@ -176,11 +206,14 @@ public class FactionsBlockListener extends BlockListener { } return false; } - else if (otherFaction.isWarZone()) { - if (!Conf.warZoneDenyBuild || Factions.hasPermManageWarZone(player)) { + else if (otherFaction.isWarZone()) + { + if (!Conf.warZoneDenyBuild || P.hasPermManageWarZone(player)) + { return true; } - if (!justCheck) { + if (!justCheck) + { me.sendMessage("You can't "+action+" in a war zone."); } return false; @@ -191,38 +224,49 @@ public class FactionsBlockListener extends BlockListener { boolean ownershipFail = Conf.ownedAreasEnabled && (Conf.ownedAreaDenyBuild || Conf.ownedAreaPainBuild) && !otherFaction.playerHasOwnershipRights(me, loc); // Cancel and/or cause pain (depending on configuration) if we are not in our own territory - if (!rel.isMember()) { + if (!rel.isMember()) + { boolean online = otherFaction.hasPlayersOnline(); boolean pain = (!justCheck) && rel.confPainBuild(online); boolean deny = rel.confDenyBuild(online); //hurt the player for building/destroying? - if (pain) { + if (pain) + { player.damage(Conf.actionDeniedPainAmount); - if (!deny) { + if (!deny) + { me.sendMessage("You are hurt for "+action+" in the territory of "+otherFaction.getTag(myFaction)); - if (!Conf.ownedAreaDenyBuild) { + if (!Conf.ownedAreaDenyBuild) + { return true; } } } - if (deny) { - if (!justCheck) { + if (deny) + { + if (!justCheck) + { me.sendMessage("You can't "+action+" in the territory of "+otherFaction.getTag(myFaction)); } return false; } } // Also cancel and/or cause pain if player doesn't have ownership rights for this claim - else if (rel.isMember() && ownershipFail && !Factions.hasPermOwnershipBypass(player)) { - if (Conf.ownedAreaPainBuild && !justCheck){ + else if (rel.isMember() && ownershipFail && !P.hasPermOwnershipBypass(player)) + { + if (Conf.ownedAreaPainBuild && !justCheck) + { player.damage(Conf.actionDeniedPainAmount); - if (!Conf.ownedAreaDenyBuild) { + if (!Conf.ownedAreaDenyBuild) + { me.sendMessage("You are hurt for "+action+" in this territory, it is owned by: "+myFaction.getOwnerListString(loc)); } } - if (Conf.ownedAreaDenyBuild){ - if (!justCheck) { + if (Conf.ownedAreaDenyBuild) + { + if (!justCheck) + { me.sendMessage("You can't "+action+" in this territory, it is owned by: "+myFaction.getOwnerListString(loc)); } return false; diff --git a/src/com/massivecraft/factions/listeners/FactionsChatEarlyListener.java b/src/com/massivecraft/factions/listeners/FactionsChatEarlyListener.java index cc15b8a9..792b9062 100644 --- a/src/com/massivecraft/factions/listeners/FactionsChatEarlyListener.java +++ b/src/com/massivecraft/factions/listeners/FactionsChatEarlyListener.java @@ -1,62 +1,70 @@ package com.massivecraft.factions.listeners; -import java.util.List; -import java.util.logging.Logger; +import java.util.logging.Level; import org.bukkit.ChatColor; -import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.event.player.PlayerChatEvent; import org.bukkit.event.player.PlayerListener; import com.massivecraft.factions.Conf; import com.massivecraft.factions.FPlayer; +import com.massivecraft.factions.FPlayers; import com.massivecraft.factions.Faction; -import com.massivecraft.factions.Factions; +import com.massivecraft.factions.P; import com.massivecraft.factions.struct.ChatMode; import com.massivecraft.factions.struct.Relation; -import com.massivecraft.factions.util.TextUtil; // this is an addtional PlayerListener for handling slashless command usage and faction chat, to be set at low priority so Factions gets to them first -public class FactionsChatEarlyListener extends PlayerListener{ - +public class FactionsChatEarlyListener extends PlayerListener +{ + public P p; + public FactionsChatEarlyListener(P p) + { + this.p = p; + } + @Override - public void onPlayerChat(PlayerChatEvent event) { + public void onPlayerChat(PlayerChatEvent event) + { // Is it a slashless Factions command? - if ((event.getMessage().startsWith(Factions.instance.getBaseCommand()+" ") || event.getMessage().equals(Factions.instance.getBaseCommand())) && Conf.allowNoSlashCommand) { + /*if ((event.getMessage().startsWith(P.p.getBaseCommand()+" ") || event.getMessage().equals(P.p.getBaseCommand())) && Conf.allowNoSlashCommand) { String msg = event.getMessage().trim(); // make sure command isn't denied due to being in enemy/neutral territory if (!FactionsPlayerListener.preventCommand("/" + msg.toLowerCase(), event.getPlayer())) { List parameters = TextUtil.split(msg); parameters.remove(0); CommandSender sender = event.getPlayer(); - Factions.instance.handleCommand(sender, parameters); + P.p.handleCommand(sender, parameters); } event.setCancelled(true); return; - } + }*/ - if (event.isCancelled()) { - return; - } + if (event.isCancelled()) return; Player talkingPlayer = event.getPlayer(); String msg = event.getMessage(); // ... it was not a command. This means that it is a chat message! - FPlayer me = FPlayer.get(talkingPlayer); + FPlayer me = FPlayers.i.get(talkingPlayer); // Is it a faction chat message? - if (me.getChatMode() == ChatMode.FACTION) { + if (me.getChatMode() == ChatMode.FACTION) + { String message = String.format(Conf.factionChatFormat, me.getNameAndRelevant(me), msg); me.getFaction().sendMessage(message); - Logger.getLogger("Minecraft").info(ChatColor.stripColor("FactionChat "+me.getFaction().getTag()+": "+message)); + + P.p.log(Level.INFO, ChatColor.stripColor("FactionChat "+me.getFaction().getTag()+": "+message)); + event.setCancelled(true); return; - } else if (me.getChatMode() == ChatMode.ALLIANCE ) { + } + else if (me.getChatMode() == ChatMode.ALLIANCE ) + { Faction myFaction = me.getFaction(); String factionAndName = ChatColor.stripColor(me.getNameAndTag()); @@ -64,13 +72,17 @@ public class FactionsChatEarlyListener extends PlayerListener{ //Send message to our own faction myFaction.sendMessage(message); - for (FPlayer fplayer : FPlayer.getAllOnline()) { - if(myFaction.getRelation(fplayer) == Relation.ALLY) { + for (FPlayer fplayer : FPlayers.i.getOnline()) + { + if(myFaction.getRelation(fplayer) == Relation.ALLY) + { //Send to all our allies fplayer.sendMessage(message); } } - Logger.getLogger("Minecraft").info(ChatColor.stripColor("AllianceChat "+me.getFaction().getTag()+": "+message)); + + P.p.log(Level.INFO, ChatColor.stripColor("AllianceChat "+me.getFaction().getTag()+": "+message)); + event.setCancelled(true); return; } diff --git a/src/com/massivecraft/factions/listeners/FactionsEntityListener.java b/src/com/massivecraft/factions/listeners/FactionsEntityListener.java index 83a6a9f0..5dcf8619 100644 --- a/src/com/massivecraft/factions/listeners/FactionsEntityListener.java +++ b/src/com/massivecraft/factions/listeners/FactionsEntityListener.java @@ -26,39 +26,58 @@ import com.massivecraft.factions.Board; import com.massivecraft.factions.Conf; import com.massivecraft.factions.FLocation; import com.massivecraft.factions.FPlayer; +import com.massivecraft.factions.FPlayers; import com.massivecraft.factions.Faction; -import com.massivecraft.factions.Factions; +import com.massivecraft.factions.P; import com.massivecraft.factions.struct.Relation; -import com.massivecraft.factions.util.EntityUtil; +import com.massivecraft.factions.util.MiscUtil; -public class FactionsEntityListener extends EntityListener { +public class FactionsEntityListener extends EntityListener +{ + public P p; + public FactionsEntityListener(P p) + { + this.p = p; + } @Override - public void onEntityDeath(EntityDeathEvent event) { + public void onEntityDeath(EntityDeathEvent event) + { Entity entity = event.getEntity(); - if ( ! (entity instanceof Player)) { + if ( ! (entity instanceof Player)) + { return; } Player player = (Player) entity; - FPlayer fplayer = FPlayer.get(player); + FPlayer fplayer = FPlayers.i.get(player); Faction faction = Board.getFactionAt(new FLocation(player.getLocation())); - if (faction.isWarZone()) { // war zones always override worldsNoPowerLoss either way, thus this layout - if (! Conf.warZonePowerLoss) { + if (faction.isWarZone()) + { + // war zones always override worldsNoPowerLoss either way, thus this layout + if (! Conf.warZonePowerLoss) + { fplayer.sendMessage("You didn't lose any power since you were in a war zone."); return; } - if (Conf.worldsNoPowerLoss.contains(player.getWorld().getName())) { + if (Conf.worldsNoPowerLoss.contains(player.getWorld().getName())) + { fplayer.sendMessage("The world you are in has power loss normally disabled, but you still lost power since you were in a war zone."); } - } else if (faction.isNone() && !Conf.wildernessPowerLoss && !Conf.worldsNoWildernessProtection.contains(player.getWorld().getName())) { + } + else if (faction.isNone() && !Conf.wildernessPowerLoss && !Conf.worldsNoWildernessProtection.contains(player.getWorld().getName())) + { fplayer.sendMessage("You didn't lose any power since you were in the wilderness."); return; - } else if (Conf.worldsNoPowerLoss.contains(player.getWorld().getName())) { + } + else if (Conf.worldsNoPowerLoss.contains(player.getWorld().getName())) + { fplayer.sendMessage("You didn't lose any power due to the world you died in."); return; - } else if (Conf.peacefulMembersDisablePowerLoss && fplayer.hasFaction() && fplayer.getFaction().isPeaceful()) { + } + else if (Conf.peacefulMembersDisablePowerLoss && fplayer.hasFaction() && fplayer.getFaction().isPeaceful()) + { fplayer.sendMessage("You didn't lose any power since you are in a peaceful faction."); return; } @@ -92,105 +111,161 @@ public class FactionsEntityListener extends EntityListener { @Override public void onEntityExplode(EntityExplodeEvent event) { - if ( event.isCancelled()) { - return; - } + if ( event.isCancelled()) return; Location loc = event.getLocation(); Faction faction = Board.getFactionAt(new FLocation(loc)); boolean online = faction.hasPlayersOnline(); - if (faction.noExplosionsInTerritory()) { + if (faction.noExplosionsInTerritory()) + { // faction is peaceful and has explosions set to disabled event.setCancelled(true); } - else if (event.getEntity() instanceof Creeper && ( - (faction.isNone() && Conf.wildernessBlockCreepers && !Conf.worldsNoWildernessProtection.contains(loc.getWorld().getName())) || - (faction.isNormal() && (online ? Conf.territoryBlockCreepers : Conf.territoryBlockCreepersWhenOffline)) || - (faction.isWarZone() && Conf.warZoneBlockCreepers) || + else if + ( + event.getEntity() instanceof Creeper + && + ( + (faction.isNone() && Conf.wildernessBlockCreepers && !Conf.worldsNoWildernessProtection.contains(loc.getWorld().getName())) + || + (faction.isNormal() && (online ? Conf.territoryBlockCreepers : Conf.territoryBlockCreepersWhenOffline)) + || + (faction.isWarZone() && Conf.warZoneBlockCreepers) + || faction.isSafeZone() - )) { + ) + ) + { // creeper which needs prevention event.setCancelled(true); - } else if (event.getEntity() instanceof Fireball && ( - (faction.isNone() && Conf.wildernessBlockFireballs && !Conf.worldsNoWildernessProtection.contains(loc.getWorld().getName())) || - (faction.isNormal() && (online ? Conf.territoryBlockFireballs : Conf.territoryBlockFireballsWhenOffline)) || - (faction.isWarZone() && Conf.warZoneBlockFireballs) || + } + else if + ( + event.getEntity() instanceof Fireball + && + ( + (faction.isNone() && Conf.wildernessBlockFireballs && !Conf.worldsNoWildernessProtection.contains(loc.getWorld().getName())) + || + (faction.isNormal() && (online ? Conf.territoryBlockFireballs : Conf.territoryBlockFireballsWhenOffline)) + || + (faction.isWarZone() && Conf.warZoneBlockFireballs) + || faction.isSafeZone() - )) { + ) + ) + { // ghast fireball which needs prevention event.setCancelled(true); - } else if ( - (faction.isNone() && Conf.wildernessBlockTNT && !Conf.worldsNoWildernessProtection.contains(loc.getWorld().getName())) || - (faction.isNormal() && (online ? Conf.territoryBlockTNT : Conf.territoryBlockTNTWhenOffline)) || - (faction.isWarZone() && Conf.warZoneBlockTNT) || - (faction.isSafeZone() && Conf.safeZoneBlockTNT) - ) { + } + else if + ( + ( + faction.isNone() + && + Conf.wildernessBlockTNT + && + ! Conf.worldsNoWildernessProtection.contains(loc.getWorld().getName()) + ) + || + ( + faction.isNormal() + && + ( + online ? Conf.territoryBlockTNT : Conf.territoryBlockTNTWhenOffline + ) + ) + || + ( + faction.isWarZone() + && + Conf.warZoneBlockTNT + ) + || + ( + faction.isSafeZone() + && + Conf.safeZoneBlockTNT + ) + ) + { // we'll assume it's TNT, which needs prevention event.setCancelled(true); } } - public boolean isPlayerInSafeZone(Entity damagee) { - if ( ! (damagee instanceof Player)) { + public boolean isPlayerInSafeZone(Entity damagee) + { + if ( ! (damagee instanceof Player)) + { return false; } - if (Board.getFactionAt(new FLocation(damagee.getLocation())).isSafeZone()) { + if (Board.getFactionAt(new FLocation(damagee.getLocation())).isSafeZone()) + { return true; } return false; } - public boolean canDamagerHurtDamagee(EntityDamageByEntityEvent sub) { + public boolean canDamagerHurtDamagee(EntityDamageByEntityEvent sub) + { Entity damager = sub.getDamager(); Entity damagee = sub.getEntity(); int damage = sub.getDamage(); - if ( ! (damagee instanceof Player)) { + if ( ! (damagee instanceof Player)) + { return true; } - FPlayer defender = FPlayer.get((Player)damagee); + FPlayer defender = FPlayers.i.get((Player)damagee); - if (defender == null || defender.getPlayer() == null) { + if (defender == null || defender.getPlayer() == null) + { return true; } Location defenderLoc = defender.getPlayer().getLocation(); - if (Conf.worldsIgnorePvP.contains(defenderLoc.getWorld().getName())) { + if (Conf.worldsIgnorePvP.contains(defenderLoc.getWorld().getName())) + { return true; } Faction defLocFaction = Board.getFactionAt(new FLocation(defenderLoc)); // for damage caused by projectiles, getDamager() returns the projectile... what we need to know is the source - if (damager instanceof Projectile) { + if (damager instanceof Projectile) + { damager = ((Projectile)damager).getShooter(); } // Players can not take attack damage in a SafeZone, or possibly peaceful territory if (defLocFaction.noPvPInTerritory()) { - if (damager instanceof Player) { - FPlayer attacker = FPlayer.get((Player)damager); + if (damager instanceof Player) + { + FPlayer attacker = FPlayers.i.get((Player)damager); attacker.sendMessage("You can't hurt other players in "+(defLocFaction.isSafeZone() ? "a SafeZone." : "peaceful territory.")); return false; } return !defLocFaction.noMonstersInTerritory(); } - if ( ! (damager instanceof Player)) { + if ( ! (damager instanceof Player)) + { return true; } - FPlayer attacker = FPlayer.get((Player)damager); + FPlayer attacker = FPlayers.i.get((Player)damager); - if (attacker == null || attacker.getPlayer() == null) { + if (attacker == null || attacker.getPlayer() == null) + { return true; } - if (attacker.hasLoginPvpDisabled()) { + if (attacker.hasLoginPvpDisabled()) + { attacker.sendMessage("You can't hurt other players for " + Conf.noPVPDamageToOthersForXSecondsAfterLogin + " seconds after logging in."); return false; } @@ -198,37 +273,45 @@ public class FactionsEntityListener extends EntityListener { Faction locFaction = Board.getFactionAt(new FLocation(attacker)); // so we know from above that the defender isn't in a safezone... what about the attacker, sneaky dog that he might be? - if (locFaction.noPvPInTerritory()) { + if (locFaction.noPvPInTerritory()) + { attacker.sendMessage("You can't hurt other players while you are in "+(locFaction.isSafeZone() ? "a SafeZone." : "peaceful territory.")); return false; } - else if (locFaction.isWarZone() && Conf.warZoneFriendlyFire) { + else if (locFaction.isWarZone() && Conf.warZoneFriendlyFire) + { return true; } Faction defendFaction = defender.getFaction(); Faction attackFaction = attacker.getFaction(); - if (attackFaction.isNone() && Conf.disablePVPForFactionlessPlayers) { + if (attackFaction.isNone() && Conf.disablePVPForFactionlessPlayers) + { attacker.sendMessage("You can't hurt other players until you join a faction."); return false; } - else if (defendFaction.isNone()) { - if (defLocFaction == attackFaction && Conf.enablePVPAgainstFactionlessInAttackersLand) { + else if (defendFaction.isNone()) + { + if (defLocFaction == attackFaction && Conf.enablePVPAgainstFactionlessInAttackersLand) + { // Allow PVP vs. Factionless in attacker's faction territory return true; } - else if (Conf.disablePVPForFactionlessPlayers) { + else if (Conf.disablePVPForFactionlessPlayers) + { attacker.sendMessage("You can't hurt players who are not currently in a faction."); return false; } } - if (defendFaction.isPeaceful()) { + if (defendFaction.isPeaceful()) + { attacker.sendMessage("You can't hurt players who are in a peaceful faction."); return false; } - else if (attackFaction.isPeaceful()) { + else if (attackFaction.isPeaceful()) + { attacker.sendMessage("You can't hurt players while you are in a peaceful faction."); return false; } @@ -236,74 +319,84 @@ public class FactionsEntityListener extends EntityListener { Relation relation = defendFaction.getRelation(attackFaction); // You can not hurt neutral factions - if (Conf.disablePVPBetweenNeutralFactions && relation.isNeutral()) { + if (Conf.disablePVPBetweenNeutralFactions && relation.isNeutral()) + { attacker.sendMessage("You can't hurt neutral factions"); return false; } // Players without faction may be hurt anywhere - if (!defender.hasFaction()) { + if (!defender.hasFaction()) + { return true; } // You can never hurt faction members or allies - if (relation.isMember() || relation.isAlly()) { - attacker.sendMessage(Conf.colorSystem+"You can't hurt "+defender.getNameAndRelevant(attacker)); + if (relation.isMember() || relation.isAlly()) + { + attacker.sendMessage(p.txt.parse("You can't hurt "+defender.getNameAndRelevant(attacker))); return false; } boolean ownTerritory = defender.isInOwnTerritory(); // You can not hurt neutrals in their own territory. - if (ownTerritory && relation.isNeutral()) { - attacker.sendMessage(Conf.colorSystem+"You can't hurt "+relation.getColor()+defender.getNameAndRelevant(attacker)+Conf.colorSystem+" in their own territory."); - defender.sendMessage(attacker.getNameAndRelevant(defender)+Conf.colorSystem+" tried to hurt you."); + if (ownTerritory && relation.isNeutral()) + { + attacker.sendMessage(p.txt.parse("You can't hurt "+relation.getColor()+defender.getNameAndRelevant(attacker)+" in their own territory.")); + defender.sendMessage(p.txt.parse(attacker.getNameAndRelevant(defender)+" tried to hurt you.")); return false; } // Damage will be dealt. However check if the damage should be reduced. - if (ownTerritory && Conf.territoryShieldFactor > 0) { + if (ownTerritory && Conf.territoryShieldFactor > 0) + { int newDamage = (int)Math.ceil(damage * (1D - Conf.territoryShieldFactor)); sub.setDamage(newDamage); // Send message String perc = MessageFormat.format("{0,number,#%}", (Conf.territoryShieldFactor)); // TODO does this display correctly?? - defender.sendMessage("Enemy damage reduced by "+ChatColor.RED+perc+Conf.colorSystem+"."); + defender.sendMessage(p.txt.parse("Enemy damage reduced by "+ChatColor.RED+perc+".")); } return true; } @Override - public void onCreatureSpawn(CreatureSpawnEvent event) { - if (event.isCancelled() || event.getLocation() == null) { + public void onCreatureSpawn(CreatureSpawnEvent event) + { + if (event.isCancelled() || event.getLocation() == null) + { return; } - if (Conf.safeZoneNerfedCreatureTypes.contains(event.getCreatureType()) && Board.getFactionAt(new FLocation(event.getLocation())).noMonstersInTerritory()) { + if (Conf.safeZoneNerfedCreatureTypes.contains(event.getCreatureType()) && Board.getFactionAt(new FLocation(event.getLocation())).noMonstersInTerritory()) + { event.setCancelled(true); } } @Override - public void onEntityTarget(EntityTargetEvent event) { - if (event.isCancelled()) { - return; - } + public void onEntityTarget(EntityTargetEvent event) + { + if (event.isCancelled()) return; // if there is a target Entity target = event.getTarget(); - if (target == null) { + if (target == null) + { return; } // We are interested in blocking targeting for certain mobs: - if ( ! Conf.safeZoneNerfedCreatureTypes.contains(EntityUtil.creatureTypeFromEntity(event.getEntity()))) { + if ( ! Conf.safeZoneNerfedCreatureTypes.contains(MiscUtil.creatureTypeFromEntity(event.getEntity()))) + { return; } // in case the target is in a safe zone. - if (Board.getFactionAt(new FLocation(target.getLocation())).noMonstersInTerritory()) { + if (Board.getFactionAt(new FLocation(target.getLocation())).noMonstersInTerritory()) + { event.setCancelled(true); } } @@ -311,21 +404,23 @@ public class FactionsEntityListener extends EntityListener { @Override public void onPaintingBreak(PaintingBreakEvent event) { - if (event.isCancelled()) { - return; - } - if (! (event instanceof PaintingBreakByEntityEvent)) { + if (event.isCancelled()) return; + + if (! (event instanceof PaintingBreakByEntityEvent)) + { return; } Entity breaker = ((PaintingBreakByEntityEvent)event).getRemover(); - if (! (breaker instanceof Player)) { + if (! (breaker instanceof Player)) + { return; } FLocation loc = new FLocation(event.getPainting().getLocation()); - if ( ! this.playerCanDoPaintings((Player)breaker, loc, "remove")) { + if ( ! this.playerCanDoPaintings((Player)breaker, loc, "remove")) + { event.setCancelled(true); } } @@ -333,41 +428,48 @@ public class FactionsEntityListener extends EntityListener { @Override public void onPaintingPlace(PaintingPlaceEvent event) { - if (event.isCancelled()) { - return; - } + if (event.isCancelled()) return; - if ( ! this.playerCanDoPaintings(event.getPlayer(), new FLocation(event.getBlock()), "place")) { + if ( ! this.playerCanDoPaintings(event.getPlayer(), new FLocation(event.getBlock()), "place")) + { event.setCancelled(true); } } - public boolean playerCanDoPaintings(Player player, FLocation loc, String action) { + public boolean playerCanDoPaintings(Player player, FLocation loc, String action) + { - if (Conf.adminBypassPlayers.contains(player.getName())) { + if (Conf.adminBypassPlayers.contains(player.getName())) + { return true; } Faction otherFaction = Board.getFactionAt(loc); - FPlayer me = FPlayer.get(player); + FPlayer me = FPlayers.i.get(player); - if (otherFaction.isNone()) { - if (!Conf.wildernessDenyBuild || Factions.hasPermAdminBypass(player) || Conf.worldsNoWildernessProtection.contains(player.getWorld().getName())) { + if (otherFaction.isNone()) + { + if (!Conf.wildernessDenyBuild || P.hasPermAdminBypass(player) || Conf.worldsNoWildernessProtection.contains(player.getWorld().getName())) + { return true; // This is not faction territory. Use whatever you like here. } me.sendMessage("You can't "+action+" paintings in the wilderness."); return false; } - if (otherFaction.isSafeZone()) { - if (Factions.hasPermManageSafeZone(player) || !Conf.safeZoneDenyBuild) { + if (otherFaction.isSafeZone()) + { + if (P.hasPermManageSafeZone(player) || !Conf.safeZoneDenyBuild) + { return true; } me.sendMessage("You can't "+action+" paintings in a safe zone."); return false; } - else if (otherFaction.isWarZone()) { - if (Factions.hasPermManageWarZone(player) || !Conf.warZoneDenyBuild) { + else if (otherFaction.isWarZone()) + { + if (P.hasPermManageWarZone(player) || !Conf.warZoneDenyBuild) + { return true; } me.sendMessage("You can't "+action+" paintings in a war zone."); @@ -379,12 +481,14 @@ public class FactionsEntityListener extends EntityListener { boolean ownershipFail = Conf.ownedAreasEnabled && Conf.ownedAreaDenyBuild && !otherFaction.playerHasOwnershipRights(me, loc); // Cancel if we are not in our own territory and building should be denied - if (!rel.isMember() && rel.confDenyBuild(otherFaction.hasPlayersOnline())) { + if (!rel.isMember() && rel.confDenyBuild(otherFaction.hasPlayersOnline())) + { me.sendMessage("You can't "+action+" paintings in the territory of "+otherFaction.getTag(myFaction)); return false; } // Also cancel if player doesn't have ownership rights for this claim - else if (rel.isMember() && ownershipFail && !Factions.hasPermOwnershipBypass(player)) { + else if (rel.isMember() && ownershipFail && !P.hasPermOwnershipBypass(player)) + { me.sendMessage("You can't "+action+" paintings in this territory, it is owned by: "+otherFaction.getOwnerListString(loc)); return false; } @@ -393,54 +497,67 @@ public class FactionsEntityListener extends EntityListener { } @Override - public void onEndermanPickup(EndermanPickupEvent event) { - if (event.isCancelled()) { - return; - } + public void onEndermanPickup(EndermanPickupEvent event) + { + if (event.isCancelled()) return; - if (stopEndermanBlockManipulation(event.getBlock().getLocation())) { + if (stopEndermanBlockManipulation(event.getBlock().getLocation())) + { event.setCancelled(true); } } @Override - public void onEndermanPlace(EndermanPlaceEvent event) { - if (event.isCancelled()) { - return; - } + public void onEndermanPlace(EndermanPlaceEvent event) + { + if (event.isCancelled()) return; - if (stopEndermanBlockManipulation(event.getLocation())) { + if (stopEndermanBlockManipulation(event.getLocation())) + { event.setCancelled(true); } } - private boolean stopEndermanBlockManipulation(Location loc) { - if (loc == null) { + private boolean stopEndermanBlockManipulation(Location loc) + { + if (loc == null) + { return false; } // quick check to see if all Enderman deny options are enabled; if so, no need to check location - if ( Conf.wildernessDenyEndermanBlocks - && Conf.territoryDenyEndermanBlocks - && Conf.territoryDenyEndermanBlocksWhenOffline - && Conf.safeZoneDenyEndermanBlocks - && Conf.warZoneDenyEndermanBlocks - ) { + if + ( + Conf.wildernessDenyEndermanBlocks + && + Conf.territoryDenyEndermanBlocks + && + Conf.territoryDenyEndermanBlocksWhenOffline + && + Conf.safeZoneDenyEndermanBlocks + && + Conf.warZoneDenyEndermanBlocks + ) + { return true; } FLocation fLoc = new FLocation(loc); Faction claimFaction = Board.getFactionAt(fLoc); - if (claimFaction.isNone()) { + if (claimFaction.isNone()) + { return Conf.wildernessDenyEndermanBlocks; } - else if (claimFaction.isNormal()) { + else if (claimFaction.isNormal()) + { return claimFaction.hasPlayersOnline() ? Conf.territoryDenyEndermanBlocks : Conf.territoryDenyEndermanBlocksWhenOffline; } - else if (claimFaction.isSafeZone()) { + else if (claimFaction.isSafeZone()) + { return Conf.safeZoneDenyEndermanBlocks; } - else if (claimFaction.isWarZone()) { + else if (claimFaction.isWarZone()) + { return Conf.warZoneDenyEndermanBlocks; } diff --git a/src/com/massivecraft/factions/listeners/FactionsPlayerListener.java b/src/com/massivecraft/factions/listeners/FactionsPlayerListener.java index 57d18b20..1e517e27 100644 --- a/src/com/massivecraft/factions/listeners/FactionsPlayerListener.java +++ b/src/com/massivecraft/factions/listeners/FactionsPlayerListener.java @@ -25,46 +25,56 @@ import org.bukkit.event.player.PlayerRespawnEvent; import com.massivecraft.factions.Board; import com.massivecraft.factions.Conf; -import com.massivecraft.factions.integration.Econ; import com.massivecraft.factions.FLocation; import com.massivecraft.factions.FPlayer; +import com.massivecraft.factions.FPlayers; import com.massivecraft.factions.Faction; import com.massivecraft.factions.Factions; +import com.massivecraft.factions.P; import com.massivecraft.factions.integration.SpoutFeatures; import com.massivecraft.factions.struct.Role; import com.massivecraft.factions.struct.Relation; -import com.massivecraft.factions.util.TextUtil; +import com.massivecraft.factions.zcore.util.TextUtil; + import java.util.logging.Level; -public class FactionsPlayerListener extends PlayerListener{ - +public class FactionsPlayerListener extends PlayerListener +{ + public P p; + public FactionsPlayerListener(P p) + { + this.p = p; + } + @Override - public void onPlayerChat(PlayerChatEvent event) { - if (event.isCancelled()) { - return; - } + public void onPlayerChat(PlayerChatEvent event) + { + if (event.isCancelled()) return; Player talkingPlayer = event.getPlayer(); String msg = event.getMessage(); // ... it was not a command. This means that it is a chat message! - FPlayer me = FPlayer.get(talkingPlayer); + FPlayer me = FPlayers.i.get(talkingPlayer); // Are we to insert the Faction tag into the format? // If we are not to insert it - we are done. - if ( ! Conf.chatTagEnabled || Conf.chatTagHandledByAnotherPlugin) { + if ( ! Conf.chatTagEnabled || Conf.chatTagHandledByAnotherPlugin) + { return; } int InsertIndex = 0; String eventFormat = event.getFormat(); - if (!Conf.chatTagReplaceString.isEmpty() && eventFormat.contains(Conf.chatTagReplaceString)) { + if (!Conf.chatTagReplaceString.isEmpty() && eventFormat.contains(Conf.chatTagReplaceString)) + { // we're using the "replace" method of inserting the faction tags // if they stuck "{FACTION_TITLE}" in there, go ahead and do it too - if (eventFormat.contains("{FACTION_TITLE}")) { + if (eventFormat.contains("{FACTION_TITLE}")) + { eventFormat = eventFormat.replace("{FACTION_TITLE}", me.getTitle()); } InsertIndex = eventFormat.indexOf(Conf.chatTagReplaceString); @@ -72,15 +82,18 @@ public class FactionsPlayerListener extends PlayerListener{ Conf.chatTagPadAfter = false; Conf.chatTagPadBefore = false; } - else if (!Conf.chatTagInsertAfterString.isEmpty() && eventFormat.contains(Conf.chatTagInsertAfterString)) { + else if (!Conf.chatTagInsertAfterString.isEmpty() && eventFormat.contains(Conf.chatTagInsertAfterString)) + { // we're using the "insert after string" method InsertIndex = eventFormat.indexOf(Conf.chatTagInsertAfterString) + Conf.chatTagInsertAfterString.length(); } - else if (!Conf.chatTagInsertBeforeString.isEmpty() && eventFormat.contains(Conf.chatTagInsertBeforeString)) { + else if (!Conf.chatTagInsertBeforeString.isEmpty() && eventFormat.contains(Conf.chatTagInsertBeforeString)) + { // we're using the "insert before string" method InsertIndex = eventFormat.indexOf(Conf.chatTagInsertBeforeString); } - else { + else + { // we'll fall back to using the index place method InsertIndex = Conf.chatTagInsertIndex; if (InsertIndex > eventFormat.length()) @@ -93,23 +106,27 @@ public class FactionsPlayerListener extends PlayerListener{ String nonColoredMsgFormat = formatStart + me.getChatTag().trim() + formatEnd; // Relation Colored? - if (Conf.chatTagRelationColored) { + if (Conf.chatTagRelationColored) + { // We must choke the standard message and send out individual messages to all players // Why? Because the relations will differ. event.setCancelled(true); - for (Player listeningPlayer : event.getRecipients()) { - FPlayer you = FPlayer.get(listeningPlayer); + for (Player listeningPlayer : event.getRecipients()) + { + FPlayer you = FPlayers.i.get(listeningPlayer); String yourFormat = formatStart + me.getChatTag(you).trim() + formatEnd; - try { + try + { listeningPlayer.sendMessage(String.format(yourFormat, talkingPlayer.getDisplayName(), msg)); } - catch (UnknownFormatConversionException ex) { - Factions.log(Level.SEVERE, "Critical error in chat message formatting! Complete format string: "+yourFormat); - Factions.log(Level.SEVERE, "First half of event.getFormat() string: "+formatStart); - Factions.log(Level.SEVERE, "Second half of event.getFormat() string: "+formatEnd); - Factions.log(Level.SEVERE, "NOTE: To fix this quickly, running this command should work: f config chatTagInsertIndex 0"); - Factions.log(Level.SEVERE, "For a more proper fix, please read the chat configuration notes on the configuration page of the Factions user guide."); + catch (UnknownFormatConversionException ex) + { + P.p.log(Level.SEVERE, "Critical error in chat message formatting! Complete format string: "+yourFormat); + P.p.log(Level.SEVERE, "First half of event.getFormat() string: "+formatStart); + P.p.log(Level.SEVERE, "Second half of event.getFormat() string: "+formatEnd); + P.p.log(Level.SEVERE, "NOTE: To fix this quickly, running this command should work: f config chatTagInsertIndex 0"); + P.p.log(Level.SEVERE, "For a more proper fix, please read the chat configuration notes on the configuration page of the Factions user guide."); ex.printStackTrace(); return; } @@ -118,28 +135,31 @@ public class FactionsPlayerListener extends PlayerListener{ // Write to the log... We will write the non colored message. String nonColoredMsg = ChatColor.stripColor(String.format(nonColoredMsgFormat, talkingPlayer.getDisplayName(), msg)); Logger.getLogger("Minecraft").info(nonColoredMsg); - } else { + } + else + { // No relation color. event.setFormat(nonColoredMsgFormat); } } @Override - public void onPlayerJoin(PlayerJoinEvent event) { + public void onPlayerJoin(PlayerJoinEvent event) + { // Make sure that all online players do have a fplayer. - final FPlayer me = FPlayer.get(event.getPlayer()); + final FPlayer me = FPlayers.i.get(event.getPlayer()); // Update the lastLoginTime for this fplayer me.setLastLoginTime(System.currentTimeMillis()); // Run the member auto kick routine. Twice to get to the admins... - FPlayer.autoLeaveOnInactivityRoutine(); - FPlayer.autoLeaveOnInactivityRoutine(); + FPlayers.i.autoLeaveOnInactivityRoutine(); + FPlayers.i.autoLeaveOnInactivityRoutine(); SpoutFeatures.updateTerritoryDisplay(me); // Appearance updates which are run when a player joins don't apply properly for other clients, so they need to be delayed slightly - Factions.instance.getServer().getScheduler().scheduleSyncDelayedTask(Factions.instance, new Runnable() { + P.p.getServer().getScheduler().scheduleSyncDelayedTask(P.p, new Runnable() { public void run() { SpoutFeatures.updateAppearances(me.getPlayer()); SpoutFeatures.updateTerritoryDisplay(me); @@ -148,27 +168,31 @@ public class FactionsPlayerListener extends PlayerListener{ } @Override - public void onPlayerQuit(PlayerQuitEvent event) { + public void onPlayerQuit(PlayerQuitEvent event) + { // Make sure player's power is up to date when they log off. - FPlayer me = FPlayer.get(event.getPlayer()); + FPlayer me = FPlayers.i.get(event.getPlayer()); me.getPower(); Faction myFaction = me.getFaction(); - if (myFaction != null) { + if (myFaction != null) + { myFaction.memberLoggedOff(); } SpoutFeatures.playerDisconnect(me); } @Override - public void onPlayerMove(PlayerMoveEvent event) { + public void onPlayerMove(PlayerMoveEvent event) + { Player player = event.getPlayer(); - FPlayer me = FPlayer.get(player); + FPlayer me = FPlayers.i.get(player); // Did we change coord? FLocation from = me.getLastStoodAt(); FLocation to = new FLocation(player.getLocation()); - if (from.equals(to)) { + if (from.equals(to)) + { return; } @@ -176,73 +200,98 @@ public class FactionsPlayerListener extends PlayerListener{ me.setLastStoodAt(to); - if (me.isMapAutoUpdating()) { + if (me.isMapAutoUpdating()) + { me.sendMessage(Board.getMap(me.getFaction(), to, player.getLocation().getYaw())); - } else { + } + else + { // Did we change "host"(faction)? Faction factionFrom = Board.getFactionAt(from); Faction factionTo = Board.getFactionAt(to); Faction myFaction = me.getFaction(); String ownersTo = myFaction.getOwnerListString(to); - if (factionFrom != factionTo) { + if (factionFrom != factionTo) + { me.sendFactionHereMessage(); - if (Conf.ownedAreasEnabled && Conf.ownedMessageOnBorder && myFaction == factionTo && !ownersTo.isEmpty()) { + if (Conf.ownedAreasEnabled && Conf.ownedMessageOnBorder && myFaction == factionTo && !ownersTo.isEmpty()) + { me.sendMessage(Conf.ownedLandMessage+ownersTo); } } - else if (Conf.ownedAreasEnabled && Conf.ownedMessageInsideTerritory && factionFrom == factionTo && myFaction == factionTo) { + else if (Conf.ownedAreasEnabled && Conf.ownedMessageInsideTerritory && factionFrom == factionTo && myFaction == factionTo) + { String ownersFrom = myFaction.getOwnerListString(from); - if (Conf.ownedMessageByChunk || !ownersFrom.equals(ownersTo)) { - if (!ownersTo.isEmpty()) { + if (Conf.ownedMessageByChunk || !ownersFrom.equals(ownersTo)) + { + if (!ownersTo.isEmpty()) + { me.sendMessage(Conf.ownedLandMessage+ownersTo); } - else if (!Conf.publicLandMessage.isEmpty()) { + else if (!Conf.publicLandMessage.isEmpty()) + { me.sendMessage(Conf.publicLandMessage); } } } } - if (me.autoClaimEnabled()) { + if (me.autoClaimEnabled()) + { Faction myFaction = me.getFaction(); - Faction otherFaction = Board.getFactionAt(to); - double cost = Econ.calculateClaimCost(myFaction.getLandRounded(), otherFaction.isNormal()); + // TODO: Why is this ("cost") here and unused??? Should it be used somewhere Brettflan? :) + // Olof just commented it out to avoid the error. + //Faction otherFaction = Board.getFactionAt(to); + //double cost = Econ.calculateClaimCost(myFaction.getLandRounded(), otherFaction.isNormal()); - if (me.getRole().value < Role.MODERATOR.value) { + if (me.getRole().value < Role.MODERATOR.value) + { me.sendMessage("You must be "+Role.MODERATOR+" to claim land."); me.enableAutoClaim(false); } - else if (Conf.worldsNoClaiming.contains(to.getWorldName())) { + else if (Conf.worldsNoClaiming.contains(to.getWorldName())) + { me.sendMessage("Sorry, this world has land claiming disabled."); me.enableAutoClaim(false); } - else if (myFaction.getLandRounded() >= myFaction.getPowerRounded()) { + else if (myFaction.getLandRounded() >= myFaction.getPowerRounded()) + { me.sendMessage("You can't claim more land! You need more power!"); me.enableAutoClaim(false); } else me.attemptClaim(false); } - else if (me.autoSafeZoneEnabled()) { - if (!Factions.hasPermManageSafeZone((CommandSender)player)) { + else if (me.autoSafeZoneEnabled()) + { + if (!P.hasPermManageSafeZone((CommandSender)player)) + { me.enableAutoSafeZone(false); - } else { + } + else + { FLocation playerFlocation = new FLocation(me); - if (!Board.getFactionAt(playerFlocation).isSafeZone()) { - Board.setFactionAt(Faction.getSafeZone(), playerFlocation); + if (!Board.getFactionAt(playerFlocation).isSafeZone()) + { + Board.setFactionAt(Factions.i.getSafeZone(), playerFlocation); me.sendMessage("This land is now a safe zone."); } } } - else if (me.autoWarZoneEnabled()) { - if (!Factions.hasPermManageWarZone((CommandSender)player)) { + else if (me.autoWarZoneEnabled()) + { + if (!P.hasPermManageWarZone((CommandSender)player)) + { me.enableAutoWarZone(false); - } else { + } + else + { FLocation playerFlocation = new FLocation(me); - if (!Board.getFactionAt(playerFlocation).isWarZone()) { - Board.setFactionAt(Faction.getWarZone(), playerFlocation); + if (!Board.getFactionAt(playerFlocation).isWarZone()) + { + Board.setFactionAt(Factions.i.getWarZone(), playerFlocation); me.sendMessage("This land is now a war zone."); } } @@ -250,36 +299,41 @@ public class FactionsPlayerListener extends PlayerListener{ } @Override - public void onPlayerInteract(PlayerInteractEvent event) { - if (event.isCancelled()) { - return; - } + public void onPlayerInteract(PlayerInteractEvent event) + { + if (event.isCancelled()) return; Block block = event.getClickedBlock(); Player player = event.getPlayer(); - if (block == null) { + if (block == null) + { return; // clicked in air, apparently } - if ( ! canPlayerUseBlock(player, block, false)) { + if ( ! canPlayerUseBlock(player, block, false)) + { event.setCancelled(true); return; } - if (event.getAction() != Action.RIGHT_CLICK_BLOCK) { + if (event.getAction() != Action.RIGHT_CLICK_BLOCK) + { return; // only interested on right-clicks for below } - if ( ! this.playerCanUseItemHere(player, block.getLocation(), event.getMaterial(), false)) { + if ( ! playerCanUseItemHere(player, block.getLocation(), event.getMaterial(), false)) + { event.setCancelled(true); return; } } - public static boolean playerCanUseItemHere(Player player, Location location, Material material, boolean justCheck) { + public static boolean playerCanUseItemHere(Player player, Location location, Material material, boolean justCheck) + { - if (Conf.adminBypassPlayers.contains(player.getName())) { + if (Conf.adminBypassPlayers.contains(player.getName())) + { return true; } @@ -287,40 +341,54 @@ public class FactionsPlayerListener extends PlayerListener{ Faction otherFaction = Board.getFactionAt(loc); if (otherFaction.hasPlayersOnline()){ - if ( ! Conf.territoryDenyUseageMaterials.contains(material)) { + if ( ! Conf.territoryDenyUseageMaterials.contains(material)) + { return true; // Item isn't one we're preventing for online factions. } - }else{ - if ( ! Conf.territoryDenyUseageMaterialsWhenOffline.contains(material)) { + } + else + { + if ( ! Conf.territoryDenyUseageMaterialsWhenOffline.contains(material)) + { return true; // Item isn't one we're preventing for offline factions. } } - FPlayer me = FPlayer.get(player); + FPlayer me = FPlayers.i.get(player); - if (otherFaction.isNone()) { - if (!Conf.wildernessDenyUseage || Factions.hasPermAdminBypass(player) || Conf.worldsNoWildernessProtection.contains(location.getWorld().getName())) { + if (otherFaction.isNone()) + { + if (!Conf.wildernessDenyUseage || P.hasPermAdminBypass(player) || Conf.worldsNoWildernessProtection.contains(location.getWorld().getName())) + { return true; // This is not faction territory. Use whatever you like here. } - if (!justCheck) { + + if (!justCheck) + { me.sendMessage("You can't use "+TextUtil.getMaterialName(material)+" in the wilderness."); } return false; } - else if (otherFaction.isSafeZone()) { - if (!Conf.safeZoneDenyUseage || Factions.hasPermManageSafeZone(player)) { + else if (otherFaction.isSafeZone()) + { + if (!Conf.safeZoneDenyUseage || P.hasPermManageSafeZone(player)) + { return true; } - if (!justCheck) { + if (!justCheck) + { me.sendMessage("You can't use "+TextUtil.getMaterialName(material)+" in a safe zone."); } return false; } - else if (otherFaction.isWarZone()) { - if (!Conf.warZoneDenyUseage || Factions.hasPermManageWarZone(player)) { + else if (otherFaction.isWarZone()) + { + if (!Conf.warZoneDenyUseage || P.hasPermManageWarZone(player)) + { return true; } - if (!justCheck) { + if (!justCheck) + { me.sendMessage("You can't use "+TextUtil.getMaterialName(material)+" in a war zone."); } return false; @@ -331,15 +399,19 @@ public class FactionsPlayerListener extends PlayerListener{ boolean ownershipFail = Conf.ownedAreasEnabled && Conf.ownedAreaDenyUseage && !otherFaction.playerHasOwnershipRights(me, loc); // Cancel if we are not in our own territory - if (!rel.isMember() && rel.confDenyUseage()) { - if (!justCheck) { + if (!rel.isMember() && rel.confDenyUseage()) + { + if (!justCheck) + { me.sendMessage("You can't use "+TextUtil.getMaterialName(material)+" in the territory of "+otherFaction.getTag(myFaction)); } return false; } // Also cancel if player doesn't have ownership rights for this claim - else if (rel.isMember() && ownershipFail && !Factions.hasPermOwnershipBypass(player)) { - if (!justCheck) { + else if (rel.isMember() && ownershipFail && !P.hasPermOwnershipBypass(player)) + { + if (!justCheck) + { me.sendMessage("You can't use "+TextUtil.getMaterialName(material)+" in this territory, it is owned by: "+myFaction.getOwnerListString(loc)); } return false; @@ -348,9 +420,11 @@ public class FactionsPlayerListener extends PlayerListener{ return true; } - public static boolean canPlayerUseBlock(Player player, Block block, boolean justCheck) { + public static boolean canPlayerUseBlock(Player player, Block block, boolean justCheck) + { - if (Conf.adminBypassPlayers.contains(player.getName())) { + if (Conf.adminBypassPlayers.contains(player.getName())) + { return true; } @@ -359,36 +433,46 @@ public class FactionsPlayerListener extends PlayerListener{ Faction otherFaction = Board.getFactionAt(loc); // no door/chest/whatever protection in wilderness, war zones, or safe zones - if (!otherFaction.isNormal()) { + if (!otherFaction.isNormal()) + { return true; } // We only care about some material types. - if (otherFaction.hasPlayersOnline()){ - if ( ! Conf.territoryProtectedMaterials.contains(material)) { + if (otherFaction.hasPlayersOnline()) + { + if ( ! Conf.territoryProtectedMaterials.contains(material)) + { return true; } - } else { - if ( ! Conf.territoryProtectedMaterialsWhenOffline.contains(material)) { + } + else + { + if ( ! Conf.territoryProtectedMaterialsWhenOffline.contains(material)) + { return true; } } - FPlayer me = FPlayer.get(player); + FPlayer me = FPlayers.i.get(player); Faction myFaction = me.getFaction(); Relation rel = myFaction.getRelation(otherFaction); boolean ownershipFail = Conf.ownedAreasEnabled && Conf.ownedAreaProtectMaterials && !otherFaction.playerHasOwnershipRights(me, loc); // You may use any block unless it is another faction's territory... - if (rel.isNeutral() || (rel.isEnemy() && Conf.territoryEnemyProtectMaterials) || (rel.isAlly() && Conf.territoryAllyProtectMaterials)) { - if (!justCheck) { + if (rel.isNeutral() || (rel.isEnemy() && Conf.territoryEnemyProtectMaterials) || (rel.isAlly() && Conf.territoryAllyProtectMaterials)) + { + if (!justCheck) + { me.sendMessage("You can't use "+TextUtil.getMaterialName(material)+" in the territory of "+otherFaction.getTag(myFaction)); } return false; } // Also cancel if player doesn't have ownership rights for this claim - else if (rel.isMember() && ownershipFail && !Factions.hasPermOwnershipBypass(player)) { - if (!justCheck) { + else if (rel.isMember() && ownershipFail && !P.hasPermOwnershipBypass(player)) + { + if (!justCheck) + { me.sendMessage("You can't use "+TextUtil.getMaterialName(material)+" in this territory, it is owned by: "+myFaction.getOwnerListString(loc)); } return false; @@ -398,12 +482,25 @@ public class FactionsPlayerListener extends PlayerListener{ } @Override - public void onPlayerRespawn(PlayerRespawnEvent event) { - FPlayer me = FPlayer.get(event.getPlayer()); + public void onPlayerRespawn(PlayerRespawnEvent event) + { + FPlayer me = FPlayers.i.get(event.getPlayer()); Location home = me.getFaction().getHome(); - if ( Conf.homesEnabled && Conf.homesTeleportToOnDeath && home != null && - (Conf.homesRespawnFromNoPowerLossWorlds || !Conf.worldsNoPowerLoss.contains(event.getPlayer().getWorld().getName())) - ) { + if + ( + Conf.homesEnabled + && + Conf.homesTeleportToOnDeath + && + home != null + && + ( + Conf.homesRespawnFromNoPowerLossWorlds + || + ! Conf.worldsNoPowerLoss.contains(event.getPlayer().getWorld().getName()) + ) + ) + { event.setRespawnLocation(home); } } @@ -411,100 +508,118 @@ public class FactionsPlayerListener extends PlayerListener{ // For some reason onPlayerInteract() sometimes misses bucket events depending on distance (something like 2-3 blocks away isn't detected), // but these separate bucket events below always fire without fail @Override - public void onPlayerBucketEmpty(PlayerBucketEmptyEvent event) { - if (event.isCancelled()) { - return; - } + public void onPlayerBucketEmpty(PlayerBucketEmptyEvent event) + { + if (event.isCancelled()) return; Block block = event.getBlockClicked(); Player player = event.getPlayer(); - if ( ! this.playerCanUseItemHere(player, block.getLocation(), event.getBucket(), false)) { + if ( ! playerCanUseItemHere(player, block.getLocation(), event.getBucket(), false)) + { event.setCancelled(true); return; } } @Override - public void onPlayerBucketFill(PlayerBucketFillEvent event) { - if (event.isCancelled()) { - return; - } + public void onPlayerBucketFill(PlayerBucketFillEvent event) + { + if (event.isCancelled()) return; Block block = event.getBlockClicked(); Player player = event.getPlayer(); - if ( ! this.playerCanUseItemHere(player, block.getLocation(), event.getBucket(), false)) { + if ( ! playerCanUseItemHere(player, block.getLocation(), event.getBucket(), false)) + { event.setCancelled(true); return; } } @Override - public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) { - if (event.isCancelled()) { - return; - } + public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) + { + if (event.isCancelled()) return; - if (preventCommand(event.getMessage().toLowerCase(), event.getPlayer())) { + if (preventCommand(event.getMessage().toLowerCase(), event.getPlayer())) + { event.setCancelled(true); } } - public static boolean preventCommand(String fullCmd, Player player) { - if ((Conf.territoryNeutralDenyCommands.isEmpty() && Conf.territoryEnemyDenyCommands.isEmpty())) { + public static boolean preventCommand(String fullCmd, Player player) + { + if ((Conf.territoryNeutralDenyCommands.isEmpty() && Conf.territoryEnemyDenyCommands.isEmpty())) + { return false; } - FPlayer me = FPlayer.get(player); + FPlayer me = FPlayers.i.get(player); - if (!me.isInOthersTerritory()) { + if (!me.isInOthersTerritory()) + { return false; } Relation rel = me.getRelationToLocation(); - if (rel.isAtLeast(Relation.ALLY)) { + if (rel.isAtLeast(Relation.ALLY)) + { return false; } String shortCmd = fullCmd.substring(1); // Get rid of the slash at the beginning - if ( - rel.isNeutral() - && !Conf.territoryNeutralDenyCommands.isEmpty() - && !Conf.adminBypassPlayers.contains(me.getName()) - ) { + if + ( + rel.isNeutral() + && + ! Conf.territoryNeutralDenyCommands.isEmpty() + && + ! Conf.adminBypassPlayers.contains(me.getName()) + ) + { Iterator iter = Conf.territoryNeutralDenyCommands.iterator(); String cmdCheck; - while (iter.hasNext()) { + while (iter.hasNext()) + { cmdCheck = iter.next(); - if (cmdCheck == null) { + if (cmdCheck == null) + { iter.remove(); continue; } cmdCheck = cmdCheck.toLowerCase(); - if (fullCmd.startsWith(cmdCheck) || shortCmd.startsWith(cmdCheck)) { + if (fullCmd.startsWith(cmdCheck) || shortCmd.startsWith(cmdCheck)) + { me.sendMessage("You can't use the command \""+fullCmd+"\" in neutral territory."); return true; } } } - else if ( - rel.isEnemy() - && !Conf.territoryEnemyDenyCommands.isEmpty() - && !Conf.adminBypassPlayers.contains(me.getName()) - ) { + else if + ( + rel.isEnemy() + && + ! Conf.territoryEnemyDenyCommands.isEmpty() + && + ! Conf.adminBypassPlayers.contains(me.getName()) + ) + { Iterator iter = Conf.territoryEnemyDenyCommands.iterator(); String cmdCheck; - while (iter.hasNext()) { + while (iter.hasNext()) + { cmdCheck = iter.next(); - if (cmdCheck == null) { + if (cmdCheck == null) + { iter.remove(); continue; } cmdCheck = cmdCheck.toLowerCase(); - if (fullCmd.startsWith(cmdCheck) || shortCmd.startsWith(cmdCheck)) { + if (fullCmd.startsWith(cmdCheck) || shortCmd.startsWith(cmdCheck)) + { me.sendMessage("You can't use the command \""+fullCmd+"\" in enemy territory."); return true; } @@ -514,20 +629,21 @@ public class FactionsPlayerListener extends PlayerListener{ } @Override - public void onPlayerKick(PlayerKickEvent event) { - if (event.isCancelled()) { - return; - } + public void onPlayerKick(PlayerKickEvent event) + { + if (event.isCancelled()) return; - FPlayer badGuy = FPlayer.get(event.getPlayer()); - if (badGuy == null) { + FPlayer badGuy = FPlayers.i.get(event.getPlayer()); + if (badGuy == null) + { return; } SpoutFeatures.playerDisconnect(badGuy); // if player was banned (not just kicked), get rid of their stored info - if (event.getReason().equals("Banned by admin.")) { + if (event.getReason().equals("Banned by admin.")) + { badGuy.leave(false); badGuy.markForDeletion(true); } diff --git a/src/com/massivecraft/factions/listeners/FactionsServerListener.java b/src/com/massivecraft/factions/listeners/FactionsServerListener.java index d5bbdded..f9e01942 100644 --- a/src/com/massivecraft/factions/listeners/FactionsServerListener.java +++ b/src/com/massivecraft/factions/listeners/FactionsServerListener.java @@ -5,42 +5,60 @@ import org.bukkit.event.server.ServerListener; import org.bukkit.event.server.PluginDisableEvent; import org.bukkit.event.server.PluginEnableEvent; +import com.massivecraft.factions.P; import com.massivecraft.factions.integration.Econ; import com.massivecraft.factions.integration.SpoutFeatures; -public class FactionsServerListener extends ServerListener { +public class FactionsServerListener extends ServerListener +{ + public P p; + public FactionsServerListener(P p) + { + this.p = p; + } + @Override - public void onPluginDisable(PluginDisableEvent event) { + public void onPluginDisable(PluginDisableEvent event) + { String name = event.getPlugin().getDescription().getName(); - if (Econ.registerHooked() && name.equals("Register")) { + if (Econ.registerHooked() && name.equals("Register")) + { Econ.registerSet(false); } - else if (Econ.iConomyHooked() && name.equals("iConomy")) { + else if (Econ.iConomyHooked() && name.equals("iConomy")) + { Econ.iConomySet(false); } - else if (Econ.essentialsEcoHooked() && name.equals("Essentials")) { + else if (Econ.essentialsEcoHooked() && name.equals("Essentials")) + { Econ.essentialsEcoSet(false); } - else if (name.equals("Spout")) { + else if (name.equals("Spout")) + { SpoutFeatures.setAvailable(false, ""); } } @Override - public void onPluginEnable(PluginEnableEvent event) { + public void onPluginEnable(PluginEnableEvent event) + { Plugin plug = event.getPlugin(); String name = plug.getDescription().getName(); - if (!Econ.registerHooked() && name.equals("Register") && plug.getClass().getName().equals("com.nijikokun.register.Register")) { + if ( ! Econ.registerHooked() && name.equals("Register") && plug.getClass().getName().equals("com.nijikokun.register.Register")) + { Econ.registerSet(true); } - else if (!Econ.iConomyHooked() && name.equals("iConomy") && plug.getClass().getName().equals("com.iConomy.iConomy")) { + else if ( ! Econ.iConomyHooked() && name.equals("iConomy") && plug.getClass().getName().equals("com.iConomy.iConomy")) + { Econ.iConomySet(true); } - else if (!Econ.essentialsEcoHooked() && name.equals("Essentials")) { + else if ( ! Econ.essentialsEcoHooked() && name.equals("Essentials")) + { Econ.essentialsEcoSet(true); } - else if (name.equals("Spout")) { + else if (name.equals("Spout")) + { SpoutFeatures.setAvailable(true, plug.getDescription().getFullName()); } } diff --git a/src/com/massivecraft/factions/struct/Relation.java b/src/com/massivecraft/factions/struct/Relation.java index dc525553..dfba0f22 100644 --- a/src/com/massivecraft/factions/struct/Relation.java +++ b/src/com/massivecraft/factions/struct/Relation.java @@ -5,7 +5,8 @@ import org.bukkit.ChatColor; import com.massivecraft.factions.Conf; -public enum Relation { +public enum Relation +{ MEMBER(3, "member"), ALLY(2, "ally"), NEUTRAL(1, "neutral"), @@ -20,107 +21,145 @@ public enum Relation { } @Override - public String toString() { + public String toString() + { return this.nicename; } - public boolean isMember() { + // TODO: Insane way to use enums!!!? + public boolean isMember() + { return this.value == MEMBER.value; } - public boolean isAlly() { + public boolean isAlly() + { return this.value == ALLY.value; } - public boolean isNeutral() { + public boolean isNeutral() + { return this.value == NEUTRAL.value; } - public boolean isEnemy() { + public boolean isEnemy() + { return this.value == ENEMY.value; } - public boolean isAtLeast(Relation relation) { + public boolean isAtLeast(Relation relation) + { return this.value >= relation.value; } - public boolean isAtMost(Relation relation) { + public boolean isAtMost(Relation relation) + { return this.value <= relation.value; } - public ChatColor getColor() { - if (this.value == MEMBER.value) { + public ChatColor getColor() + { + if (this.value == MEMBER.value) + { return Conf.colorMember; - } else if (this.value == ALLY.value) { + } + else if (this.value == ALLY.value) + { return Conf.colorAlly; - } else if (this.value == NEUTRAL.value) { + } + else if (this.value == NEUTRAL.value) + { return Conf.colorNeutral; - } else { + } + else + { return Conf.colorEnemy; } } // return appropriate Conf setting for DenyBuild based on this relation and their online status - public boolean confDenyBuild(boolean online) { - if (online) { - if (isEnemy()) { + public boolean confDenyBuild(boolean online) + { + if (online) + { + if (isEnemy()) + { return Conf.territoryEnemyDenyBuild; } - else if (isAlly()) { + else if (isAlly()) + { return Conf.territoryAllyDenyBuild; } - else { + else + { return Conf.territoryDenyBuild; } } - else { - if (isEnemy()) { + else + { + if (isEnemy()) + { return Conf.territoryEnemyDenyBuildWhenOffline; } - else if (isAlly()) { + else if (isAlly()) + { return Conf.territoryAllyDenyBuildWhenOffline; } - else { + else + { return Conf.territoryDenyBuildWhenOffline; } } } // return appropriate Conf setting for PainBuild based on this relation and their online status - public boolean confPainBuild(boolean online) { - if (online) { - if (isEnemy()) { + public boolean confPainBuild(boolean online) + { + if (online) + { + if (isEnemy()) + { return Conf.territoryEnemyPainBuild; } - else if (isAlly()) { + else if (isAlly()) + { return Conf.territoryAllyPainBuild; } - else { + else + { return Conf.territoryPainBuild; } } - else { - if (isEnemy()) { + else + { + if (isEnemy()) + { return Conf.territoryEnemyPainBuildWhenOffline; } - else if (isAlly()) { + else if (isAlly()) + { return Conf.territoryAllyPainBuildWhenOffline; } - else { + else + { return Conf.territoryPainBuildWhenOffline; } } } // return appropriate Conf setting for DenyUseage based on this relation - public boolean confDenyUseage() { - if (isEnemy()) { + public boolean confDenyUseage() + { + if (isEnemy()) + { return Conf.territoryEnemyDenyUseage; } - else if (isAlly()) { + else if (isAlly()) + { return Conf.territoryAllyDenyUseage; } - else { + else + { return Conf.territoryDenyUseage; } } diff --git a/src/com/massivecraft/factions/util/DiscUtil.java b/src/com/massivecraft/factions/util/DiscUtil.java deleted file mode 100644 index 0d2ce25f..00000000 --- a/src/com/massivecraft/factions/util/DiscUtil.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.massivecraft.factions.util; - -import java.io.*; - -/** - * Harddisc related methods such as read and write. - */ -public class DiscUtil { - /** - * Convenience function for writing a string to a file. - */ - public static void write(File file, String content) throws IOException { - BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, false), "UTF8")); - out.write(content); - out.close(); - } - - /** - * Convenience function for reading a file as a string. - */ - public static String read(File file) throws IOException { - BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8")); - String ret = new String(new byte[0], "UTF-8"); - - String line; - while ((line = in.readLine()) != null) { - ret += line; - } - - in.close(); - return ret; - } -} diff --git a/src/com/massivecraft/factions/util/EntityUtil.java b/src/com/massivecraft/factions/util/EntityUtil.java deleted file mode 100644 index 9e5411d3..00000000 --- a/src/com/massivecraft/factions/util/EntityUtil.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.massivecraft.factions.util; - -import org.bukkit.entity.Creature; -import org.bukkit.entity.CreatureType; -import org.bukkit.entity.Entity; - -public class EntityUtil { - public static CreatureType creatureTypeFromEntity(Entity entity) { - if ( ! (entity instanceof Creature)) { - return null; - } - - String name = entity.getClass().getSimpleName(); - name = name.substring(5); // Remove "Craft" - - return CreatureType.fromName(name); - } -} diff --git a/src/com/massivecraft/factions/util/JarLoader.java b/src/com/massivecraft/factions/util/JarLoader.java deleted file mode 100644 index a0840780..00000000 --- a/src/com/massivecraft/factions/util/JarLoader.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.massivecraft.factions.util; - -import java.io.File; -import java.lang.reflect.Method; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class JarLoader { - - private static URLClassLoader sysloader = (URLClassLoader)ClassLoader.getSystemClassLoader(); - - public static boolean load(String filename) { - return load(new File(filename)); - } - - public static boolean load(File file) { - if ( ! file.exists()) { - log("This file does not exist: " + file); - return false; - } - - try { - return load(file.toURI().toURL()); - } catch (MalformedURLException e) { - log("The url for \""+file+"\" was malformed." + e); - return false; - } - } - - public static boolean load(URL url) { - // If the file already is loaded we can skip it - for (URL otherUrl : sysloader.getURLs()) { - if (otherUrl.sameFile(url)) { - return true; - } - } - - try { - Method addURLMethod = URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{ URL.class }); - addURLMethod.setAccessible(true); - addURLMethod.invoke(sysloader, new Object[]{ url }); - return true; - } catch (Exception e) { - log("Failed to load \""+url+"\":" + e); - return false; - } - } - - // -------------------------------------------- // - // Logger - // -------------------------------------------- // - private static void log(Object o) { - Logger.getLogger("Minecraft").log(Level.SEVERE, "[JAR-LOADER] " + o); - } - -} \ No newline at end of file diff --git a/src/com/massivecraft/factions/util/MapFLocToStringSetTypeAdapter.java b/src/com/massivecraft/factions/util/MapFLocToStringSetTypeAdapter.java index 77f27ac7..2a1cbfc4 100644 --- a/src/com/massivecraft/factions/util/MapFLocToStringSetTypeAdapter.java +++ b/src/com/massivecraft/factions/util/MapFLocToStringSetTypeAdapter.java @@ -19,16 +19,19 @@ import com.google.gson.JsonPrimitive; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; import com.massivecraft.factions.FLocation; -import com.massivecraft.factions.Factions; +import com.massivecraft.factions.P; -public class MapFLocToStringSetTypeAdapter implements JsonDeserializer>>, JsonSerializer>> { +public class MapFLocToStringSetTypeAdapter implements JsonDeserializer>>, JsonSerializer>> +{ @Override - public Map> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + public Map> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException + { try { JsonObject obj = json.getAsJsonObject(); - if (obj == null) { + if (obj == null) + { return null; } @@ -39,16 +42,19 @@ public class MapFLocToStringSetTypeAdapter implements JsonDeserializer entry : obj.entrySet()) { + for (Entry entry : obj.entrySet()) + { worldName = entry.getKey(); - for (Entry entry2 : entry.getValue().getAsJsonObject().entrySet()) { + for (Entry entry2 : entry.getValue().getAsJsonObject().entrySet()) + { coords = entry2.getKey().trim().split("[,\\s]+"); x = Integer.parseInt(coords[0]); z = Integer.parseInt(coords[1]); nameSet = new HashSet(); iter = entry2.getValue().getAsJsonArray().iterator(); - while (iter.hasNext()) { + while (iter.hasNext()) + { nameSet.add(iter.next().getAsString()); } @@ -58,19 +64,23 @@ public class MapFLocToStringSetTypeAdapter implements JsonDeserializer> src, Type typeOfSrc, JsonSerializationContext context) { + public JsonElement serialize(Map> src, Type typeOfSrc, JsonSerializationContext context) + { JsonObject obj = new JsonObject(); try { - if (src != null) { + if (src != null) + { FLocation loc; String locWorld; Set nameSet; @@ -78,23 +88,27 @@ public class MapFLocToStringSetTypeAdapter implements JsonDeserializer> entry : src.entrySet()) { + for (Entry> entry : src.entrySet()) + { loc = entry.getKey(); locWorld = loc.getWorldName(); nameSet = entry.getValue(); - if (nameSet == null || nameSet.isEmpty()) { + if (nameSet == null || nameSet.isEmpty()) + { continue; } nameArray = new JsonArray(); iter = nameSet.iterator(); - while (iter.hasNext()) { + while (iter.hasNext()) + { nameElement = new JsonPrimitive(iter.next()); nameArray.add(nameElement); } - if ( ! obj.has(locWorld)) { + if ( ! obj.has(locWorld)) + { obj.add(locWorld, new JsonObject()); } @@ -103,9 +117,11 @@ public class MapFLocToStringSetTypeAdapter implements JsonDeserializer substanceChars = new HashSet(Arrays.asList(new String []{ + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", + "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", + "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", + "s", "t", "u", "v", "w", "x", "y", "z" + })); + + public static String getComparisonString(String str) + { + String ret = ""; + + for (char c : str.toCharArray()) + { + if (substanceChars.contains(String.valueOf(c))) + { + ret += c; + } + } + return ret.toLowerCase(); + } + } diff --git a/src/com/massivecraft/factions/util/MyLocationTypeAdapter.java b/src/com/massivecraft/factions/util/MyLocationTypeAdapter.java index 71d56ea2..7135375a 100644 --- a/src/com/massivecraft/factions/util/MyLocationTypeAdapter.java +++ b/src/com/massivecraft/factions/util/MyLocationTypeAdapter.java @@ -13,10 +13,11 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; -import com.massivecraft.factions.Factions; +import com.massivecraft.factions.P; -public class MyLocationTypeAdapter implements JsonDeserializer, JsonSerializer { +public class MyLocationTypeAdapter implements JsonDeserializer, JsonSerializer +{ private static final String WORLD = "world"; private static final String X = "x"; private static final String Y = "y"; @@ -25,14 +26,16 @@ public class MyLocationTypeAdapter implements JsonDeserializer, JsonSe private static final String PITCH = "pitch"; @Override - public Location deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { - try { + public Location deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException + { + try + { JsonObject obj = json.getAsJsonObject(); String worldname = obj.get(WORLD).getAsString(); - World world = Factions.instance.getServer().getWorld(worldname); + World world = P.p.getServer().getWorld(worldname); if (world == null) { - Factions.log(Level.WARNING, "Stored location's world \"" + worldname + "\" not found on server; dropping the location."); + P.p.log(Level.WARNING, "Stored location's world \"" + worldname + "\" not found on server; dropping the location."); return null; } @@ -44,9 +47,11 @@ public class MyLocationTypeAdapter implements JsonDeserializer, JsonSe return new Location(world, x, y, z, yaw, pitch); - } catch (Exception ex) { + } + catch (Exception ex) + { ex.printStackTrace(); - Factions.log(Level.WARNING, "Error encountered while deserializing a location."); + P.p.log(Level.WARNING, "Error encountered while deserializing a location."); return null; } } @@ -55,10 +60,11 @@ public class MyLocationTypeAdapter implements JsonDeserializer, JsonSe public JsonElement serialize(Location src, Type typeOfSrc, JsonSerializationContext context) { JsonObject obj = new JsonObject(); - try { + try + { if (src.getWorld() == null) { - Factions.log(Level.WARNING, "Passed location's world was not found on the server. Dropping the location."); + P.p.log(Level.WARNING, "Passed location's world was not found on the server. Dropping the location."); return obj; } @@ -71,9 +77,11 @@ public class MyLocationTypeAdapter implements JsonDeserializer, JsonSe return obj; - } catch (Exception ex) { + } + catch (Exception ex) + { ex.printStackTrace(); - Factions.log(Level.WARNING, "Error encountered while serializing a location."); + P.p.log(Level.WARNING, "Error encountered while serializing a location."); return obj; } } diff --git a/src/com/massivecraft/factions/util/TextUtil.java b/src/com/massivecraft/factions/util/TextUtil.java deleted file mode 100644 index 847c6677..00000000 --- a/src/com/massivecraft/factions/util/TextUtil.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.massivecraft.factions.util; -import java.util.*; - -import org.bukkit.Material; - -import com.massivecraft.factions.Conf; - - -public class TextUtil { - public static String titleize(String str) { - String line = Conf.colorChrome+repeat("_", 60); - String center = ".[ " + Conf.colorSystem + str + Conf.colorChrome + " ]."; - int pivot = line.length() / 2; - int eatLeft = center.length() / 2; - int eatRight = center.length() - eatLeft; - - if (eatLeft < pivot) - return line.substring(0, pivot - eatLeft) + center + line.substring(pivot + eatRight); - else - return center; - } - - public static String repeat(String s, int times) { - if (times <= 0) return ""; - else return s + repeat(s, times-1); - } - - public static ArrayList split(String str) { - return new ArrayList(Arrays.asList(str.trim().split("\\s+"))); - } - - public static String implode(List list, String glue) { - String ret = ""; - for (int i=0; i list) { - return implode(list, " "); - } - - /*public static String commandHelp(List aliases, String param, String desc) { - ArrayList parts = new ArrayList(); - parts.add(Conf.colorCommand+Conf.aliasBase.get(0)); - parts.add(TextUtil.implode(aliases, ", ")); - if (param.length() > 0) { - parts.add(Conf.colorParameter+param); - } - if (desc.length() > 0) { - parts.add(Conf.colorSystem+desc); - } - //Log.debug(TextUtil.implode(parts, " ")); - return TextUtil.implode(parts, " "); - }*/ - - public static String getMaterialName(Material material) { - String ret = material.toString(); - ret = ret.replace('_', ' '); - ret = ret.toLowerCase(); - return ret.substring(0, 1).toUpperCase()+ret.substring(1); - } - - /// TODO create tag whitelist!! - public static HashSet substanceChars = new HashSet(Arrays.asList(new String []{ - "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", - "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", - "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", - "s", "t", "u", "v", "w", "x", "y", "z" - })); - - public static String getComparisonString(String str) { - String ret = ""; - - for (char c : str.toCharArray()) { - if (substanceChars.contains(String.valueOf(c))) { - ret += c; - } - } - - return ret.toLowerCase(); - } -} - - diff --git a/src/com/massivecraft/factions/zcore/CommandVisibility.java b/src/com/massivecraft/factions/zcore/CommandVisibility.java new file mode 100644 index 00000000..f63964be --- /dev/null +++ b/src/com/massivecraft/factions/zcore/CommandVisibility.java @@ -0,0 +1,9 @@ +package com.massivecraft.factions.zcore; + +public enum CommandVisibility +{ + VISIBLE, // Visible commands are visible to anyone. Even those who don't have permission to use it or is of invalid sender type. + SECRET, // Secret commands are visible only to those who can use the command. These commands are usually some kind of admin commands. + INVISIBLE, // Invisible commands are invisible to everyone, even those who can use the command. + ; +} diff --git a/src/com/massivecraft/factions/zcore/MCommand.java b/src/com/massivecraft/factions/zcore/MCommand.java new file mode 100644 index 00000000..9c731b3c --- /dev/null +++ b/src/com/massivecraft/factions/zcore/MCommand.java @@ -0,0 +1,429 @@ +package com.massivecraft.factions.zcore; + +import java.util.*; +import java.util.Map.Entry; + +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import com.massivecraft.factions.zcore.MCommand; +import com.massivecraft.factions.zcore.MPlugin; +import com.massivecraft.factions.zcore.util.TextUtil; + + +public abstract class MCommand +{ + public T p; + + // The sub-commands to this command + public List> subCommands; + + // The different names this commands will react to + public List aliases; + public boolean allowNoSlashAccess; + + // Information on the args + public List requiredArgs; + public LinkedHashMap optionalArgs; + + // Help info + public String helpShort; + public List helpLong; + public CommandVisibility visibility; + + // Some information on permissions + public boolean senderMustBePlayer; + public String permission; + + // Information available on execution of the command + public CommandSender sender; // Will always be set + public Player player; // Will only be set when the sender is a player + public List args; // Will contain the arguments, or and empty list if there are none. + public List> commandChain; // The command chain used to execute this command + + public MCommand(T p) + { + this.p = p; + + this.permission = null; + + this.allowNoSlashAccess = false; + + this.subCommands = new ArrayList>(); + this.aliases = new ArrayList(); + + this.requiredArgs = new ArrayList(); + this.optionalArgs = new LinkedHashMap(); + + this.helpShort = "*Default helpShort*"; + this.helpLong = new ArrayList(); + this.visibility = CommandVisibility.VISIBLE; + } + + // The commandChain is a list of the parent command chain used to get to this command. + public void execute(CommandSender sender, List args, List> commandChain) + { + // Set the execution-time specific variables + this.sender = sender; + if (sender instanceof Player) + { + this.player = (Player)sender; + } + else + { + this.player = null; + } + this.args = args; + this.commandChain = commandChain; + + // Is there a matching sub command? + if (args.size() > 0 ) + { + for (MCommand subCommand: this.subCommands) + { + if (subCommand.aliases.contains(args.get(0))) + { + args.remove(0); + commandChain.add(this); + subCommand.execute(sender, args, commandChain); + return; + } + } + } + + if ( ! validCall(this.sender, this.args)) + { + return; + } + + perform(); + } + + public void execute(CommandSender sender, List args) + { + execute(sender, args, new ArrayList>()); + } + + // This is where the command action is performed. + public abstract void perform(); + + + // -------------------------------------------- // + // Call Validation + // -------------------------------------------- // + + /** + * In this method we validate that all prerequisites to perform this command has been met. + */ + + // TODO: There should be a boolean for silence + public boolean validCall(CommandSender sender, List args) + { + if ( ! validSenderType(sender, true)) + { + return false; + } + + if ( ! validSenderPermissions(sender, true)) + { + return false; + } + + if ( ! validArgs(args, sender)) + { + return false; + } + + return true; + } + + public boolean validSenderType(CommandSender sender, boolean informSenderIfNot) + { + if (this.senderMustBePlayer && ! (sender instanceof Player)) + { + if (informSenderIfNot) + { + sender.sendMessage(p.txt.get("command.sender_must_me_player")); + } + return false; + } + return true; + } + + public boolean validSenderPermissions(CommandSender sender, boolean informSenderIfNot) + { + if (this.permission == null) return true; + return p.perm.has(sender, this.permission, informSenderIfNot); + } + + public boolean validArgs(List args, CommandSender sender) + { + if (args.size() < this.requiredArgs.size()) + { + if (sender != null) + { + sender.sendMessage(p.txt.get("command.to_few_args")); + sender.sendMessage(this.getUseageTemplate()); + } + return false; + } + + if (args.size() > this.requiredArgs.size() + this.optionalArgs.size()) + { + if (sender != null) + { + // Get the to many string slice + List theToMany = args.subList(this.requiredArgs.size() + this.optionalArgs.size(), args.size()); + sender.sendMessage(String.format(p.txt.get("command.to_many_args"), TextUtil.implode(theToMany, " "))); + sender.sendMessage(this.getUseageTemplate()); + } + return false; + } + return true; + } + public boolean validArgs(List args) + { + return this.validArgs(args, null); + } + + // -------------------------------------------- // + // Help and Usage information + // -------------------------------------------- // + + public String getUseageTemplate(List> commandChain, boolean addShortHelp) + { + StringBuilder ret = new StringBuilder(); + ret.append(p.txt.tags("")); + ret.append('/'); + + for (MCommand mc : commandChain) + { + ret.append(TextUtil.implode(mc.aliases, ",")); + ret.append(' '); + } + + ret.append(TextUtil.implode(this.aliases, ",")); + + List args = new ArrayList(); + + for (String requiredArg : this.requiredArgs) + { + args.add("<"+requiredArg+">"); + } + + for (Entry optionalArg : this.optionalArgs.entrySet()) + { + String val = optionalArg.getValue(); + if (val == null) + { + val = ""; + } + else + { + val = "="+val; + } + args.add("["+optionalArg.getKey()+val+"]"); + } + + if (args.size() > 0) + { + ret.append(p.txt.tags("

")); + ret.append(TextUtil.implode(args, " ")); + } + + if (addShortHelp) + { + ret.append(p.txt.tags(" ")); + ret.append(this.helpShort); + } + + return ret.toString(); + } + + public String getUseageTemplate(boolean addShortHelp) + { + return getUseageTemplate(this.commandChain, addShortHelp); + } + + public String getUseageTemplate() + { + return getUseageTemplate(false); + } + + // -------------------------------------------- // + // Message Sending Helpers + // -------------------------------------------- // + + public void msg(String msg, boolean parseColors) + { + if (parseColors) + { + sender.sendMessage(p.txt.tags(msg)); + return; + } + sender.sendMessage(msg); + } + + public void msg(String msg) + { + this.msg(msg, false); + } + + public void msg(List msgs, boolean parseColors) + { + for(String msg : msgs) + { + this.msg(msg, parseColors); + } + } + + public void msg(List msgs) + { + msg(msgs, false); + } + + // -------------------------------------------- // + // Argument Readers + // -------------------------------------------- // + + // STRING + public String argAsString(int idx, String def) + { + if (this.args.size() < idx+1) + { + return def; + } + return this.args.get(idx); + } + public String argAsString(int idx) + { + return this.argAsString(idx, null); + } + + // INT + public int argAsInt(int idx, int def) + { + String str = this.argAsString(idx); + if (str == null) return def; + try + { + int ret = Integer.parseInt(str); + return ret; + } + catch (Exception e) + { + return def; + } + } + public int argAsInt(int idx) + { + return this.argAsInt(idx, -1); + } + + // Double + public double argAsDouble(int idx, double def) + { + String str = this.argAsString(idx); + if (str == null) return def; + try + { + double ret = Double.parseDouble(str); + return ret; + } + catch (Exception e) + { + return def; + } + } + public double argAsDouble(int idx) + { + return this.argAsDouble(idx, -1d); + } + + // Boolean + public boolean argAsBool(int idx, boolean def) + { + String str = this.argAsString(idx); + if (str == null) return def; + + str = str.toLowerCase(); + if (str.startsWith("y") || str.startsWith("t") || str.startsWith("on") || str.startsWith("+") || str.startsWith("1")) + { + return true; + } + return false; + } + public boolean argAsBool(int idx) + { + return this.argAsBool(idx, false); + } + + // PLAYER + public Player argAsPlayer(int idx, Player def, boolean msg) + { + Player ret = def; + + String name = this.argAsString(idx); + if (name != null) + { + Player player = Bukkit.getServer().getPlayer(name); + if (player != null) + { + ret = player; + } + } + + if (msg && ret == null) + { + // TODO: Fix this injection risk! + this.msg(p.txt.tags("The player \"

"+name+"\" could not be found.")); + } + + return ret; + } + public Player argAsPlayer(int idx, Player def) + { + return this.argAsPlayer(idx, def, true); + } + public Player argAsPlayer(int idx) + { + return this.argAsPlayer(idx, null); + } + + // BEST PLAYER MATCH + public Player argAsBestPlayerMatch(int idx, Player def, boolean msg) + { + Player ret = def; + + String name = this.argAsString(idx); + if (name != null) + { + List players = Bukkit.getServer().matchPlayer(name); + if (players.size() > 0) + { + ret = players.get(0); + } + } + + if (msg && ret == null) + { + // TODO: Fix this injection risk! + this.msg(p.txt.tags("No player match found for \"

"+name+"\".")); + } + + return ret; + } + public Player argAsBestPlayerMatch(int idx, Player def) + { + return this.argAsBestPlayerMatch(idx, def, true); + } + public Player argAsBestPlayerMatch(int idx) + { + return this.argAsPlayer(idx, null); + } + + + + +} diff --git a/src/com/massivecraft/factions/zcore/MPlugin.java b/src/com/massivecraft/factions/zcore/MPlugin.java new file mode 100644 index 00000000..2cc4a2ea --- /dev/null +++ b/src/com/massivecraft/factions/zcore/MPlugin.java @@ -0,0 +1,236 @@ +package com.massivecraft.factions.zcore; + +import java.lang.reflect.Modifier; +import java.lang.reflect.Type; +import java.util.*; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.event.Event; +import org.bukkit.plugin.PluginManager; +import org.bukkit.plugin.java.JavaPlugin; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; +import com.massivecraft.factions.zcore.persist.EM; +import com.massivecraft.factions.zcore.persist.SaveTask; +import com.massivecraft.factions.zcore.util.LibLoader; +import com.massivecraft.factions.zcore.util.PermUtil; +import com.massivecraft.factions.zcore.util.Persist; +import com.massivecraft.factions.zcore.util.TextUtil; + + +public abstract class MPlugin extends JavaPlugin +{ + // Some utils + public Persist persist; + public TextUtil txt; + public LibLoader lib; + public PermUtil perm; + + // Persist related + public Gson gson; + private Integer saveTask = null; + + // Listeners + private MPluginSecretPlayerListener mPluginSecretPlayerListener; + private MPluginSecretServerListener mPluginSecretServerListener; + + // Our stored base commands + private List> baseCommands = new ArrayList>(); + public List> getBaseCommands() { return this.baseCommands; } + + // -------------------------------------------- // + // ENABLE + // -------------------------------------------- // + private long timeEnableStart; + public boolean preEnable() + { + log("=== ENABLE START ==="); + timeEnableStart = System.currentTimeMillis(); + + // Ensure basefolder exists! + this.getDataFolder().mkdirs(); + + // Create Utility Instances + this.perm = new PermUtil(this); + this.persist = new Persist(this); + this.lib = new LibLoader(this); + + if ( ! lib.require("gson.jar", "http://search.maven.org/remotecontent?filepath=com/google/code/gson/gson/1.7.1/gson-1.7.1.jar")) return false; + this.gson = this.getGsonBuilder().create(); + + initTXT(); + + // Create and register listeners + this.mPluginSecretPlayerListener = new MPluginSecretPlayerListener(this); + this.mPluginSecretServerListener = new MPluginSecretServerListener(this); + PluginManager pm = this.getServer().getPluginManager(); + pm.registerEvent(Event.Type.PLAYER_PRELOGIN, this.mPluginSecretPlayerListener, Event.Priority.Lowest, this); + pm.registerEvent(Event.Type.PLAYER_CHAT, this.mPluginSecretPlayerListener, Event.Priority.Low, this); + pm.registerEvent(Event.Type.PLAYER_COMMAND_PREPROCESS, this.mPluginSecretPlayerListener, Event.Priority.Lowest, this); + pm.registerEvent(Event.Type.SERVER_COMMAND, this.mPluginSecretServerListener, Event.Priority.Lowest, this); + + + // Register recurring tasks + long saveTicks = 20 * 60 * 30; // Approximately every 30 min + if (saveTask == null) + { + saveTask = Bukkit.getServer().getScheduler().scheduleSyncRepeatingTask(this, new SaveTask(), saveTicks, saveTicks); + } + + return true; + } + + public void postEnable() + { + log("=== ENABLE DONE (Took "+(System.currentTimeMillis()-timeEnableStart)+"ms) ==="); + } + + public void onDisable() + { + if (saveTask != null) + { + this.getServer().getScheduler().cancelTask(saveTask); + saveTask = null; + } + EM.saveAllToDisc(); + log("Disabled"); + } + + public void suicide() + { + log("Now I suicide!"); + this.getServer().getPluginManager().disablePlugin(this); + } + + // -------------------------------------------- // + // Some inits... + // You are supposed to override these in the plugin if you aren't satisfied with the defaults + // The goal is that you always will be satisfied though. + // -------------------------------------------- // + + public GsonBuilder getGsonBuilder() + { + return new GsonBuilder() + .setPrettyPrinting() + .disableHtmlEscaping() + .serializeNulls() + .excludeFieldsWithModifiers(Modifier.TRANSIENT, Modifier.VOLATILE); + } + + // -------------------------------------------- // + // LANG AND TAGS + // -------------------------------------------- // + + // These are not supposed to be used directly. + // They are loaded and used through the TextUtil instance for the plugin. + public Map tags = new LinkedHashMap(); + public Map lang = new LinkedHashMap(); + + public void addLang() + { + this.lang.put("perm.forbidden", "You don't have permission to %s."); + this.lang.put("perm.dothat", "do that"); + this.lang.put("command.sender_must_me_player", "This command can only be used by ingame players."); + this.lang.put("command.to_few_args", "To few arguments. Use like this:"); + this.lang.put("command.to_many_args", "Strange argument \"

%s\". Use the command like this:"); + } + + public void addTags() + { + this.tags.put("black", "§0"); + this.tags.put("navy", "§1"); + this.tags.put("green", "§2"); + this.tags.put("teal", "§3"); + this.tags.put("red", "§4"); + this.tags.put("purple", "§5"); + this.tags.put("gold", "§6"); + this.tags.put("silver", "§7"); + this.tags.put("gray", "§8"); + this.tags.put("blue", "§9"); + this.tags.put("white", "§f"); + this.tags.put("lime", "§a"); + this.tags.put("aqua", "§b"); + this.tags.put("rose", "§c"); + this.tags.put("pink", "§d"); + this.tags.put("yellow", "§e"); + + this.tags.put("l", "§2"); // logo + this.tags.put("a", "§6"); // art + this.tags.put("n", "§7"); // notice + this.tags.put("i", "§e"); // info + this.tags.put("g", "§a"); // good + this.tags.put("b", "§c"); // bad + this.tags.put("h", "§d"); // highligh + this.tags.put("c", "§b"); // command + this.tags.put("p", "§3"); // parameter + } + + public void initTXT() + { + this.addLang(); + this.addTags(); + + Type type = new TypeToken>(){}.getType(); + + Map langFromFile = this.persist.load(type, "lang"); + if (langFromFile != null) this.lang.putAll(langFromFile); + this.persist.save(this.lang, "lang"); + + Map tagsFromFile = this.persist.load(type, "tags"); + if (tagsFromFile != null) this.tags.putAll(tagsFromFile); + this.persist.save(this.tags, "tags"); + + this.txt = new TextUtil(this.tags, this.lang); + } + + + // -------------------------------------------- // + // COMMAND HANDLING + // -------------------------------------------- // + + public boolean handleCommand(CommandSender sender, String commandString) + { + boolean noSlash = false; + if (commandString.startsWith("/")) + { + noSlash = true; + commandString = commandString.substring(1); + } + + for (MCommand command : this.getBaseCommands()) + { + if (noSlash && ! command.allowNoSlashAccess) continue; + + for (String alias : command.aliases) + { + if (commandString.startsWith(alias) || commandString.equals(alias+" ")) + { + List args = new ArrayList(Arrays.asList(commandString.split("\\s+"))); + args.remove(0); + command.execute(sender, args); + return true; + } + } + } + return false; + } + + + // -------------------------------------------- // + // LOGGING + // -------------------------------------------- // + public void log(Object msg) + { + log(Level.INFO, msg); + } + + public void log(Level level, Object msg) + { + Logger.getLogger("Minecraft").log(level, "["+this.getDescription().getFullName()+"] "+msg); + } +} diff --git a/src/com/massivecraft/factions/zcore/MPluginSecretPlayerListener.java b/src/com/massivecraft/factions/zcore/MPluginSecretPlayerListener.java new file mode 100644 index 00000000..02977c02 --- /dev/null +++ b/src/com/massivecraft/factions/zcore/MPluginSecretPlayerListener.java @@ -0,0 +1,55 @@ +package com.massivecraft.factions.zcore; + +import org.bukkit.event.player.PlayerChatEvent; +import org.bukkit.event.player.PlayerCommandPreprocessEvent; +import org.bukkit.event.player.PlayerListener; +import org.bukkit.event.player.PlayerPreLoginEvent; + +import com.massivecraft.factions.zcore.persist.EM; +import com.massivecraft.factions.zcore.persist.Entity; +import com.massivecraft.factions.zcore.persist.EntityCollection; +import com.massivecraft.factions.zcore.persist.PlayerEntityCollection; + +public class MPluginSecretPlayerListener extends PlayerListener +{ + private MPlugin p; + public MPluginSecretPlayerListener(MPlugin p) + { + this.p = p; + } + + @Override + public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) + { + if (event.isCancelled()) return; + + if (p.handleCommand(event.getPlayer(), event.getMessage())) + { + event.setCancelled(true); + } + } + + @Override + public void onPlayerChat(PlayerChatEvent event) + { + if (event.isCancelled()) return; + + if (p.handleCommand(event.getPlayer(), event.getMessage())) + { + event.setCancelled(true); + } + } + + @Override + public void onPlayerPreLogin(PlayerPreLoginEvent event) + { + for (EntityCollection ecoll : EM.class2Entities.values()) + { + if (ecoll instanceof PlayerEntityCollection) + { + ecoll.get(event.getName()); + } + } + } + +} diff --git a/src/com/massivecraft/factions/zcore/MPluginSecretServerListener.java b/src/com/massivecraft/factions/zcore/MPluginSecretServerListener.java new file mode 100644 index 00000000..dcd6585d --- /dev/null +++ b/src/com/massivecraft/factions/zcore/MPluginSecretServerListener.java @@ -0,0 +1,26 @@ +package com.massivecraft.factions.zcore; + +import org.bukkit.event.server.ServerCommandEvent; +import org.bukkit.event.server.ServerListener; + +public class MPluginSecretServerListener extends ServerListener +{ + private MPlugin p; + public MPluginSecretServerListener(MPlugin p) + { + this.p = p; + } + + // This method is not perfect. It says unknown console command. + @Override + public void onServerCommand(ServerCommandEvent event) + { + if (event.getCommand().length() == 0) return; + + if (p.handleCommand(event.getSender(), event.getCommand())) + { + event.setCommand(""); + } + } + +} diff --git a/src/com/massivecraft/factions/zcore/persist/EM.java b/src/com/massivecraft/factions/zcore/persist/EM.java new file mode 100644 index 00000000..fada416b --- /dev/null +++ b/src/com/massivecraft/factions/zcore/persist/EM.java @@ -0,0 +1,74 @@ +package com.massivecraft.factions.zcore.persist; + +import java.util.*; + +import com.massivecraft.factions.zcore.persist.Entity; +import com.massivecraft.factions.zcore.persist.EntityCollection; + +public class EM +{ + public static Map, EntityCollection> class2Entities = new LinkedHashMap, EntityCollection>(); + + @SuppressWarnings("unchecked") + public static EntityCollection getEntitiesCollectionForEntityClass(Class entityClass) + { + return (EntityCollection) class2Entities.get(entityClass); + } + + public static void setEntitiesCollectionForEntityClass(Class entityClass, EntityCollection entities) + { + class2Entities.put(entityClass, entities); + } + + // -------------------------------------------- // + // ATTACH AND DETACH + // -------------------------------------------- // + + @SuppressWarnings("unchecked") + public static void attach(T entity) + { + EntityCollection ec = (EntityCollection) getEntitiesCollectionForEntityClass(entity.getClass()); + ec.attach(entity); + } + + @SuppressWarnings("unchecked") + public static void detach(T entity) + { + EntityCollection ec = (EntityCollection) getEntitiesCollectionForEntityClass(entity.getClass()); + ec.detach(entity); + } + + @SuppressWarnings("unchecked") + public static boolean attached(T entity) + { + EntityCollection ec = (EntityCollection) getEntitiesCollectionForEntityClass(entity.getClass()); + return ec.attached(entity); + } + + @SuppressWarnings("unchecked") + public static boolean detached(T entity) + { + EntityCollection ec = (EntityCollection) getEntitiesCollectionForEntityClass(entity.getClass()); + return ec.detached(entity); + } + + // -------------------------------------------- // + // DISC + // -------------------------------------------- // + + public static void saveAllToDisc() + { + for (EntityCollection ec : class2Entities.values()) + { + ec.saveToDisc(); + } + } + + public static void loadAllFromDisc() + { + for (EntityCollection ec : class2Entities.values()) + { + ec.loadFromDisc(); + } + } +} diff --git a/src/com/massivecraft/factions/zcore/persist/Entity.java b/src/com/massivecraft/factions/zcore/persist/Entity.java new file mode 100644 index 00000000..1b800361 --- /dev/null +++ b/src/com/massivecraft/factions/zcore/persist/Entity.java @@ -0,0 +1,65 @@ +package com.massivecraft.factions.zcore.persist; + +public abstract class Entity +{ + public Entity() + { + + } + + protected transient String id = null; + + public String getId() + { + return id; + } + + protected void setId(String id) + { + this.id = id; + } + + public boolean shouldBeSaved() + { + return true; + } + + // -------------------------------------------- // + // ATTACH AND DETACH + // -------------------------------------------- // + + public void attach() + { + EM.attach(this); + } + + public void detach() + { + EM.detach(this); + } + + public boolean attached() + { + return EM.attached(this); + } + + public boolean detached() + { + return EM.detached(this); + } + + // -------------------------------------------- // + // EVENTS + // -------------------------------------------- // + + public void preDetach() + { + + } + + public void postDetach() + { + + } + +} diff --git a/src/com/massivecraft/factions/zcore/persist/EntityCollection.java b/src/com/massivecraft/factions/zcore/persist/EntityCollection.java new file mode 100644 index 00000000..e3169a5c --- /dev/null +++ b/src/com/massivecraft/factions/zcore/persist/EntityCollection.java @@ -0,0 +1,250 @@ +package com.massivecraft.factions.zcore.persist; + +import java.io.File; +import java.lang.reflect.Type; +import java.util.*; +import java.util.Map.Entry; + +import com.google.gson.Gson; +import com.massivecraft.factions.zcore.util.DiscUtil; + +public abstract class EntityCollection +{ + // -------------------------------------------- // + // FIELDS + // -------------------------------------------- // + + // These must be instantiated in order to allow for different configuration (orders, comparators etc) + private Collection entities; + private Map id2entity; + + // If the entities are creative they will create a new instance if a non existent id was requested + private boolean creative; + public boolean isCreative() { return creative; } + public void setCreative(boolean creative) { this.creative = creative; } + + // This is the auto increment for the primary key "id" + private int nextId; + + // This ugly crap is necessary due to java type erasure + private Class entityClass; + public abstract Type getMapType(); // This is special stuff for GSON. + + // Info on how to persist + private Gson gson; + public Gson getGson() { return gson; } + public void setGson(Gson gson) { this.gson = gson; } + + private File file; + public File getFile() { return file; } + public void setFile(File file) { this.file = file; } + + // -------------------------------------------- // + // CONSTRUCTORS + // -------------------------------------------- // + + public EntityCollection(Class entityClass, Collection entities, Map id2entity, File file, Gson gson, boolean creative) + { + this.entityClass = entityClass; + this.entities = entities; + this.id2entity = id2entity; + this.file = file; + this.gson = gson; + this.creative = creative; + this.nextId = 1; + + EM.setEntitiesCollectionForEntityClass(this.entityClass, this); + } + + public EntityCollection(Class entityClass, Collection entities, Map id2entity, File file, Gson gson) + { + this(entityClass, entities, id2entity, file, gson, false); + } + + // -------------------------------------------- // + // GET + // -------------------------------------------- // + + public Collection get() + { + return entities; + } + + public Map getMap() + { + return this.id2entity; + } + + public E get(String id) + { + if (this.creative) return this.getCreative(id); + return id2entity.get(id); + } + + public E getCreative(String id) + { + E e = id2entity.get(id); + if (e != null) return e; + return this.create(id); + } + + public boolean exists(String id) + { + return id2entity.get(id) != null; + } + + // -------------------------------------------- // + // CREATE + // -------------------------------------------- // + + public E create() + { + return this.create(this.getNextId()); + } + + public E create(String id) + { + if ( ! this.isIdFree(id)) return null; + + E e = null; + try + { + e = this.entityClass.newInstance(); + } catch (Exception ignored) {} + + e.setId(id); + this.entities.add(e); + this.id2entity.put(e.getId(), e); + this.updateNextIdForId(id); + return e; + } + + // -------------------------------------------- // + // ATTACH AND DETACH + // -------------------------------------------- // + + public void attach(E entity) + { + if (entity.getId() != null) return; + entity.setId(this.getNextId()); + this.entities.add(entity); + this.id2entity.put(entity.getId(), entity); + } + + public void detach(E entity) + { + entity.preDetach(); + this.entities.remove(entity); + this.id2entity.remove(entity.getId()); + entity.postDetach(); + } + + public void detach(String id) + { + E entity = this.id2entity.get(id); + if (entity == null) return; + this.detach(entity); + } + + public boolean attached(E entity) + { + return this.entities.contains(entity); + } + + public boolean detached(E entity) + { + return ! this.attached(entity); + } + + // -------------------------------------------- // + // DISC + // -------------------------------------------- // + + public boolean saveToDisc() + { + Map entitiesThatShouldBeSaved = new HashMap(); + for (E entity : this.entities) + { + if (entity.shouldBeSaved()) + { + entitiesThatShouldBeSaved.put(entity.getId(), entity); + } + } + + return this.saveCore(entitiesThatShouldBeSaved); + } + + private boolean saveCore(Map entities) + { + return DiscUtil.writeCatch(this.file, this.gson.toJson(entities)); + } + + public boolean loadFromDisc() + { + Map id2entity = this.loadCore(); + if (id2entity == null) return false; + this.entities.clear(); + this.entities.addAll(id2entity.values()); + this.id2entity.clear(); + this.id2entity.putAll(id2entity); + this.fillIds(); + return true; + } + + private Map loadCore() + { + if ( ! this.file.exists()) + { + return new HashMap(); + } + + String content = DiscUtil.readCatch(this.file); + if (content == null) + { + return null; + } + + Type type = this.getMapType(); + return this.gson.fromJson(content, type); + } + + // -------------------------------------------- // + // ID MANAGEMENT + // -------------------------------------------- // + + public String getNextId() + { + this.nextId += 1; + return "" + (nextId - 1); + } + + public boolean isIdFree(String id) + { + return ! this.id2entity.containsKey(id); + } + + protected void fillIds() + { + this.nextId = 1; + for(Entry entry : this.id2entity.entrySet()) + { + String id = entry.getKey(); + E entity = entry.getValue(); + entity.id = id; + this.updateNextIdForId(id); + } + } + + protected void updateNextIdForId(String id) + { + try + { + int idAsInt = Integer.parseInt(id); + if (this.nextId < idAsInt) + { + this.nextId = idAsInt + 1; + } + } catch (Exception ignored) {} + } + +} diff --git a/src/com/massivecraft/factions/zcore/persist/PlayerEntity.java b/src/com/massivecraft/factions/zcore/persist/PlayerEntity.java new file mode 100644 index 00000000..f8ccced9 --- /dev/null +++ b/src/com/massivecraft/factions/zcore/persist/PlayerEntity.java @@ -0,0 +1,44 @@ +package com.massivecraft.factions.zcore.persist; + +import java.util.List; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +public class PlayerEntity extends Entity +{ + public Player getPlayer() + { + return Bukkit.getPlayer(this.getId()); + } + + public boolean isOnline() + { + return this.getPlayer() != null; + } + + public boolean isOffline() + { + return ! isOnline(); + } + + // -------------------------------------------- // + // Message Sending Helpers + // -------------------------------------------- // + + public void sendMessage(String msg) + { + Player player = this.getPlayer(); + if (player == null) return; + player.sendMessage(msg); + } + + public void sendMessage(List msgs) + { + for(String msg : msgs) + { + this.sendMessage(msg); + } + } + +} diff --git a/src/com/massivecraft/factions/zcore/persist/PlayerEntityCollection.java b/src/com/massivecraft/factions/zcore/persist/PlayerEntityCollection.java new file mode 100644 index 00000000..81404c7c --- /dev/null +++ b/src/com/massivecraft/factions/zcore/persist/PlayerEntityCollection.java @@ -0,0 +1,46 @@ +package com.massivecraft.factions.zcore.persist; + +import java.io.File; +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import com.google.gson.Gson; + +/** + * The PlayerEntityCollection is an EntityCollection with the extra features + * a player skin usually requires. + * + * This entity collection is not only creative. It even creates the instance for the player + * when the player logs in to the server. + * + * This way we can be sure that PlayerEntityCollection.get() will contain + * all entities in PlayerEntityCollection.getOnline() + */ +public abstract class PlayerEntityCollection extends EntityCollection +{ + public PlayerEntityCollection(Class entityClass, Collection entities, Map id2entity, File file, Gson gson) + { + super(entityClass, entities, id2entity, file, gson, true); + } + + public E get(Player player) + { + return this.get(player.getName()); + } + + public Set getOnline() + { + Set entities = new HashSet(); + for (Player player : Bukkit.getServer().getOnlinePlayers()) + { + entities.add(this.get(player)); + } + return entities; + } + +} diff --git a/src/com/massivecraft/factions/zcore/persist/SaveTask.java b/src/com/massivecraft/factions/zcore/persist/SaveTask.java new file mode 100644 index 00000000..effb4424 --- /dev/null +++ b/src/com/massivecraft/factions/zcore/persist/SaveTask.java @@ -0,0 +1,9 @@ +package com.massivecraft.factions.zcore.persist; + +public class SaveTask implements Runnable +{ + public void run() + { + EM.saveAllToDisc(); + } +} diff --git a/src/com/massivecraft/factions/zcore/util/ClassLoadHack.java b/src/com/massivecraft/factions/zcore/util/ClassLoadHack.java new file mode 100644 index 00000000..d478cc5f --- /dev/null +++ b/src/com/massivecraft/factions/zcore/util/ClassLoadHack.java @@ -0,0 +1,51 @@ +package com.massivecraft.factions.zcore.util; + +import java.io.File; +import java.lang.reflect.Method; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; + +public class ClassLoadHack { + + private static URLClassLoader sysloader = (URLClassLoader)ClassLoader.getSystemClassLoader(); + + public static boolean load(String filename) + { + return load(new File(filename)); + } + + public static boolean load(File file) + { + try + { + return load(file.toURI().toURL()); + } + catch (MalformedURLException e) + { + return false; + } + } + + public static boolean load(URL url) + { + // If the file already is loaded we can skip it + for (URL otherUrl : sysloader.getURLs()) + { + if (otherUrl.sameFile(url)) return true; + } + + try + { + Method addURLMethod = URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{ URL.class }); + addURLMethod.setAccessible(true); + addURLMethod.invoke(sysloader, new Object[]{ url }); + return true; + } + catch (Exception e) + { + return false; + } + } + +} \ No newline at end of file diff --git a/src/com/massivecraft/factions/zcore/util/DiscUtil.java b/src/com/massivecraft/factions/zcore/util/DiscUtil.java new file mode 100644 index 00000000..36230408 --- /dev/null +++ b/src/com/massivecraft/factions/zcore/util/DiscUtil.java @@ -0,0 +1,78 @@ +package com.massivecraft.factions.zcore.util; + +import java.io.*; +import java.net.URL; +import java.nio.channels.Channels; +import java.nio.channels.ReadableByteChannel; + +public class DiscUtil +{ + public static void write(File file, String content) throws IOException + { + BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, false), "UTF8")); + out.write(content); + out.close(); + } + + public static String read(File file) throws IOException + { + BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8")); + String ret = new String(new byte[0], "UTF-8"); + + String line; + while ((line = in.readLine()) != null) + { + ret += line; + } + + in.close(); + return ret; + } + + public static boolean writeCatch(File file, String content) + { + try + { + write(file, content); + return true; + } + catch (Exception e) + { + return false; + } + } + + public static String readCatch(File file) + { + try + { + return read(file); + } + catch (IOException e) + { + return null; + } + } + + public static boolean downloadUrl(String urlstring, File file) + { + try + { + URL url = new URL(urlstring); + ReadableByteChannel rbc = Channels.newChannel(url.openStream()); + FileOutputStream fos = new FileOutputStream(file); + fos.getChannel().transferFrom(rbc, 0, 1 << 24); + return true; + } + catch (Exception e) + { + e.printStackTrace(); + return false; + } + } + + public static boolean downloadUrl(String urlstring, String filename) + { + return downloadUrl(urlstring, new File(filename)); + } +} diff --git a/src/com/massivecraft/factions/zcore/util/LibLoader.java b/src/com/massivecraft/factions/zcore/util/LibLoader.java new file mode 100644 index 00000000..63bafc77 --- /dev/null +++ b/src/com/massivecraft/factions/zcore/util/LibLoader.java @@ -0,0 +1,50 @@ +package com.massivecraft.factions.zcore.util; + +import java.io.File; + +import com.massivecraft.factions.zcore.MPlugin; + +public class LibLoader +{ + MPlugin p; + public LibLoader(MPlugin p) + { + this.p = p; + new File("./lib").mkdirs(); + } + + public boolean require(String filename, String url) + { + if ( ! include(filename, url)) + { + p.log("Failed to load the required library "+filename); + p.suicide(); + return false; + } + return true; + } + + public boolean include (String filename, String url) + { + File file = getFile(filename); + if ( ! file.exists()) + { + p.log("Downloading library "+filename); + if ( ! DiscUtil.downloadUrl(url, file)) + { + p.log("Failed to download "+filename); + return false; + } + } + + return ClassLoadHack.load(file); + } + + private static File getFile(String filename) + { + return new File("./lib/"+filename); + } +} + + + diff --git a/src/com/massivecraft/factions/zcore/util/PermUtil.java b/src/com/massivecraft/factions/zcore/util/PermUtil.java new file mode 100644 index 00000000..b5e0fe5f --- /dev/null +++ b/src/com/massivecraft/factions/zcore/util/PermUtil.java @@ -0,0 +1,128 @@ +package com.massivecraft.factions.zcore.util; + +import java.util.*; +import java.util.Map.Entry; + +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.permissions.Permission; +import org.bukkit.plugin.Plugin; + +import ru.tehkode.permissions.PermissionManager; +import ru.tehkode.permissions.bukkit.PermissionsEx; + +import com.massivecraft.factions.zcore.MPlugin; +import com.nijiko.permissions.PermissionHandler; +import com.nijikokun.bukkit.Permissions.Permissions; + + +public class PermUtil { + + public PermissionManager pex = null; + public PermissionHandler perm2or3 = null; + public Map permissionDescriptions = new HashMap(); + + protected MPlugin p; + + public PermUtil(MPlugin p) + { + this.p = p; + this.setup(); + } + + public String getForbiddenMessage(String perm) + { + return p.txt.get("perm.forbidden", getPermissionDescription(perm)); + } + + /** + * This method hooks into all permission plugins we are supporting + */ + public void setup() + { + for(Permission permission : p.getDescription().getPermissions()) + { + this.permissionDescriptions.put(permission.getName(), permission.getDescription()); + } + + if ( Bukkit.getServer().getPluginManager().isPluginEnabled("PermissionsEx")) + { + pex = PermissionsEx.getPermissionManager(); + p.log("Will use this plugin for permissions: " + Bukkit.getServer().getPluginManager().getPlugin("PermissionsEx").getDescription().getFullName()); + return; + } + + if ( Bukkit.getServer().getPluginManager().isPluginEnabled("Permissions")) + { + Plugin permissionsPlugin = Bukkit.getServer().getPluginManager().getPlugin("Permissions"); + perm2or3 = ((Permissions) permissionsPlugin).getHandler(); + p.log("Will use this plugin for permissions: " + permissionsPlugin.getDescription().getFullName()); + return; + } + + p.log("No permission plugin detected. Defaulting to native bukkit permissions."); + } + + public String getPermissionDescription (String perm) + { + String desc = permissionDescriptions.get(perm); + if (desc == null) + { + return p.txt.get("perm.dothat"); + } + return desc; + } + + /** + * This method tests if me has a certain permission and returns + * true if me has. Otherwise false + */ + public boolean has (CommandSender me, String perm) + { + if ( ! (me instanceof Player)) + { + return me.hasPermission(perm); + } + + if (pex != null) + { + return pex.has((Player)me, perm); + } + + if (perm2or3 != null) + { + return perm2or3.has((Player)me, perm); + } + + return me.hasPermission(perm); + } + + public boolean has (CommandSender me, String perm, boolean informSenderIfNot) + { + if (has(me, perm)) + { + return true; + } + else if (informSenderIfNot) + { + me.sendMessage(this.getForbiddenMessage(perm)); + } + return false; + } + + public T pickFirstVal(CommandSender me, Map perm2val) + { + if (perm2val == null) return null; + T ret = null; + + for ( Entry entry : perm2val.entrySet()) + { + ret = entry.getValue(); + if (has(me, entry.getKey())) break; + } + + return ret; + } + +} diff --git a/src/com/massivecraft/factions/zcore/util/Persist.java b/src/com/massivecraft/factions/zcore/util/Persist.java new file mode 100644 index 00000000..72ce45f8 --- /dev/null +++ b/src/com/massivecraft/factions/zcore/util/Persist.java @@ -0,0 +1,154 @@ +package com.massivecraft.factions.zcore.util; + +import java.io.File; +import java.lang.reflect.Type; +import java.util.logging.Level; + +import com.massivecraft.factions.zcore.MPlugin; + +// TODO: Give better name and place to differenciate from the entity-orm-ish system in "com.massivecraft.core.persist". + +public class Persist { + + private MPlugin p; + public Persist(MPlugin p) + { + this.p = p; + } + + // ------------------------------------------------------------ // + // GET NAME - What should we call this type of object? + // ------------------------------------------------------------ // + + public static String getName(Class clazz) + { + return clazz.getSimpleName().toLowerCase(); + } + + public static String getName(Object o) + { + return getName(o.getClass()); + } + + public static String getName(Type type) + { + return getName(type.getClass()); + } + + // ------------------------------------------------------------ // + // GET FILE - In which file would we like to store this object? + // ------------------------------------------------------------ // + + public File getFile(String name) + { + return new File(p.getDataFolder(), name+".json"); + } + + public File getFile(Class clazz) + { + return getFile(getName(clazz)); + } + + public File getFile(Object obj) + { + return getFile(getName(obj)); + } + + public File getFile(Type type) + { + return getFile(getName(type)); + } + + + // NICE WRAPPERS + + public T loadOrSaveDefault(T def, Class clazz) + { + return loadOrSaveDefault(def, clazz, getFile(clazz)); + } + + public T loadOrSaveDefault(T def, Class clazz, String name) + { + return loadOrSaveDefault(def, clazz, getFile(name)); + } + + public T loadOrSaveDefault(T def, Class clazz, File file) + { + if ( ! file.exists()) + { + p.log("Creating default: "+file); + this.save(def, file); + return def; + } + + T loaded = this.load(clazz, file); + + if (loaded == null) + { + p.log(Level.WARNING, "Using default as I failed to load: "+file); + return def; + } + + return loaded; + } + + // SAVE + + public boolean save(Object instance) + { + return save(instance, getFile(instance)); + } + + public boolean save(Object instance, String name) + { + return save(instance, getFile(name)); + } + + public boolean save(Object instance, File file) + { + return DiscUtil.writeCatch(file, p.gson.toJson(instance)); + } + + // LOAD BY CLASS + + public T load(Class clazz) + { + return load(clazz, getFile(clazz)); + } + + public T load(Class clazz, String name) + { + return load(clazz, getFile(name)); + } + + public T load(Class clazz, File file) + { + String content = DiscUtil.readCatch(file); + if (content == null) + { + return null; + } + + T instance = p.gson.fromJson(content, clazz); + + return instance; + } + + + // LOAD BY TYPE + public T load(Type typeOfT, String name) + { + return load(typeOfT, getFile(name)); + } + + public T load(Type typeOfT, File file) + { + String content = DiscUtil.readCatch(file); + if (content == null) { + return null; + } + + return p.gson.fromJson(content, typeOfT); + } + +} diff --git a/src/com/massivecraft/factions/zcore/util/TextUtil.java b/src/com/massivecraft/factions/zcore/util/TextUtil.java new file mode 100644 index 00000000..6707ec84 --- /dev/null +++ b/src/com/massivecraft/factions/zcore/util/TextUtil.java @@ -0,0 +1,245 @@ +package com.massivecraft.factions.zcore.util; + +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.bukkit.ChatColor; +import org.bukkit.Material; + +public class TextUtil +{ + private Map tags = new HashMap(); + private Map lang = new HashMap(); + + public TextUtil(Map tags, Map lang) + { + if (tags != null) + { + this.tags.putAll(tags); + } + + if (lang != null) + { + this.lang.putAll(lang); + } + } + + // Get is supposed to be the way we reach registered lang + // TODO: Is the parse + public String get(String name) + { + String str = lang.get(name); + if (str == null) str = name; + + return this.parse(str); + } + + public String get(String name, Object... args) + { + String str = lang.get(name); + if (str == null) str = name; + + return this.parse(str, args); + } + + // Parse is used to handle non registered text + public String parse(String str, Object... args) + { + return String.format(this.tags(str), args); + } + + public String parse(String str) + { + return this.tags(str); + } + + public Map getTags() + { + return tags; + } + + public Map getLang() + { + return lang; + } + + public String tags(String str) + { + return replaceTags(str, this.tags); + } + + public static final transient Pattern patternTag = Pattern.compile("<([^<>]*)>"); + public static String replaceTags(String str, Map tags) + { + StringBuffer ret = new StringBuffer(); + Matcher matcher = patternTag.matcher(str); + while (matcher.find()) + { + String tag = matcher.group(1); + String repl = tags.get(tag); + if (repl == null) + { + matcher.appendReplacement(ret, "<"+tag+">"); + } + else + { + matcher.appendReplacement(ret, repl); + } + } + matcher.appendTail(ret); + return ret.toString(); + } + + public static String implode(List list, String glue) + { + StringBuilder ret = new StringBuilder(); + for (int i=0; i") + str + tags("")+ " ]."; + int centerlen = ChatColor.stripColor(center).length(); + int pivot = titleizeLine.length() / 2; + int eatLeft = (centerlen / 2) - titleizeBalance; + int eatRight = (centerlen - eatLeft) + titleizeBalance; + + if (eatLeft < pivot) + return tags("")+titleizeLine.substring(0, pivot - eatLeft) + center + titleizeLine.substring(pivot + eatRight); + else + return tags("")+center; + } + + public ArrayList getPage(List lines, int pageHumanBased, String title) + { + ArrayList ret = new ArrayList(); + int pageZeroBased = pageHumanBased - 1; + int pageheight = 9; + int pagecount = (lines.size() / pageheight)+1; + + ret.add(this.titleize(title+" "+pageHumanBased+"/"+pagecount)); + + if (pagecount == 0) + { + ret.add(this.tags("Sorry. No Pages available.")); + return ret; + } + else if (pageZeroBased < 0 || pageHumanBased > pagecount) + { + ret.add(this.tags("Invalid page. Must be between 1 and "+pagecount)); + return ret; + } + + int from = pageZeroBased * pageheight; + int to = from+pageheight; + if (to > lines.size()) + { + to = lines.size(); + } + + ret.addAll(lines.subList(from, to)); + + return ret; + } + + /** + * Using this function you transform a delta in milliseconds + * to a String like "2 weeks from now" or "7 days ago". + */ + public static final long millisPerSecond = 1000; + public static final long millisPerMinute = 60 * millisPerSecond; + public static final long millisPerHour = 60 * millisPerMinute; + public static final long millisPerDay = 24 * millisPerHour; + public static final long millisPerWeek = 7 * millisPerDay; + public static final long millisPerMonth = 31 * millisPerDay; + public static final long millisPerYear = 365 * millisPerDay; + public static String getTimeDeltaDescriptionRelNow(long millis) + { + double absmillis = (double) Math.abs(millis); + String agofromnow = "from now"; + String unit; + long num; + if (millis <= 0) + { + agofromnow = "ago"; + } + + // We use a factor 3 below for a reason... why do you think? + // Answer: it is a way to make our round of error smaller. + if (absmillis < 3 * millisPerSecond) + { + unit = "milliseconds"; + num = (long) (absmillis); + } + else if (absmillis < 3 * millisPerMinute) + { + unit = "seconds"; + num = (long) (absmillis / millisPerSecond); + } + else if (absmillis < 3 * millisPerHour) + { + unit = "minutes"; + num = (long) (absmillis / millisPerMinute); + } + else if (absmillis < 3 * millisPerDay) + { + unit = "hours"; + num = (long) (absmillis / millisPerHour); + } + else if (absmillis < 3 * millisPerWeek) + { + unit = "days"; + num = (long) (absmillis / millisPerDay); + } + else if (absmillis < 3 * millisPerMonth) + { + unit = "weeks"; + num = (long) (absmillis / millisPerWeek); + } + else if (absmillis < 3 * millisPerYear) + { + unit = "months"; + num = (long) (absmillis / millisPerMonth); + } + else + { + unit = "years"; + num = (long) (absmillis / millisPerYear); + } + + return ""+num+" "+unit+" "+agofromnow; + } +} diff --git a/src/com/massivecraft/factions/zcore/util/WorldUtil.java b/src/com/massivecraft/factions/zcore/util/WorldUtil.java new file mode 100644 index 00000000..4e451cde --- /dev/null +++ b/src/com/massivecraft/factions/zcore/util/WorldUtil.java @@ -0,0 +1,39 @@ +package com.massivecraft.factions.zcore.util; + +import java.io.File; + +import org.bukkit.Bukkit; + +public class WorldUtil +{ + // Previously We had crappy support for multiworld management. + // This should however be handled by an external plugin! + /*public static boolean load(String name) { + if (isWorldLoaded(name)) { + return true; + } + + if ( ! doesWorldExist(name)) { + return false; + } + + Environment env = WorldEnv.get(name); + if (env == null) { + P.log(Level.WARNING, "Failed to load world. Environment was unknown."); + return false; + } + + P.p.getServer().createWorld(name, env); + return true; + }*/ + + public static boolean isWorldLoaded(String name) + { + return Bukkit.getServer().getWorld(name) != null; + } + + public static boolean doesWorldExist(String name) + { + return new File(name, "level.dat").exists(); + } +} diff --git a/src/plugin.yml b/src/plugin.yml index 3060ead1..fcf062cc 100644 --- a/src/plugin.yml +++ b/src/plugin.yml @@ -1,7 +1,8 @@ name: Factions -version: 1.5.1_dev -main: com.massivecraft.factions.Factions +version: 1.6.0_dev +main: com.massivecraft.factions.P softdepend: + - PermissionsEx - Permissions - Essentials - EssentialsChat @@ -16,10 +17,6 @@ softdepend: - Spout - WorldEdit - WorldGuard -commands: - f: - description: All of the Factions commands - usage: See documentation. permissions: factions.*: description: Grants all Factions permissions