From 815a9dcca22b108c158fcf41035edac2ff4b7178 Mon Sep 17 00:00:00 2001 From: ElgarL Date: Wed, 14 Dec 2011 16:24:47 +0000 Subject: [PATCH 01/39] Removed op permissions from admins in the default GloblaGroups.yml. --- EssentialsGroupManager/src/Changelog.txt | 3 ++- EssentialsGroupManager/src/globalgroups.yml | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/EssentialsGroupManager/src/Changelog.txt b/EssentialsGroupManager/src/Changelog.txt index 47a0f6b79..1f5f0313d 100644 --- a/EssentialsGroupManager/src/Changelog.txt +++ b/EssentialsGroupManager/src/Changelog.txt @@ -85,4 +85,5 @@ v 1.7: - Offline player checks now support partial name matches. - Added custom events so plugins can now be notified of changes within GroupManager. - GM now registers with Bukkits ServicesManager. - - deleting the contents of GlobalGroups.yml will no longer thrown a NullPointerException. \ No newline at end of file + - deleting the contents of GlobalGroups.yml will no longer thrown a NullPointerException. + - Removed op permissions from admins in the default GloblaGroups.yml. \ No newline at end of file diff --git a/EssentialsGroupManager/src/globalgroups.yml b/EssentialsGroupManager/src/globalgroups.yml index 9662baf43..066f4faea 100644 --- a/EssentialsGroupManager/src/globalgroups.yml +++ b/EssentialsGroupManager/src/globalgroups.yml @@ -150,9 +150,9 @@ groups: - bukkit.command.kill - bukkit.command.list - bukkit.command.me - - bukkit.command.op - - bukkit.command.op.give - - bukkit.command.op.take + - -bukkit.command.op + - -bukkit.command.op.give + - -bukkit.command.op.take - bukkit.command.plugins - bukkit.command.reload - bukkit.command.save From ac77bbb0b4f405598b8fc8271d520818d0646da9 Mon Sep 17 00:00:00 2001 From: ElgarL Date: Sun, 18 Dec 2011 04:25:54 +0000 Subject: [PATCH 02/39] Changed ServicesManager registration to lowest from normal. --- EssentialsGroupManager/src/Changelog.txt | 3 ++- .../src/org/anjocaido/groupmanager/GroupManager.java | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/EssentialsGroupManager/src/Changelog.txt b/EssentialsGroupManager/src/Changelog.txt index 1f5f0313d..76782e99a 100644 --- a/EssentialsGroupManager/src/Changelog.txt +++ b/EssentialsGroupManager/src/Changelog.txt @@ -86,4 +86,5 @@ v 1.7: - Added custom events so plugins can now be notified of changes within GroupManager. - GM now registers with Bukkits ServicesManager. - deleting the contents of GlobalGroups.yml will no longer thrown a NullPointerException. - - Removed op permissions from admins in the default GloblaGroups.yml. \ No newline at end of file + - Removed op permissions from admins in the default GloblaGroups.yml. + - Changed ServicesManager registration to lowest from normal. \ No newline at end of file diff --git a/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java b/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java index 2d46e74c8..c51efb8be 100644 --- a/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java +++ b/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java @@ -161,7 +161,7 @@ public class GroupManager extends JavaPlugin { System.out.println(pdfFile.getName() + " version " + pdfFile.getVersion() + " is enabled!"); // Register as a service - this.getServer().getServicesManager().register(AnjoPermissionsHandler.class, this.permissionHandler, this, ServicePriority.Normal); + this.getServer().getServicesManager().register(AnjoPermissionsHandler.class, this.permissionHandler, this, ServicePriority.Lowest); } public static boolean isLoaded() { From de40f7f556513d4997779f0c5cc1d77fe050a02a Mon Sep 17 00:00:00 2001 From: ElgarL Date: Sun, 18 Dec 2011 14:26:00 +0000 Subject: [PATCH 03/39] Fixed 'manucheckp' returning a null for the searched node when it's a group/subgroup. --- EssentialsGroupManager/src/Changelog.txt | 4 +++- .../groupmanager/permissions/AnjoPermissionsHandler.java | 3 +++ EssentialsGroupManager/src/plugin.yml | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/EssentialsGroupManager/src/Changelog.txt b/EssentialsGroupManager/src/Changelog.txt index 76782e99a..69f921bb3 100644 --- a/EssentialsGroupManager/src/Changelog.txt +++ b/EssentialsGroupManager/src/Changelog.txt @@ -87,4 +87,6 @@ v 1.7: - GM now registers with Bukkits ServicesManager. - deleting the contents of GlobalGroups.yml will no longer thrown a NullPointerException. - Removed op permissions from admins in the default GloblaGroups.yml. - - Changed ServicesManager registration to lowest from normal. \ No newline at end of file +v 1.8: + - Changed ServicesManager registration to lowest from normal. + - Fixed 'manucheckp' returning a null for the searched node when it's a group/subgroup. \ No newline at end of file diff --git a/EssentialsGroupManager/src/org/anjocaido/groupmanager/permissions/AnjoPermissionsHandler.java b/EssentialsGroupManager/src/org/anjocaido/groupmanager/permissions/AnjoPermissionsHandler.java index 6e18bf3f8..9b7253c41 100644 --- a/EssentialsGroupManager/src/org/anjocaido/groupmanager/permissions/AnjoPermissionsHandler.java +++ b/EssentialsGroupManager/src/org/anjocaido/groupmanager/permissions/AnjoPermissionsHandler.java @@ -711,12 +711,14 @@ public class AnjoPermissionsHandler extends PermissionsReaderInterface { PermissionCheckResult resultUser = checkUserOnlyPermission(user, targetPermission); if (resultUser.resultType != PermissionCheckResult.Type.NOTFOUND) { + resultUser.accessLevel = targetPermission; return resultUser; } // IT ONLY CHECKS GROUPS PERMISSIONS IF RESULT FOR USER IS NOT FOUND PermissionCheckResult resultGroup = checkGroupPermissionWithInheritance(user.getGroup(), targetPermission); if (resultGroup.resultType != PermissionCheckResult.Type.NOTFOUND) { + result.accessLevel = targetPermission; return resultGroup; } @@ -724,6 +726,7 @@ public class AnjoPermissionsHandler extends PermissionsReaderInterface { for (Group subGroup : user.subGroupListCopy()) { PermissionCheckResult resultSubGroup = checkGroupPermissionWithInheritance(subGroup, targetPermission); if (resultSubGroup.resultType != PermissionCheckResult.Type.NOTFOUND) { + resultSubGroup.accessLevel = targetPermission; return resultSubGroup; } } diff --git a/EssentialsGroupManager/src/plugin.yml b/EssentialsGroupManager/src/plugin.yml index 115e92ba2..da5164ce1 100644 --- a/EssentialsGroupManager/src/plugin.yml +++ b/EssentialsGroupManager/src/plugin.yml @@ -1,5 +1,5 @@ name: GroupManager -version: "1.7 (Phoenix)" +version: "1.8 (Phoenix)" main: org.anjocaido.groupmanager.GroupManager website: http://www.anjocaido.info/ description: Provides on-the-fly system for permissions system created by Nijikokun. But all in memory, and with flat-file saving schedule. From 244673e1fb8a8583c95df765e7b64f18c2c631c3 Mon Sep 17 00:00:00 2001 From: ElgarL Date: Sun, 18 Dec 2011 14:35:14 +0000 Subject: [PATCH 04/39] Fixed manucheckp on group (missed it in last commit) --- .../groupmanager/permissions/AnjoPermissionsHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EssentialsGroupManager/src/org/anjocaido/groupmanager/permissions/AnjoPermissionsHandler.java b/EssentialsGroupManager/src/org/anjocaido/groupmanager/permissions/AnjoPermissionsHandler.java index 9b7253c41..b23fc01d4 100644 --- a/EssentialsGroupManager/src/org/anjocaido/groupmanager/permissions/AnjoPermissionsHandler.java +++ b/EssentialsGroupManager/src/org/anjocaido/groupmanager/permissions/AnjoPermissionsHandler.java @@ -718,7 +718,7 @@ public class AnjoPermissionsHandler extends PermissionsReaderInterface { // IT ONLY CHECKS GROUPS PERMISSIONS IF RESULT FOR USER IS NOT FOUND PermissionCheckResult resultGroup = checkGroupPermissionWithInheritance(user.getGroup(), targetPermission); if (resultGroup.resultType != PermissionCheckResult.Type.NOTFOUND) { - result.accessLevel = targetPermission; + resultGroup.accessLevel = targetPermission; return resultGroup; } From afe8ecd3dfd26d49c1133e330c942c90e7826e1f Mon Sep 17 00:00:00 2001 From: ElgarL Date: Sun, 18 Dec 2011 15:21:56 +0000 Subject: [PATCH 05/39] Fixed a typo --- .../src/org/anjocaido/groupmanager/GroupManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java b/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java index c51efb8be..062d3df21 100644 --- a/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java +++ b/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java @@ -805,7 +805,7 @@ public class GroupManager extends JavaPlugin { sender.sendMessage(ChatColor.YELLOW + "Permission Node: " + permissionResult.accessLevel); } else if (permissionResult.owner instanceof Group) { if (permissionResult.resultType.equals(PermissionCheckResult.Type.NEGATION)) { - sender.sendMessage(ChatColor.RED + "The user inherits the a negation permission from group: " + permissionResult.owner.getName()); + sender.sendMessage(ChatColor.RED + "The user inherits a negation permission from group: " + permissionResult.owner.getName()); } else { sender.sendMessage(ChatColor.YELLOW + "The user inherits the permission from group: " + permissionResult.owner.getName()); } From 7c8c40c7903852e3ee2d5740729f2e94b5982240 Mon Sep 17 00:00:00 2001 From: ElgarL Date: Mon, 19 Dec 2011 15:44:27 +0000 Subject: [PATCH 06/39] manpromote and mandemote now correctly sent the notification to the console if the command was issued there. --- EssentialsGroupManager/src/Changelog.txt | 3 ++- .../src/org/anjocaido/groupmanager/GroupManager.java | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/EssentialsGroupManager/src/Changelog.txt b/EssentialsGroupManager/src/Changelog.txt index 69f921bb3..4b85fa94f 100644 --- a/EssentialsGroupManager/src/Changelog.txt +++ b/EssentialsGroupManager/src/Changelog.txt @@ -89,4 +89,5 @@ v 1.7: - Removed op permissions from admins in the default GloblaGroups.yml. v 1.8: - Changed ServicesManager registration to lowest from normal. - - Fixed 'manucheckp' returning a null for the searched node when it's a group/subgroup. \ No newline at end of file + - Fixed 'manucheckp' returning a null for the searched node when it's a group/subgroup. + - manpromote and mandemote now correctly send the notification to the console if the command was issued there. \ No newline at end of file diff --git a/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java b/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java index 062d3df21..9762a35a7 100644 --- a/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java +++ b/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java @@ -415,7 +415,7 @@ public class GroupManager extends JavaPlugin { // PARECE OK auxUser.setGroup(auxGroup); - if (!sender.hasPermission("groupmanager.notify.other")) + if (!sender.hasPermission("groupmanager.notify.other") || (isConsole)) sender.sendMessage(ChatColor.YELLOW + "You changed player '" + auxUser.getName() + "' group to '" + auxGroup.getName() + "'."); targetPlayer = this.getServer().getPlayer(auxUser.getName()); @@ -1610,7 +1610,7 @@ public class GroupManager extends JavaPlugin { } // PARECE OK auxUser.setGroup(auxGroup); - if (!sender.hasPermission("groupmanager.notify.other")) + if (!sender.hasPermission("groupmanager.notify.other") || (isConsole)) sender.sendMessage(ChatColor.YELLOW + "You changed " + auxUser.getName() + " group to " + auxGroup.getName() + "."); targetPlayer = this.getServer().getPlayer(auxUser.getName()); @@ -1666,7 +1666,7 @@ public class GroupManager extends JavaPlugin { } // PARECE OK auxUser.setGroup(auxGroup); - if (!sender.hasPermission("groupmanager.notify.other")) + if (!sender.hasPermission("groupmanager.notify.other") || (isConsole)) sender.sendMessage(ChatColor.YELLOW + "You changed " + auxUser.getName() + " group to " + auxGroup.getName() + "."); targetPlayer = this.getServer().getPlayer(auxUser.getName()); From a2202439bb1aeddaaad08c3b6607ce11b98356f3 Mon Sep 17 00:00:00 2001 From: ElgarL Date: Mon, 19 Dec 2011 19:22:04 +0000 Subject: [PATCH 07/39] Expanded GlobalGroups.yml and Groups.yml to include Towny permissions. --- EssentialsGroupManager/src/Changelog.txt | 3 +- EssentialsGroupManager/src/globalgroups.yml | 57 +++++++++++++++++++-- EssentialsGroupManager/src/groups.yml | 3 ++ 3 files changed, 58 insertions(+), 5 deletions(-) diff --git a/EssentialsGroupManager/src/Changelog.txt b/EssentialsGroupManager/src/Changelog.txt index 4b85fa94f..6611c3e1b 100644 --- a/EssentialsGroupManager/src/Changelog.txt +++ b/EssentialsGroupManager/src/Changelog.txt @@ -90,4 +90,5 @@ v 1.7: v 1.8: - Changed ServicesManager registration to lowest from normal. - Fixed 'manucheckp' returning a null for the searched node when it's a group/subgroup. - - manpromote and mandemote now correctly send the notification to the console if the command was issued there. \ No newline at end of file + - manpromote and mandemote now correctly send the notification to the console if the command was issued there. + - Expanded GlobalGroups.yml and Groups.yml to include Towny permissions. \ No newline at end of file diff --git a/EssentialsGroupManager/src/globalgroups.yml b/EssentialsGroupManager/src/globalgroups.yml index 066f4faea..fe7b7df99 100644 --- a/EssentialsGroupManager/src/globalgroups.yml +++ b/EssentialsGroupManager/src/globalgroups.yml @@ -1,9 +1,5 @@ groups: - g:bukkit_default: - permissions: - - bukkit.broadcast.user - g:essentials_default: permissions: - essentials.help @@ -129,6 +125,10 @@ groups: - groupmanager.manucheckp - groupmanager.manulistp + g:bukkit_default: + permissions: + - bukkit.broadcast.user + g:bukkit_moderator: permissions: - bukkit.command.ban @@ -174,3 +174,52 @@ groups: - bukkit.command.whitelist.list - bukkit.command.whitelist.reload - bukkit.command.whitelist.remove + + g:towny_builder: + permissions: + - towny.town.* + - towny.nation.* + - towny.chat.tc + - towny.chat.nc + - towny.wild.block.6.* + - towny.wild.block.14.destroy + - towny.wild.block.15.destroy + - towny.wild.block.16.destroy + - towny.wild.block.17.* + - towny.wild.block.18.destroy + - towny.wild.block.21.destroy + - towny.wild.block.31.destroy + - towny.wild.block.37.destroy + - towny.wild.block.38.destroy + - towny.wild.block.39.destroy + - towny.wild.block.40.destroy + - towny.wild.block.50.destroy + - towny.wild.block.56.destroy + - towny.wild.block.73.destroy + - towny.wild.block.74.destroy + - towny.wild.block.78.destroy + - towny.wild.block.81.destroy + - towny.wild.block.82.destroy + - towny.wild.block.83.destroy + - towny.wild.block.86.destroy + - towny.wild.block.103.destroy + - towny.wild.block.106.destroy + - towny.wild.block.111.destroy + - towny.wild.block.115.destroy + + g:towny_moderator: + permissions: + - towny.chat.mod + - towny.wild.block.64.switch + - towny.wild.block.83.build + - towny.wild.block.86.build + - towny.wild.block.103.build + - towny.wild.block.111.build + - towny.wild.block.115.build + + g:towny_admin: + permissions: + - towny.admin + - -towny.wild.block.119.destroy + - -towny.wild.block.120.destroy + - towny.chat.admin \ No newline at end of file diff --git a/EssentialsGroupManager/src/groups.yml b/EssentialsGroupManager/src/groups.yml index 81fb4f030..e50054c9f 100644 --- a/EssentialsGroupManager/src/groups.yml +++ b/EssentialsGroupManager/src/groups.yml @@ -24,6 +24,7 @@ groups: inheritance: - default - g:essentials_builder + - g:towny_moderator info: prefix: '&2' build: true @@ -35,6 +36,7 @@ groups: - builder - g:essentials_moderator - g:bukkit_moderator + - g:towny_moderator info: prefix: '&5' build: true @@ -46,6 +48,7 @@ groups: - moderator - g:essentials_admin - g:bukkit_admin + - g:towny_admin info: prefix: '&c' build: true From debcf4714a3db9065219e3701137e8dea6439970 Mon Sep 17 00:00:00 2001 From: ElgarL Date: Tue, 20 Dec 2011 17:21:55 +0000 Subject: [PATCH 08/39] Delayed GroupManager events so Superperms will be fully updated before plugins receive the events. --- EssentialsGroupManager/src/Changelog.txt | 3 ++- .../org/anjocaido/groupmanager/GroupManager.java | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/EssentialsGroupManager/src/Changelog.txt b/EssentialsGroupManager/src/Changelog.txt index 6611c3e1b..dc661f7c3 100644 --- a/EssentialsGroupManager/src/Changelog.txt +++ b/EssentialsGroupManager/src/Changelog.txt @@ -91,4 +91,5 @@ v 1.8: - Changed ServicesManager registration to lowest from normal. - Fixed 'manucheckp' returning a null for the searched node when it's a group/subgroup. - manpromote and mandemote now correctly send the notification to the console if the command was issued there. - - Expanded GlobalGroups.yml and Groups.yml to include Towny permissions. \ No newline at end of file + - Expanded GlobalGroups.yml and Groups.yml to include Towny permissions. + - Delayed GroupManager events so Superperms will be fully updated before plugins receive the events. \ No newline at end of file diff --git a/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java b/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java index 9762a35a7..5ced1fb47 100644 --- a/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java +++ b/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java @@ -1840,11 +1840,21 @@ public class GroupManager extends JavaPlugin { /** * Triggers all GroupManager events for other plugins to see. + * Schedules events for 1 tick later to allow GM to finish populating super perms. * * @param event */ - public static void callEvent(GroupManagerEvent event) { - Bukkit.getServer().getPluginManager().callEvent(event); + public static void callEvent(final GroupManagerEvent event) { + + if (Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Bukkit.getPluginManager().getPlugin("GroupManager"), new Runnable() { + + @Override + public void run() { + Bukkit.getServer().getPluginManager().callEvent(event); + } + }) == -1) + GroupManager.logger.warning("Could not schedule GM Event."); + } /** From 69847af08aa1ebc91c7808310bb7c7c1183b137f Mon Sep 17 00:00:00 2001 From: ElgarL Date: Thu, 22 Dec 2011 17:01:10 +0000 Subject: [PATCH 09/39] Decreased ranks of default users so idiots who don't remove them can't get 'hacked' when in offline mode. --- EssentialsGroupManager/src/users.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/EssentialsGroupManager/src/users.yml b/EssentialsGroupManager/src/users.yml index 28fa79910..72a7b652f 100644 --- a/EssentialsGroupManager/src/users.yml +++ b/EssentialsGroupManager/src/users.yml @@ -3,7 +3,7 @@ users: snowleo: subgroups: [] permissions: [] - group: Admin + group: Builder KHobbits: subgroups: [] permissions: [] @@ -11,5 +11,5 @@ users: ElgarL: subgroups: [] permissions: [] - group: Owner + group: Moderator From 3aba996e97bd5bb90e65ed3166f3666d9122a729 Mon Sep 17 00:00:00 2001 From: ElgarL Date: Thu, 29 Dec 2011 14:52:42 +0000 Subject: [PATCH 10/39] Changed the way events are raised to prevent variable corruption. --- EssentialsGroupManager/src/Changelog.txt | 3 ++- .../anjocaido/groupmanager/GroupManager.java | 19 ---------------- .../events/GroupManagerEvent.java | 22 ++++++++++++++++++- .../events/GroupManagerEventHandler.java | 8 +++---- 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/EssentialsGroupManager/src/Changelog.txt b/EssentialsGroupManager/src/Changelog.txt index dc661f7c3..3929b9d22 100644 --- a/EssentialsGroupManager/src/Changelog.txt +++ b/EssentialsGroupManager/src/Changelog.txt @@ -92,4 +92,5 @@ v 1.8: - Fixed 'manucheckp' returning a null for the searched node when it's a group/subgroup. - manpromote and mandemote now correctly send the notification to the console if the command was issued there. - Expanded GlobalGroups.yml and Groups.yml to include Towny permissions. - - Delayed GroupManager events so Superperms will be fully updated before plugins receive the events. \ No newline at end of file + - Delayed GroupManager events so Superperms will be fully updated before plugins receive the events. + - Changed the way events are raised to prevent variable corruption. \ No newline at end of file diff --git a/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java b/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java index 5ced1fb47..c0e9ef2f8 100644 --- a/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java +++ b/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java @@ -1837,25 +1837,6 @@ public class GroupManager extends JavaPlugin { return match; } - - /** - * Triggers all GroupManager events for other plugins to see. - * Schedules events for 1 tick later to allow GM to finish populating super perms. - * - * @param event - */ - public static void callEvent(final GroupManagerEvent event) { - - if (Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Bukkit.getPluginManager().getPlugin("GroupManager"), new Runnable() { - - @Override - public void run() { - Bukkit.getServer().getPluginManager().callEvent(event); - } - }) == -1) - GroupManager.logger.warning("Could not schedule GM Event."); - - } /** * @return the config diff --git a/EssentialsGroupManager/src/org/anjocaido/groupmanager/events/GroupManagerEvent.java b/EssentialsGroupManager/src/org/anjocaido/groupmanager/events/GroupManagerEvent.java index 856abbc28..0834b3ada 100644 --- a/EssentialsGroupManager/src/org/anjocaido/groupmanager/events/GroupManagerEvent.java +++ b/EssentialsGroupManager/src/org/anjocaido/groupmanager/events/GroupManagerEvent.java @@ -1,6 +1,8 @@ package org.anjocaido.groupmanager.events; +import org.anjocaido.groupmanager.GroupManager; +import org.bukkit.Bukkit; import org.bukkit.event.Event; /** @@ -13,10 +15,28 @@ public abstract class GroupManagerEvent extends Event { * */ private static final long serialVersionUID = 8790362185329926951L; - + protected GroupManagerEvent(String name) { super(name); } + /** + * Triggers all GroupManager events for other plugins to see. + * Schedules events for 1 tick later to allow GM to finish populating super perms. + * + * @param event + */ + public void schedule(final GroupManagerEvent event) { + + if (Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Bukkit.getPluginManager().getPlugin("GroupManager"), new Runnable() { + + @Override + public void run() { + Bukkit.getServer().getPluginManager().callEvent(event); + } + }, 1) == -1) + GroupManager.logger.warning("Could not schedule GM Event."); + } + } \ No newline at end of file diff --git a/EssentialsGroupManager/src/org/anjocaido/groupmanager/events/GroupManagerEventHandler.java b/EssentialsGroupManager/src/org/anjocaido/groupmanager/events/GroupManagerEventHandler.java index 83cac393d..3c077b6c6 100644 --- a/EssentialsGroupManager/src/org/anjocaido/groupmanager/events/GroupManagerEventHandler.java +++ b/EssentialsGroupManager/src/org/anjocaido/groupmanager/events/GroupManagerEventHandler.java @@ -1,11 +1,9 @@ package org.anjocaido.groupmanager.events; -import org.anjocaido.groupmanager.GroupManager; import org.anjocaido.groupmanager.data.Group; import org.anjocaido.groupmanager.data.User; - /** * @author ElgarL * @@ -13,13 +11,13 @@ import org.anjocaido.groupmanager.data.User; public class GroupManagerEventHandler { protected static void callEvent(GMGroupEvent event) { - GroupManager.callEvent(event); + event.schedule(event); } protected static void callEvent(GMUserEvent event) { - GroupManager.callEvent(event); + event.schedule(event); } protected static void callEvent(GMSystemEvent event) { - GroupManager.callEvent(event); + event.schedule(event); } public static void callEvent(Group group, GMGroupEvent.Action action) { From 7726fd0081346b59e4c555476f6c62e1aa38b69f Mon Sep 17 00:00:00 2001 From: ElgarL Date: Tue, 3 Jan 2012 02:04:48 +0000 Subject: [PATCH 11/39] Reload GlobalGroups when you perform a world load. Changed GlobalGroups to save/load before local groups in the scheduled data saving/loading --- EssentialsGroupManager/src/Changelog.txt | 4 +++- .../anjocaido/groupmanager/GroupManager.java | 2 ++ .../dataholder/worlds/WorldsHolder.java | 19 ++++++++++--------- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/EssentialsGroupManager/src/Changelog.txt b/EssentialsGroupManager/src/Changelog.txt index 3929b9d22..9b43fddbb 100644 --- a/EssentialsGroupManager/src/Changelog.txt +++ b/EssentialsGroupManager/src/Changelog.txt @@ -93,4 +93,6 @@ v 1.8: - manpromote and mandemote now correctly send the notification to the console if the command was issued there. - Expanded GlobalGroups.yml and Groups.yml to include Towny permissions. - Delayed GroupManager events so Superperms will be fully updated before plugins receive the events. - - Changed the way events are raised to prevent variable corruption. \ No newline at end of file + - Changed the way events are raised to prevent variable corruption. + - Reload GlobalGroups when you perform a world load. + - Changed GlobalGroups to save/load before local groups in the scheduled data saving/loading \ No newline at end of file diff --git a/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java b/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java index c0e9ef2f8..16e0222d8 100644 --- a/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java +++ b/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java @@ -1504,7 +1504,9 @@ public class GroupManager extends JavaPlugin { isLoaded = false; // Disable Bukkit Perms update + globalGroups.load(); worldsHolder.loadWorld(auxString); + sender.sendMessage("The request to world '" + auxString + "' was sent."); isLoaded = true; diff --git a/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/worlds/WorldsHolder.java b/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/worlds/WorldsHolder.java index 25eaf8526..dbdd17f34 100644 --- a/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/worlds/WorldsHolder.java +++ b/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/worlds/WorldsHolder.java @@ -176,6 +176,16 @@ public class WorldsHolder { ArrayList alreadyDone = new ArrayList(); Tasks.removeOldFiles(plugin, plugin.getBackupFolder()); + // Write Global Groups + if (GroupManager.getGlobalGroups().haveGroupsChanged()) { + GroupManager.getGlobalGroups().writeGroups(overwrite); + } else { + if (GroupManager.getGlobalGroups().getTimeStampGroups() < GroupManager.getGlobalGroups().getGlobalGroupsFile().lastModified()) { + System.out.print("Newer GlobalGroups file found (Loading changes)!"); + GroupManager.getGlobalGroups().load(); + } + } + for (OverloadedWorldHolder w : worldsData.values()) { if (alreadyDone.contains(w)) { continue; @@ -228,15 +238,6 @@ public class WorldsHolder { } alreadyDone.add(w); } - // Write Global Groups - if (GroupManager.getGlobalGroups().haveGroupsChanged()) { - GroupManager.getGlobalGroups().writeGroups(overwrite); - } else { - if (GroupManager.getGlobalGroups().getTimeStampGroups() < GroupManager.getGlobalGroups().getGlobalGroupsFile().lastModified()) { - System.out.print("Newer GlobalGroups file found (Loading changes)!"); - GroupManager.getGlobalGroups().load(); - } - } } /** From 4c485f31476825993ba8edac00a71080d51d6a2d Mon Sep 17 00:00:00 2001 From: ElgarL Date: Thu, 5 Jan 2012 19:43:30 +0000 Subject: [PATCH 12/39] Fix 'manucheckp' to correctly report if a permission is available from GroupManager or Bukkit. --- EssentialsGroupManager/src/Changelog.txt | 3 +- .../anjocaido/groupmanager/GroupManager.java | 40 +++++++++---------- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/EssentialsGroupManager/src/Changelog.txt b/EssentialsGroupManager/src/Changelog.txt index 9b43fddbb..8e4996b01 100644 --- a/EssentialsGroupManager/src/Changelog.txt +++ b/EssentialsGroupManager/src/Changelog.txt @@ -95,4 +95,5 @@ v 1.8: - Delayed GroupManager events so Superperms will be fully updated before plugins receive the events. - Changed the way events are raised to prevent variable corruption. - Reload GlobalGroups when you perform a world load. - - Changed GlobalGroups to save/load before local groups in the scheduled data saving/loading \ No newline at end of file + - Changed GlobalGroups to save/load before local groups in the scheduled data saving/loading + - Fix 'manucheckp' to correctly report if a permission is available from GroupManager or Bukkit. \ No newline at end of file diff --git a/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java b/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java index 16e0222d8..4ba2624bf 100644 --- a/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java +++ b/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java @@ -787,29 +787,29 @@ public class GroupManager extends JavaPlugin { } targetPlayer = this.getServer().getPlayer(auxUser.getName()); // VALIDANDO PERMISSAO - permissionResult = permissionHandler.checkFullUserPermission(auxUser, args[1]); + permissionResult = permissionHandler.checkFullGMPermission(auxUser, args[1], false); + if (permissionResult.resultType.equals(PermissionCheckResult.Type.NOTFOUND)) { + //No permissions found in GM so fall through and check Bukkit. sender.sendMessage(ChatColor.RED + "The player doesn't have access to that permission"); - sender.sendMessage(ChatColor.YELLOW + "SuperPerms reports Node: " + targetPlayer.hasPermission(args[1])); - return false; - } - // PARECE OK - // auxString = - // permissionHandler.checkUserOnlyPermission(auxUser, args[1]); - if (permissionResult.owner instanceof User) { - if (permissionResult.resultType.equals(PermissionCheckResult.Type.NEGATION)) { - sender.sendMessage(ChatColor.RED + "The user has directly a negation node for that permission."); - } else { - sender.sendMessage(ChatColor.YELLOW + "The user has directly this permission."); + + } else { + // This permission was found in groupmanager. + if (permissionResult.owner instanceof User) { + if (permissionResult.resultType.equals(PermissionCheckResult.Type.NEGATION)) { + sender.sendMessage(ChatColor.RED + "The user has directly a negation node for that permission."); + } else { + sender.sendMessage(ChatColor.YELLOW + "The user has directly this permission."); + } + sender.sendMessage(ChatColor.YELLOW + "Permission Node: " + permissionResult.accessLevel); + } else if (permissionResult.owner instanceof Group) { + if (permissionResult.resultType.equals(PermissionCheckResult.Type.NEGATION)) { + sender.sendMessage(ChatColor.RED + "The user inherits a negation permission from group: " + permissionResult.owner.getName()); + } else { + sender.sendMessage(ChatColor.YELLOW + "The user inherits the permission from group: " + permissionResult.owner.getName()); + } + sender.sendMessage(ChatColor.YELLOW + "Permission Node: " + permissionResult.accessLevel); } - sender.sendMessage(ChatColor.YELLOW + "Permission Node: " + permissionResult.accessLevel); - } else if (permissionResult.owner instanceof Group) { - if (permissionResult.resultType.equals(PermissionCheckResult.Type.NEGATION)) { - sender.sendMessage(ChatColor.RED + "The user inherits a negation permission from group: " + permissionResult.owner.getName()); - } else { - sender.sendMessage(ChatColor.YELLOW + "The user inherits the permission from group: " + permissionResult.owner.getName()); - } - sender.sendMessage(ChatColor.YELLOW + "Permission Node: " + permissionResult.accessLevel); } // superperms From 58057a771d4e8e971d8531e1ad208eb6bbb0df54 Mon Sep 17 00:00:00 2001 From: ElgarL Date: Mon, 9 Jan 2012 02:00:58 +0000 Subject: [PATCH 13/39] Changed over to a reflection method for populating superperms as Bukkit lags when you handle permissions one at a time. --- EssentialsGroupManager/src/Changelog.txt | 3 +- EssentialsGroupManager/src/config.yml | 4 +- EssentialsGroupManager/src/globalgroups.yml | 1 + .../Tasks/BukkitPermsUpdateTask.java | 2 +- .../permissions/BukkitPermissions.java | 117 ++++++++++++------ 5 files changed, 88 insertions(+), 39 deletions(-) diff --git a/EssentialsGroupManager/src/Changelog.txt b/EssentialsGroupManager/src/Changelog.txt index 8e4996b01..6ac58e8ff 100644 --- a/EssentialsGroupManager/src/Changelog.txt +++ b/EssentialsGroupManager/src/Changelog.txt @@ -96,4 +96,5 @@ v 1.8: - Changed the way events are raised to prevent variable corruption. - Reload GlobalGroups when you perform a world load. - Changed GlobalGroups to save/load before local groups in the scheduled data saving/loading - - Fix 'manucheckp' to correctly report if a permission is available from GroupManager or Bukkit. \ No newline at end of file + - Fix 'manucheckp' to correctly report if a permission is available from GroupManager or Bukkit. + - Changed over to a reflection method for populating superperms as Bukkit lags when you handle permissions one at a time. \ No newline at end of file diff --git a/EssentialsGroupManager/src/config.yml b/EssentialsGroupManager/src/config.yml index 2fe5e509c..17ffc5b6e 100644 --- a/EssentialsGroupManager/src/config.yml +++ b/EssentialsGroupManager/src/config.yml @@ -4,9 +4,9 @@ settings: # The user will be able to promote players to the same group or even above. opOverrides: true - # If enabled any plugins bukkit permissions which default to true will be left enabled. + # If enabled any bukkit permissiosn which default to true will be left enabled. # If the player is op any permissions set to Op will follow suit. - bukkit_perms_override: false + bukkit_perms_override: true # Default setting for 'mantoglevalidate' # true will cause GroupManager to attempt name matching by default. diff --git a/EssentialsGroupManager/src/globalgroups.yml b/EssentialsGroupManager/src/globalgroups.yml index fe7b7df99..c9ecc2673 100644 --- a/EssentialsGroupManager/src/globalgroups.yml +++ b/EssentialsGroupManager/src/globalgroups.yml @@ -128,6 +128,7 @@ groups: g:bukkit_default: permissions: - bukkit.broadcast.user + - -bukkit.command.plugins g:bukkit_moderator: permissions: diff --git a/EssentialsGroupManager/src/org/anjocaido/groupmanager/Tasks/BukkitPermsUpdateTask.java b/EssentialsGroupManager/src/org/anjocaido/groupmanager/Tasks/BukkitPermsUpdateTask.java index f4b805c35..8788fc83b 100644 --- a/EssentialsGroupManager/src/org/anjocaido/groupmanager/Tasks/BukkitPermsUpdateTask.java +++ b/EssentialsGroupManager/src/org/anjocaido/groupmanager/Tasks/BukkitPermsUpdateTask.java @@ -18,7 +18,7 @@ public class BukkitPermsUpdateTask implements Runnable { public void run() { // Signal loaded and update BukkitPermissions. GroupManager.setLoaded(true); - GroupManager.BukkitPermissions.collectPermissions(); + //GroupManager.BukkitPermissions.collectPermissions(); GroupManager.BukkitPermissions.updateAllPlayers(); GroupManager.logger.info("Bukkit Permissions Updated!"); diff --git a/EssentialsGroupManager/src/org/anjocaido/groupmanager/permissions/BukkitPermissions.java b/EssentialsGroupManager/src/org/anjocaido/groupmanager/permissions/BukkitPermissions.java index 202e27e3a..7c157a2f7 100644 --- a/EssentialsGroupManager/src/org/anjocaido/groupmanager/permissions/BukkitPermissions.java +++ b/EssentialsGroupManager/src/org/anjocaido/groupmanager/permissions/BukkitPermissions.java @@ -16,6 +16,7 @@ package org.anjocaido.groupmanager.permissions; +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; @@ -23,9 +24,9 @@ import java.util.List; import java.util.Map; import org.anjocaido.groupmanager.GroupManager; -import org.anjocaido.groupmanager.data.User; +//import org.anjocaido.groupmanager.data.User; import org.anjocaido.groupmanager.dataholder.OverloadedWorldHolder; -import org.anjocaido.groupmanager.utils.PermissionCheckResult; +//import org.anjocaido.groupmanager.utils.PermissionCheckResult; import org.bukkit.Bukkit; import org.bukkit.entity.Player; @@ -43,8 +44,7 @@ import org.bukkit.event.server.ServerListener; import org.bukkit.permissions.Permission; import org.bukkit.permissions.PermissionAttachment; import org.bukkit.permissions.PermissionAttachmentInfo; -import org.bukkit.permissions.PermissionDefault; -import org.bukkit.plugin.Plugin; +//import org.bukkit.permissions.PermissionDefault; import org.bukkit.plugin.PluginManager; @@ -52,7 +52,7 @@ import org.bukkit.plugin.PluginManager; * * BukkitPermissions overrides to force GM reponses to Superperms * - * @author ElgarL, based upon PermissionsEX implementation + * @author ElgarL, originally based upon PermissionsEX implementation */ public class BukkitPermissions { @@ -62,13 +62,25 @@ public class BukkitPermissions { protected boolean dumpAllPermissions = true; protected boolean dumpMatchedPermissions = true; public boolean player_join = false; + + private static Field permissions; + + // Setup reflection (Thanks to Codename_B for the reflection source) + static { + try { + permissions = PermissionAttachment.class.getDeclaredField("permissions"); + permissions.setAccessible(true); + } catch (SecurityException e) { + e.printStackTrace(); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } + } public BukkitPermissions(GroupManager plugin) { this.plugin = plugin; - - this.collectPermissions(); + //this.collectPermissions(); this.registerEvents(); - this.updateAllPlayers(); GroupManager.logger.info("Superperms support enabled."); @@ -93,6 +105,7 @@ public class BukkitPermissions { manager.registerEvent(Event.Type.PLUGIN_DISABLE, serverListener, Event.Priority.Normal, plugin); } + /* public void collectPermissions() { registeredPermissions.clear(); for (Plugin bukkitPlugin : Bukkit.getServer().getPluginManager().getPlugins()) { @@ -100,43 +113,56 @@ public class BukkitPermissions { registeredPermissions.push(permission); } } + */ public void updatePermissions(Player player) { this.updatePermissions(player, null); } + + /** + * Push all permissions which are registered with GM for this player, on this world to Bukkit + * and make it update for the child nodes. + * + * @param player + * @param world + */ public void updatePermissions(Player player, String world) { if (player == null || !GroupManager.isLoaded()) { return; } - if (!this.attachments.containsKey(player)) { - this.attachments.put(player, player.addAttachment(plugin)); + PermissionAttachment attachment; + // Find the players current attachment, or add a new one. + if (this.attachments.containsKey(player)) { + attachment = this.attachments.get(player); + } else { + attachment = player.addAttachment(plugin); + this.attachments.put(player, attachment);; } if (world == null) { world = player.getWorld().getName(); } - // All permissions registered with Bukkit for this player - PermissionAttachment attachment = this.attachments.get(player); - OverloadedWorldHolder worldData = plugin.getWorldsHolder().getWorldData(world); + Boolean value = false; + //User user = worldData.getUser(player.getName()); - User user = worldData.getUser(player.getName()); - + /* // clear permissions for (String permission : attachment.getPermissions().keySet()) attachment.unsetPermission(permission); - + */ + /* * find matching permissions * * and base bukkit perms if we are set to allow bukkit permissions to * override. */ - Boolean value = false; - + + /* for (Permission permission : registeredPermissions) { PermissionCheckResult result = worldData.getPermissionsHandler().checkFullGMPermission(user, permission.getName(), false); @@ -163,35 +189,47 @@ public class BukkitPermissions { if ((value == true) || (result.resultType == PermissionCheckResult.Type.NEGATION)) { attachment.setPermission(permission, value); } - /* - if ((value == true) || (result.resultType == PermissionCheckResult.Type.NOTFOUND)) { - // fetch and set all children of this permission node - Map children = permission.getChildren(); - if (children != null) { - for (String child : children.keySet()) { - if (children.get(child)) - attachment.setPermission(child, value); - } - } - }*/ - } + */ - // Add any missing permissions for this player (non bukkit plugins and child nodes) + // Add all permissions for this player (GM only) + // child nodes will be calculated by Bukkit. List playerPermArray = worldData.getPermissionsHandler().getAllPlayersPermissions(player.getName()); - + Map newPerms = new HashMap(); + for (String permission : playerPermArray) { value = true; if (permission.startsWith("-")) { permission = permission.substring(1); // cut off - value = false; } - + /* if (!attachment.getPermissions().containsKey(permission)) { attachment.setPermission(permission, value); } + */ + newPerms.put(permission, value); + } + //player.recalculatePermissions(); + + /** + * This is put in place until such a time as Bukkit pull 466 is implemented + * https://github.com/Bukkit/Bukkit/pull/466 + */ + try { // Codename_B source + @SuppressWarnings("unchecked") + Map orig = (Map) permissions.get(attachment); + // Clear the map (faster than removing the attachment and recalculating) + orig.clear(); + // Then whack our map into there + orig.putAll(newPerms); + // That's all folks! + attachment.getPermissible().recalculatePermissions(); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); } - player.recalculatePermissions(); } /** @@ -245,6 +283,12 @@ public class BukkitPermissions { return null; } + /** + * List all effective permissions for this player. + * + * @param player + * @return + */ public List listPerms(Player player) { List perms = new ArrayList(); @@ -266,6 +310,9 @@ public class BukkitPermissions { return perms; } + /** + * force Bukkit to update every OnlinePlayers permissions. + */ public void updateAllPlayers() { for (Player player : Bukkit.getServer().getOnlinePlayers()) { updatePermissions(player); @@ -326,7 +373,7 @@ public class BukkitPermissions { if (!GroupManager.isLoaded()) return; - collectPermissions(); + //collectPermissions(); updateAllPlayers(); } From 2a478fe03d97c97eeb189f90721724ec6d7fcad5 Mon Sep 17 00:00:00 2001 From: ElgarL Date: Tue, 10 Jan 2012 18:40:34 +0000 Subject: [PATCH 14/39] Optimize fetching of Mirrored world data. --- EssentialsGroupManager/src/Changelog.txt | 3 ++- EssentialsGroupManager/src/config.yml | 10 +++++----- .../groupmanager/dataholder/worlds/WorldsHolder.java | 12 +++++++----- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/EssentialsGroupManager/src/Changelog.txt b/EssentialsGroupManager/src/Changelog.txt index 6ac58e8ff..d9ac389cd 100644 --- a/EssentialsGroupManager/src/Changelog.txt +++ b/EssentialsGroupManager/src/Changelog.txt @@ -97,4 +97,5 @@ v 1.8: - Reload GlobalGroups when you perform a world load. - Changed GlobalGroups to save/load before local groups in the scheduled data saving/loading - Fix 'manucheckp' to correctly report if a permission is available from GroupManager or Bukkit. - - Changed over to a reflection method for populating superperms as Bukkit lags when you handle permissions one at a time. \ No newline at end of file + - Changed over to a reflection method for populating superperms as Bukkit lags when you handle permissions one at a time. + - Optimize fetching of Mirrored world data. \ No newline at end of file diff --git a/EssentialsGroupManager/src/config.yml b/EssentialsGroupManager/src/config.yml index 17ffc5b6e..2847dfe0e 100644 --- a/EssentialsGroupManager/src/config.yml +++ b/EssentialsGroupManager/src/config.yml @@ -25,14 +25,14 @@ settings: level: INFO mirrors: - # Worlds listed here have their permissions mirrored in their children. + # Worlds listed here have their settings mirrored in their children. # the first element 'world' is the main worlds name - # subsequent elements '- world_nether' are worlds which will use the same - # user/groups permissions as the parent. + # subsequent elements 'world_nether' and 'world_the_end' are worlds which will use the same + # user/groups files as the parent. world: - world_nether - world_the_end - - world2 - - world3 + # - world2 + # - world3 # world4: # - world5 \ No newline at end of file diff --git a/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/worlds/WorldsHolder.java b/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/worlds/WorldsHolder.java index dbdd17f34..49205e4fb 100644 --- a/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/worlds/WorldsHolder.java +++ b/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/worlds/WorldsHolder.java @@ -270,11 +270,12 @@ public class WorldsHolder { */ public OverloadedWorldHolder getWorldData(String worldName) { String worldNameLowered = worldName.toLowerCase(); - OverloadedWorldHolder data = worldsData.get(worldNameLowered); + // If a mirror change to the real world to load. if (mirrors.containsKey(worldNameLowered)) { - String realOne = mirrors.get(worldNameLowered); - data = worldsData.get(realOne.toLowerCase()); - } + worldNameLowered = mirrors.get(worldNameLowered); + } + OverloadedWorldHolder data = worldsData.get(worldNameLowered); + if (data == null) { GroupManager.logger.finest("Requested world " + worldName + " not found or badly mirrored. Returning default world..."); data = getDefaultWorld(); @@ -283,8 +284,9 @@ public class WorldsHolder { } /** - * Do a matching of playerName, if it s found only one player, do + * Do a matching of playerName, if its found only one player, do * getWorldData(player) + * * @param playerName * @return null if matching returned no player, or more than one. */ From 768c92e5bfe42ae9acb4d3ecdb6ebe8777534b11 Mon Sep 17 00:00:00 2001 From: ElgarL Date: Wed, 11 Jan 2012 05:51:40 +0000 Subject: [PATCH 15/39] Major, MAJOR changes to support partial/full world mirroring. You can now mirror groups.yml, users.yml or both files between different worlds. --- EssentialsGroupManager/src/Changelog.txt | 3 +- EssentialsGroupManager/src/config.yml | 26 +- .../anjocaido/groupmanager/GroupManager.java | 6 +- .../dataholder/GroupsDataHolder.java | 118 ++++ .../dataholder/OverloadedWorldHolder.java | 50 +- .../dataholder/UsersDataHolder.java | 106 ++++ .../dataholder/WorldDataHolder.java | 526 ++++++------------ .../dataholder/worlds/WorldsHolder.java | 305 +++++----- .../permissions/BukkitPermissions.java | 2 +- 9 files changed, 614 insertions(+), 528 deletions(-) create mode 100644 EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/GroupsDataHolder.java create mode 100644 EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/UsersDataHolder.java diff --git a/EssentialsGroupManager/src/Changelog.txt b/EssentialsGroupManager/src/Changelog.txt index d9ac389cd..364ae5952 100644 --- a/EssentialsGroupManager/src/Changelog.txt +++ b/EssentialsGroupManager/src/Changelog.txt @@ -98,4 +98,5 @@ v 1.8: - Changed GlobalGroups to save/load before local groups in the scheduled data saving/loading - Fix 'manucheckp' to correctly report if a permission is available from GroupManager or Bukkit. - Changed over to a reflection method for populating superperms as Bukkit lags when you handle permissions one at a time. - - Optimize fetching of Mirrored world data. \ No newline at end of file + - Major, MAJOR changes to support partial/full world mirroring. + You can now mirror groups.yml, users.yml or both files between different worlds. \ No newline at end of file diff --git a/EssentialsGroupManager/src/config.yml b/EssentialsGroupManager/src/config.yml index 2847dfe0e..13b59d343 100644 --- a/EssentialsGroupManager/src/config.yml +++ b/EssentialsGroupManager/src/config.yml @@ -26,13 +26,21 @@ settings: mirrors: # Worlds listed here have their settings mirrored in their children. - # the first element 'world' is the main worlds name - # subsequent elements 'world_nether' and 'world_the_end' are worlds which will use the same - # user/groups files as the parent. + # The first element 'world' is the main worlds name + # subsequent elements 'world_nether' and 'world_the_end' are worlds which will use + # the same user/groups files as the parent. + # Each child world can be configured to mirror the 'groups', 'users' or both files from it's parent. world: - - world_nether - - world_the_end - # - world2 - # - world3 - # world4: - # - world5 \ No newline at end of file + world_nether: + - users + - groups + world_the_end: + - users + - groups + # world2: (World2 would have it's own set of user and groups files) + # world3: + # - users (World3 would use the users.yml from world2, but it's own groups.yml) + # world4: + # - groups (World4 would use the groups.yml from world2, but it's own users.yml) + # world5: + # - world6 (this would cause world6 to mirror both files from world5) \ No newline at end of file diff --git a/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java b/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java index 4ba2624bf..8134a1c23 100644 --- a/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java +++ b/EssentialsGroupManager/src/org/anjocaido/groupmanager/GroupManager.java @@ -948,7 +948,7 @@ public class GroupManager extends JavaPlugin { } } else { - sender.sendMessage(ChatColor.YELLOW + "The grpup '" + auxGroup.getName() + "' has no specific permissions."); + sender.sendMessage(ChatColor.YELLOW + "The group '" + auxGroup.getName() + "' has no specific permissions."); auxString = ""; for (String grp : auxGroup.getInherits()) { auxString += grp + ", "; @@ -1522,7 +1522,8 @@ public class GroupManager extends JavaPlugin { } // WORKING config.load(); - + worldsHolder.mirrorSetUp(); + isLoaded = false; if (args.length > 0) { @@ -1539,7 +1540,6 @@ public class GroupManager extends JavaPlugin { worldsHolder.reloadAll(); sender.sendMessage(ChatColor.YELLOW + " The current world was reloaded."); } - worldsHolder.mirrorSetUp(); isLoaded = true; diff --git a/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/GroupsDataHolder.java b/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/GroupsDataHolder.java new file mode 100644 index 000000000..4fc819245 --- /dev/null +++ b/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/GroupsDataHolder.java @@ -0,0 +1,118 @@ +package org.anjocaido.groupmanager.dataholder; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +import org.anjocaido.groupmanager.data.Group; + + + +/** + * @author ElgarL + * + */ +public class GroupsDataHolder { + + /** + * Root World name this set of groups is associated with. + */ + private String name; + private Group defaultGroup = null; + private File groupsFile; + private boolean haveGroupsChanged = false; + private long timeStampGroups = 0; + + /** + * The actual groups holder + */ + private Map groups = new HashMap(); + + /** + * Constructor + */ + protected GroupsDataHolder() { + } + + protected void setWorldName(String worldName) { + name = worldName; + } + + /** + * @return the name + */ + public String getWorldName() { + return name; + } + + /** + * @return the defaultGroup + */ + public Group getDefaultGroup() { + return defaultGroup; + } + + /** + * @param defaultGroup the defaultGroup to set + */ + public void setDefaultGroup(Group defaultGroup) { + this.defaultGroup = defaultGroup; + } + + /** + * @return the groups + */ + public Map getGroups() { + return groups; + } + + /** + * @param groups the groups to set + */ + public void setGroups(Map groups) { + this.groups = groups; + } + + /** + * @return the groupsFile + */ + public File getGroupsFile() { + return groupsFile; + } + + /** + * @param groupsFile the groupsFile to set + */ + public void setGroupsFile(File groupsFile) { + this.groupsFile = groupsFile; + } + + /** + * @return the haveGroupsChanged + */ + public boolean HaveGroupsChanged() { + return haveGroupsChanged; + } + + /** + * @param haveGroupsChanged the haveGroupsChanged to set + */ + public void setGroupsChanged(boolean haveGroupsChanged) { + this.haveGroupsChanged = haveGroupsChanged; + } + + /** + * @return the timeStampGroups + */ + public long getTimeStampGroups() { + return timeStampGroups; + } + + /** + * @param timeStampGroups the timeStampGroups to set + */ + public void setTimeStampGroups(long timeStampGroups) { + this.timeStampGroups = timeStampGroups; + } + +} \ No newline at end of file diff --git a/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/OverloadedWorldHolder.java b/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/OverloadedWorldHolder.java index 8e2df5d10..b37c55e51 100644 --- a/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/OverloadedWorldHolder.java +++ b/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/OverloadedWorldHolder.java @@ -25,13 +25,11 @@ public class OverloadedWorldHolder extends WorldDataHolder { * * @param ph */ - @SuppressWarnings("deprecation") public OverloadedWorldHolder(WorldDataHolder ph) { super(ph.getName()); - this.f = ph.f; - this.groupsFile = ph.groupsFile; - this.usersFile = ph.usersFile; - this.defaultGroup = ph.defaultGroup; + this.setGroupsFile(ph.getGroupsFile()); + this.setUsersFile(ph.getUsersFile()); + //this.setDefaultGroup(ph.getDefaultGroup()); this.groups = ph.groups; this.users = ph.users; } @@ -49,11 +47,11 @@ public class OverloadedWorldHolder extends WorldDataHolder { return overloadedUsers.get(userNameLowered); } //END CODE - if (users.containsKey(userNameLowered)) { - return users.get(userNameLowered); + if (getUsers().containsKey(userNameLowered)) { + return getUsers().get(userNameLowered); } User newUser = createUser(userName); - haveUsersChanged = true; + setUsersChanged(true); return newUser; } @@ -69,8 +67,8 @@ public class OverloadedWorldHolder extends WorldDataHolder { if (theUser == null) { return; } - if ((theUser.getGroup() == null) || (!groups.containsKey(theUser.getGroupName().toLowerCase()))) { - theUser.setGroup(defaultGroup); + if ((theUser.getGroup() == null) || (!getGroups().containsKey(theUser.getGroupName().toLowerCase()))) { + theUser.setGroup(getDefaultGroup()); } //OVERLOADED CODE if (overloadedUsers.containsKey(theUser.getName().toLowerCase())) { @@ -80,8 +78,8 @@ public class OverloadedWorldHolder extends WorldDataHolder { } //END CODE removeUser(theUser.getName()); - users.put(theUser.getName().toLowerCase(), theUser); - haveUsersChanged = true; + getUsers().put(theUser.getName().toLowerCase(), theUser); + setUsersChanged(true); } /** @@ -97,9 +95,9 @@ public class OverloadedWorldHolder extends WorldDataHolder { return true; } //END CODE - if (users.containsKey(userName.toLowerCase())) { - users.remove(userName.toLowerCase()); - haveUsersChanged = true; + if (getUsers().containsKey(userName.toLowerCase())) { + getUsers().remove(userName.toLowerCase()); + setUsersChanged(true); return true; } return false; @@ -107,16 +105,16 @@ public class OverloadedWorldHolder extends WorldDataHolder { @Override public boolean removeGroup(String groupName) { - if (groupName.equals(defaultGroup)) { + if (groupName.equals(getDefaultGroup())) { return false; } - for (String key : groups.keySet()) { + for (String key : getGroups().keySet()) { if (groupName.equalsIgnoreCase(key)) { - groups.remove(key); - for (String userKey : users.keySet()) { - User user = users.get(userKey); + getGroups().remove(key); + for (String userKey : getUsers().keySet()) { + User user = getUsers().get(userKey); if (user.getGroupName().equalsIgnoreCase(key)) { - user.setGroup(defaultGroup); + user.setGroup(getDefaultGroup()); } } @@ -124,12 +122,12 @@ public class OverloadedWorldHolder extends WorldDataHolder { for (String userKey : overloadedUsers.keySet()) { User user = overloadedUsers.get(userKey); if (user.getGroupName().equalsIgnoreCase(key)) { - user.setGroup(defaultGroup); + user.setGroup(getDefaultGroup()); } } //END OVERLOAD - haveGroupsChanged = true; + setGroupsChanged(true); return true; } } @@ -143,7 +141,7 @@ public class OverloadedWorldHolder extends WorldDataHolder { @Override public Collection getUserList() { Collection overloadedList = new ArrayList(); - Collection normalList = users.values(); + Collection normalList = getUsers().values(); for (User u : normalList) { if (overloadedUsers.containsKey(u.getName().toLowerCase())) { overloadedList.add(overloadedUsers.get(u.getName().toLowerCase())); @@ -198,8 +196,8 @@ public class OverloadedWorldHolder extends WorldDataHolder { if (!isOverloaded(userName)) { return getUser(userName); } - if (users.containsKey(userName.toLowerCase())) { - return users.get(userName.toLowerCase()); + if (getUsers().containsKey(userName.toLowerCase())) { + return getUsers().get(userName.toLowerCase()); } User newUser = createUser(userName); return newUser; diff --git a/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/UsersDataHolder.java b/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/UsersDataHolder.java new file mode 100644 index 000000000..37e1c4b43 --- /dev/null +++ b/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/UsersDataHolder.java @@ -0,0 +1,106 @@ +package org.anjocaido.groupmanager.dataholder; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +import org.anjocaido.groupmanager.data.User; + + + +/** + * @author ElgarL + * + */ +public class UsersDataHolder { + + /** + * Root World name this set of groups is associated with. + */ + private String name; + private File usersFile; + private boolean haveUsersChanged = false; + private long timeStampUsers = 0; + + /** + * The actual groups holder + */ + private Map users = new HashMap(); + + /** + * Constructor + */ + protected UsersDataHolder() { + } + + /** + * @param worldName + */ + public void setWorldName(String worldName) { + this.name = worldName; + } + + /** + * @return the name + */ + public String getWorldName() { + return this.name; + } + + /** + * @return the users + */ + public Map getUsers() { + return users; + } + + /** + * @param users the users to set + */ + public void setUsers(Map users) { + this.users = users; + } + + /** + * @return the usersFile + */ + public File getUsersFile() { + return usersFile; + } + + /** + * @param usersFile the usersFile to set + */ + public void setUsersFile(File usersFile) { + this.usersFile = usersFile; + } + + /** + * @return the haveUsersChanged + */ + public boolean HaveUsersChanged() { + return haveUsersChanged; + } + + /** + * @param haveUsersChanged the haveUsersChanged to set + */ + public void setUsersChanged(boolean haveUsersChanged) { + this.haveUsersChanged = haveUsersChanged; + } + + /** + * @return the timeStampUsers + */ + public long getTimeStampUsers() { + return timeStampUsers; + } + + /** + * @param timeStampUsers the timeStampUsers to set + */ + public void setTimeStampUsers(long timeStampUsers) { + this.timeStampUsers = timeStampUsers; + } + +} \ No newline at end of file diff --git a/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/WorldDataHolder.java b/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/WorldDataHolder.java index 2a944856b..73c4dca0f 100644 --- a/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/WorldDataHolder.java +++ b/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/WorldDataHolder.java @@ -8,7 +8,6 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; -import java.io.FileWriter; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; @@ -49,72 +48,37 @@ public class WorldDataHolder { /** * The actual groups holder */ - protected Map groups = new HashMap(); + protected GroupsDataHolder groups = new GroupsDataHolder(); /** * The actual users holder */ - protected Map users = new HashMap(); - - /** - * Points to the default group - */ - protected Group defaultGroup = null; - - /** - * The file, which this class loads/save data from/to - * @deprecated - */ - @Deprecated - protected File f; - + protected UsersDataHolder users = new UsersDataHolder(); /** * */ protected AnjoPermissionsHandler permissionsHandler; - /** - * - */ - protected File usersFile; - /** - * - */ - protected File groupsFile; - /** - * - */ - protected boolean haveUsersChanged = false; - /** - * - */ - protected boolean haveGroupsChanged = false; - /** - * - */ - protected long timeStampGroups = 0; - /** - * - */ - protected long timeStampUsers = 0; /** * Prevent direct instantiation * @param worldName */ - protected WorldDataHolder(String worldName) { + public WorldDataHolder(String worldName) { name = worldName; } /** * The main constructor for a new WorldDataHolder - * Please don't set the default group as null * @param worldName - * @param defaultGroup the default group. its good to start with one + * @param groups + * @param users */ - public WorldDataHolder(String worldName, Group defaultGroup) { + public WorldDataHolder(String worldName, GroupsDataHolder groups, UsersDataHolder users) { this.name = worldName; - groups.put(defaultGroup.getName().toLowerCase(), defaultGroup); - this.defaultGroup = defaultGroup; + this.groups = groups; + this.users = users; + + //this.defaultGroup = defaultGroup; } /** @@ -125,8 +89,8 @@ public class WorldDataHolder { * @return class that manage that user permission */ public User getUser(String userName) { - if (users.containsKey(userName.toLowerCase())) { - return users.get(userName.toLowerCase()); + if (getUsers().containsKey(userName.toLowerCase())) { + return getUsers().get(userName.toLowerCase()); } User newUser = createUser(userName); return newUser; @@ -144,11 +108,11 @@ public class WorldDataHolder { return; } if ((theUser.getGroup() == null)) { - theUser.setGroup(defaultGroup); + theUser.setGroup(groups.getDefaultGroup()); } removeUser(theUser.getName()); - users.put(theUser.getName().toLowerCase(), theUser); - haveUsersChanged = true; + getUsers().put(theUser.getName().toLowerCase(), theUser); + setUsersChanged(true); if (GroupManager.isLoaded()) GroupManagerEventHandler.callEvent(theUser, Action.USER_ADDED); } @@ -159,9 +123,9 @@ public class WorldDataHolder { * @return true if it had something to remove */ public boolean removeUser(String userName) { - if (users.containsKey(userName.toLowerCase())) { - users.remove(userName.toLowerCase()); - haveUsersChanged = true; + if (getUsers().containsKey(userName.toLowerCase())) { + getUsers().remove(userName.toLowerCase()); + setUsersChanged(true); if (GroupManager.isLoaded()) GroupManagerEventHandler.callEvent(userName, GMUserEvent.Action.USER_REMOVED); return true; @@ -175,7 +139,7 @@ public class WorldDataHolder { * @return true if we have data for this player. */ public boolean isUserDeclared(String userName) { - return users.containsKey(userName.toLowerCase()); + return getUsers().containsKey(userName.toLowerCase()); } /** @@ -183,11 +147,11 @@ public class WorldDataHolder { * @param group the group you want make default. */ public void setDefaultGroup(Group group) { - if (!groups.containsKey(group.getName().toLowerCase()) || (group.getDataSource() != this)) { + if (!getGroups().containsKey(group.getName().toLowerCase()) || (group.getDataSource() != this)) { addGroup(group); } - defaultGroup = this.getGroup(group.getName()); - haveGroupsChanged = true; + groups.setDefaultGroup(getGroup(group.getName())); + setGroupsChanged(true); if (GroupManager.isLoaded()) GroupManagerEventHandler.callEvent(GMSystemEvent.Action.DEFAULT_GROUP_CHANGED); } @@ -197,7 +161,7 @@ public class WorldDataHolder { * @return the default group */ public Group getDefaultGroup() { - return defaultGroup; + return groups.getDefaultGroup(); } /** @@ -209,7 +173,7 @@ public class WorldDataHolder { if (groupName.toLowerCase().startsWith("g:")) return GroupManager.getGlobalGroups().getGroup(groupName); else - return groups.get(groupName.toLowerCase()); + return getGroups().get(groupName.toLowerCase()); } /** @@ -222,7 +186,7 @@ public class WorldDataHolder { if (groupName.toLowerCase().startsWith("g:")) return GroupManager.getGlobalGroups().hasGroup(groupName); else - return groups.containsKey(groupName.toLowerCase()); + return getGroups().containsKey(groupName.toLowerCase()); } /** @@ -240,8 +204,8 @@ public class WorldDataHolder { groupToAdd = groupToAdd.clone(this); } removeGroup(groupToAdd.getName()); - groups.put(groupToAdd.getName().toLowerCase(), groupToAdd); - haveGroupsChanged = true; + getGroups().put(groupToAdd.getName().toLowerCase(), groupToAdd); + setGroupsChanged(true); if (GroupManager.isLoaded()) GroupManagerEventHandler.callEvent(groupToAdd, GMGroupEvent.Action.GROUP_ADDED); } @@ -256,12 +220,12 @@ public class WorldDataHolder { return GroupManager.getGlobalGroups().removeGroup(groupName); } - if (defaultGroup != null && groupName.equalsIgnoreCase(defaultGroup.getName())) { + if (getDefaultGroup() != null && groupName.equalsIgnoreCase(getDefaultGroup().getName())) { return false; } - if (groups.containsKey(groupName.toLowerCase())) { - groups.remove(groupName.toLowerCase()); - haveGroupsChanged = true; + if (getGroups().containsKey(groupName.toLowerCase())) { + getGroups().remove(groupName.toLowerCase()); + setGroupsChanged(true); if (GroupManager.isLoaded()) GroupManagerEventHandler.callEvent(groupName.toLowerCase(), GMGroupEvent.Action.GROUP_REMOVED); return true; @@ -277,13 +241,13 @@ public class WorldDataHolder { * @return null if user already exists. or new User */ public User createUser(String userName) { - if (this.users.containsKey(userName.toLowerCase())) { + if (getUsers().containsKey(userName.toLowerCase())) { return null; } User newUser = new User(this, userName); - newUser.setGroup(defaultGroup); - this.addUser(newUser); - haveUsersChanged = true; + newUser.setGroup(groups.getDefaultGroup()); + addUser(newUser); + setUsersChanged(true); return newUser; } @@ -299,13 +263,13 @@ public class WorldDataHolder { return GroupManager.getGlobalGroups().newGroup(newGroup); } - if (this.groups.containsKey(groupName.toLowerCase())) { + if (getGroups().containsKey(groupName.toLowerCase())) { return null; } Group newGroup = new Group(this, groupName); - this.addGroup(newGroup); - haveGroupsChanged = true; + addGroup(newGroup); + setGroupsChanged(true); return newGroup; } @@ -314,7 +278,7 @@ public class WorldDataHolder { * @return a collection of the groups */ public Collection getGroupList() { - return groups.values(); + return getGroups().values(); } /** @@ -322,7 +286,7 @@ public class WorldDataHolder { * @return a collection of the users */ public Collection getUserList() { - return users.values(); + return getUsers().values(); } /** @@ -352,9 +316,9 @@ public class WorldDataHolder { for (Group tempGroup : ph.getGroupList()) { tempGroup.clone(this); } - this.setDefaultGroup(this.getGroup(ph.getDefaultGroup().getName())); + this.setDefaultGroup(getGroup(ph.getDefaultGroup().getName())); this.removeGroupsChangedFlag(); - this.timeStampGroups = getGroupsFile().lastModified(); + this.setTimeStampGroups(getGroupsFile().lastModified()); ph = null; } catch (Exception ex) { @@ -377,7 +341,7 @@ public class WorldDataHolder { tempGroup.clone(ph); } // setup the default group before loading user data. - ph.setDefaultGroup(ph.getGroup(this.getDefaultGroup().getName())); + ph.setDefaultGroup(ph.getGroup(getDefaultGroup().getName())); loadUsers(ph, getUsersFile()); // transfer new data resetUsers(); @@ -385,7 +349,7 @@ public class WorldDataHolder { tempUser.clone(this); } this.removeUsersChangedFlag(); - this.timeStampUsers = getUsersFile().lastModified(); + this.setTimeStampUsers(getUsersFile().lastModified()); ph = null; } catch (Exception ex) { @@ -395,165 +359,39 @@ public class WorldDataHolder { GroupManagerEventHandler.callEvent(GMSystemEvent.Action.RELOADED); } - /** - * Save by yourself! - * @deprecated - */ - @Deprecated - public void commit() { - writeGroups(this, getGroupsFile()); - writeUsers(this, getUsersFile()); + public void loadGroups(File groupsFile) { + + GroupManager.setLoaded(false); + try { + setGroupsFile(groupsFile); + loadGroups(this, groupsFile); + } catch (FileNotFoundException e) { + e.printStackTrace(); + throw new IllegalArgumentException("The file which should contain groups does not exist!\n" + groupsFile.getPath()); + } catch (IOException e) { + e.printStackTrace(); + throw new IllegalArgumentException("Error access the groups file!\n" + groupsFile.getPath()); + } + + GroupManager.setLoaded(true); } + + public void loadUsers(File usersFile) { - /** - * Returns a data holder for the given file - * - * @param worldName - * @param file - * @return a new WorldDataHolder - * - * @throws Exception - * @deprecated - */ - @SuppressWarnings({"rawtypes", "unchecked"}) - @Deprecated - public static WorldDataHolder load(String worldName, File file) throws Exception { - WorldDataHolder ph = new WorldDataHolder(worldName); - ph.f = file; - final Yaml yaml = new Yaml(new SafeConstructor()); - Map rootDataNode; - if (!file.exists()) { - throw new Exception("The file which should contain permissions does not exist!\n" + file.getPath()); - } - FileInputStream rx = new FileInputStream(file); - try { - rootDataNode = (Map) yaml.load(new UnicodeReader(rx)); - if (rootDataNode == null) { - throw new NullPointerException(); - } - } catch (Exception ex) { - throw new Exception("The following file couldn't pass on Parser.\n" + file.getPath(), ex); - } finally { - rx.close(); - } - Map> inheritance = new HashMap>(); - try { - Map allGroupsNode = (Map) rootDataNode.get("groups"); - for (String groupKey : allGroupsNode.keySet()) { - Map thisGroupNode = (Map) allGroupsNode.get(groupKey); - Group thisGrp = ph.createGroup(groupKey); - if (thisGrp == null) { - throw new IllegalArgumentException("I think this group was declared more than once: " + groupKey); - } - if (thisGroupNode.get("default") == null) { - thisGroupNode.put("default", false); - } - if ((Boolean.parseBoolean(thisGroupNode.get("default").toString()))) { - if (ph.getDefaultGroup() != null) { - GroupManager.logger.warning("The group " + thisGrp.getName() + " is declaring be default where" + ph.getDefaultGroup().getName() + " already was."); - GroupManager.logger.warning("Overriding first request."); - } - ph.setDefaultGroup(thisGrp); - } + GroupManager.setLoaded(false); + try { + setUsersFile(usersFile); + loadUsers(this, usersFile); + } catch (FileNotFoundException e) { + e.printStackTrace(); + throw new IllegalArgumentException("The file which should contain users does not exist!\n" + usersFile.getPath()); + } catch (IOException e) { + e.printStackTrace(); + throw new IllegalArgumentException("Error access the users file!\n" + usersFile.getPath()); + } - //PERMISSIONS NODE - if (thisGroupNode.get("permissions") == null) { - thisGroupNode.put("permissions", new ArrayList()); - } - if (thisGroupNode.get("permissions") instanceof List) { - for (Object o : ((List) thisGroupNode.get("permissions"))) { - thisGrp.addPermission(o.toString()); - } - } else if (thisGroupNode.get("permissions") instanceof String) { - thisGrp.addPermission((String) thisGroupNode.get("permissions")); - } else { - throw new IllegalArgumentException("Unknown type of permissions node(Should be String or List): " + thisGroupNode.get("permissions").getClass().getName()); - } - - //INFO NODE - Map infoNode = (Map) thisGroupNode.get("info"); - if (infoNode != null) { - thisGrp.setVariables(infoNode); - } - - //END INFO NODE - - Object inheritNode = thisGroupNode.get("inheritance"); - if (inheritNode == null) { - thisGroupNode.put("inheritance", new ArrayList()); - } else if (inheritNode instanceof List) { - List groupsInh = (List) inheritNode; - for (String grp : groupsInh) { - if (inheritance.get(groupKey) == null) { - List thisInherits = new ArrayList(); - inheritance.put(groupKey, thisInherits); - } - inheritance.get(groupKey).add(grp); - - } - } - } - } catch (Exception ex) { - ex.printStackTrace(); - throw new Exception("Your Permissions config file is invalid. See console for details."); - } - if (ph.defaultGroup == null) { - throw new IllegalArgumentException("There was no Default Group declared."); - } - for (String groupKey : inheritance.keySet()) { - List inheritedList = inheritance.get(groupKey); - Group thisGroup = ph.getGroup(groupKey); - for (String inheritedKey : inheritedList) { - Group inheritedGroup = ph.getGroup(inheritedKey); - if (thisGroup != null && inheritedGroup != null) { - thisGroup.addInherits(inheritedGroup); - } - } - } - // Process USERS - Map allUsersNode = (Map) rootDataNode.get("users"); - for (String usersKey : allUsersNode.keySet()) { - Map thisUserNode = (Map) allUsersNode.get(usersKey); - User thisUser = ph.createUser(usersKey); - if (thisUser == null) { - GroupManager.logger.warning("I think this user was declared more than once: " + usersKey); - continue; - } - if (thisUserNode.get("permissions") == null) { - thisUserNode.put("permissions", new ArrayList()); - } - if (thisUserNode.get("permissions") instanceof List) { - for (Object o : ((List) thisUserNode.get("permissions"))) { - thisUser.addPermission(o.toString()); - } - } else if (thisUserNode.get("permissions") instanceof String) { - thisUser.addPermission(thisUserNode.get("permissions").toString()); - } - - - //USER INFO NODE - BETA - - //INFO NODE - Map infoNode = (Map) thisUserNode.get("info"); - if (infoNode != null) { - thisUser.setVariables(infoNode); - } - //END INFO NODE - BETA - - if (thisUserNode.get("group") != null) { - Group hisGroup = ph.getGroup(thisUserNode.get("group").toString()); - if (hisGroup == null) { - GroupManager.logger.warning("There is no group " + thisUserNode.get("group").toString() + ", as stated for player " + thisUser.getName()); - thisUser.setGroup(ph.defaultGroup); - } - thisUser.setGroup(hisGroup); - } else { - thisUser.setGroup(ph.defaultGroup); - } - } - return ph; + GroupManager.setLoaded(true); } - /** * Returns a NEW data holder containing data read from the files * @@ -568,8 +406,8 @@ public class WorldDataHolder { WorldDataHolder ph = new WorldDataHolder(worldName); GroupManager.setLoaded(false); - loadGroups(ph, groupsFile); - loadUsers(ph, usersFile); + if (groupsFile != null) loadGroups(ph, groupsFile); + if (usersFile != null) loadUsers(ph, usersFile); GroupManager.setLoaded(true); return ph; @@ -673,7 +511,7 @@ public class WorldDataHolder { // ex.printStackTrace(); // throw new IllegalArgumentException("Your Permissions config file is invalid. See console for details."); //} - if (ph.defaultGroup == null) { + if (ph.getDefaultGroup() == null) { throw new IllegalArgumentException("There was no Default Group declared in file: " + groupsFile.getPath()); } for (String groupKey : inheritance.keySet()) { @@ -689,7 +527,7 @@ public class WorldDataHolder { ph.removeGroupsChangedFlag(); // Update the LastModified time. - ph.groupsFile = groupsFile; + ph.setGroupsFile(groupsFile); ph.setTimeStampGroups(groupsFile.lastModified()); //return ph; @@ -788,108 +626,21 @@ public class WorldDataHolder { Group hisGroup = ph.getGroup(thisUserNode.get("group").toString()); if (hisGroup == null) { GroupManager.logger.warning("There is no group " + thisUserNode.get("group").toString() + ", as stated for player " + thisUser.getName() + ": Set to '" + ph.getDefaultGroup().getName() + "' for file: " + usersFile.getPath()); - hisGroup = ph.defaultGroup; + hisGroup = ph.getDefaultGroup(); //throw new IllegalArgumentException("There is no group " + thisUserNode.get("group").toString() + ", as stated for player " + thisUser.getName()); } thisUser.setGroup(hisGroup); } else { - thisUser.setGroup(ph.defaultGroup); + thisUser.setGroup(ph.getDefaultGroup()); } } ph.removeUsersChangedFlag(); // Update the LastModified time. - ph.usersFile = usersFile; + ph.setUsersFile(usersFile); ph.setTimeStampUsers(usersFile.lastModified()); } - /** - * Write a dataHolder in a specified file - * @param ph - * @param file - * @deprecated - */ - @Deprecated - public static void write(WorldDataHolder ph, File file) { - Map root = new HashMap(); - - Map pluginMap = new HashMap(); - root.put("plugin", pluginMap); - - Map permissionsMap = new HashMap(); - pluginMap.put("permissions", permissionsMap); - - permissionsMap.put("system", "default"); - - Map groupsMap = new HashMap(); - root.put("groups", groupsMap); - for (String groupKey : ph.groups.keySet()) { - Group group = ph.groups.get(groupKey); - - Map aGroupMap = new HashMap(); - groupsMap.put(group.getName(), aGroupMap); - - aGroupMap.put("default", group.equals(ph.defaultGroup)); - - Map infoMap = new HashMap(); - aGroupMap.put("info", infoMap); - - for (String infoKey : group.getVariables().getVarKeyList()) { - infoMap.put(infoKey, group.getVariables().getVarObject(infoKey)); - } - - aGroupMap.put("inheritance", group.getInherits()); - - aGroupMap.put("permissions", group.getPermissionList()); - } - - Map usersMap = new HashMap(); - root.put("users", usersMap); - for (String userKey : ph.users.keySet()) { - User user = ph.users.get(userKey); - if ((user.getGroup() == null || user.getGroup().equals(ph.defaultGroup)) && user.getPermissionList().isEmpty()) { - continue; - } - - Map aUserMap = new HashMap(); - usersMap.put(user.getName(), aUserMap); - - if (user.getGroup() == null) { - aUserMap.put("group", ph.defaultGroup.getName()); - } else { - aUserMap.put("group", user.getGroup().getName()); - } - //USER INFO NODE - BETA - if (user.getVariables().getSize() > 0) { - Map infoMap = new HashMap(); - aUserMap.put("info", infoMap); - - for (String infoKey : user.getVariables().getVarKeyList()) { - infoMap.put(infoKey, user.getVariables().getVarObject(infoKey)); - } - } - //END USER INFO NODE - BETA - - aUserMap.put("permissions", user.getPermissionList()); - } - DumperOptions opt = new DumperOptions(); - opt.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); - final Yaml yaml = new Yaml(opt); - - FileWriter tx = null; - try { - tx = new FileWriter(file, false); - tx.write(yaml.dump(root)); - tx.flush(); - } catch (Exception e) { - } finally { - try { - tx.close(); - } catch (IOException ex) { - } - } - } - /** * Write a dataHolder in a specified file * @param ph @@ -901,16 +652,16 @@ public class WorldDataHolder { Map groupsMap = new HashMap(); root.put("groups", groupsMap); - for (String groupKey : ph.groups.keySet()) { - Group group = ph.groups.get(groupKey); + for (String groupKey : ph.getGroups().keySet()) { + Group group = ph.getGroups().get(groupKey); Map aGroupMap = new HashMap(); groupsMap.put(group.getName(), aGroupMap); - if (ph.defaultGroup == null) { + if (ph.getDefaultGroup() == null) { GroupManager.logger.severe("There is no default group for world: " + ph.getName()); } - aGroupMap.put("default", group.equals(ph.defaultGroup)); + aGroupMap.put("default", group.equals(ph.getDefaultGroup())); Map infoMap = new HashMap(); aGroupMap.put("info", infoMap); @@ -951,7 +702,7 @@ public class WorldDataHolder { } // Update the LastModified time. - ph.groupsFile = groupsFile; + ph.setGroupsFile(groupsFile); ph.setTimeStampGroups(groupsFile.lastModified()); ph.removeGroupsChangedFlag(); @@ -982,9 +733,9 @@ public class WorldDataHolder { Map usersMap = new HashMap(); root.put("users", usersMap); - for (String userKey : ph.users.keySet()) { - User user = ph.users.get(userKey); - if ((user.getGroup() == null || user.getGroup().equals(ph.defaultGroup)) && user.getPermissionList().isEmpty() && user.getVariables().isEmpty() && user.isSubGroupsEmpty()) { + for (String userKey : ph.getUsers().keySet()) { + User user = ph.getUsers().get(userKey); + if ((user.getGroup() == null || user.getGroup().equals(ph.getDefaultGroup())) && user.getPermissionList().isEmpty() && user.getVariables().isEmpty() && user.isSubGroupsEmpty()) { continue; } @@ -992,7 +743,7 @@ public class WorldDataHolder { usersMap.put(user.getName(), aUserMap); if (user.getGroup() == null) { - aUserMap.put("group", ph.defaultGroup.getName()); + aUserMap.put("group", ph.getDefaultGroup().getName()); } else { aUserMap.put("group", user.getGroup().getName()); } @@ -1027,7 +778,7 @@ public class WorldDataHolder { } // Update the LastModified time. - ph.usersFile = usersFile; + ph.setUsersFile(usersFile); ph.setTimeStampUsers(usersFile.lastModified()); ph.removeUsersChangedFlag(); @@ -1083,16 +834,23 @@ public class WorldDataHolder { } return permissionsHandler; } + + /** + * @param haveUsersChanged the haveUsersChanged to set + */ + public void setUsersChanged(boolean haveUsersChanged) { + users.setUsersChanged(haveUsersChanged); + } /** * * @return true if any user data has changed */ public boolean haveUsersChanged() { - if (haveUsersChanged) { + if (users.HaveUsersChanged()) { return true; } - for (User u : users.values()) { + for (User u : users.getUsers().values()) { if (u.isChanged()) { return true; } @@ -1100,15 +858,22 @@ public class WorldDataHolder { return false; } + /** + * @param setGroupsChanged the haveGroupsChanged to set + */ + public void setGroupsChanged(boolean setGroupsChanged) { + groups.setGroupsChanged(setGroupsChanged); + } + /** * * @return true if any group data has changed. */ public boolean haveGroupsChanged() { - if (haveGroupsChanged) { + if (groups.HaveGroupsChanged()) { return true; } - for (Group g : groups.values()) { + for (Group g : groups.getGroups().values()) { if (g.isChanged()) { return true; } @@ -1120,8 +885,8 @@ public class WorldDataHolder { * */ public void removeUsersChangedFlag() { - haveUsersChanged = false; - for (User u : users.values()) { + setUsersChanged(false); + for (User u : getUsers().values()) { u.flagAsSaved(); } } @@ -1130,8 +895,8 @@ public class WorldDataHolder { * */ public void removeGroupsChangedFlag() { - haveGroupsChanged = false; - for (Group g : groups.values()) { + setGroupsChanged(false); + for (Group g : getGroups().values()) { g.flagAsSaved(); } } @@ -1140,14 +905,28 @@ public class WorldDataHolder { * @return the usersFile */ public File getUsersFile() { - return usersFile; + return users.getUsersFile(); + } + + /** + * @param file the usersFile to set + */ + public void setUsersFile(File file) { + users.setUsersFile(file); } /** * @return the groupsFile */ public File getGroupsFile() { - return groupsFile; + return groups.getGroupsFile(); + } + + /** + * @param file the groupsFile to set + */ + public void setGroupsFile(File file) { + groups.setGroupsFile(file); } /** @@ -1161,60 +940,85 @@ public class WorldDataHolder { * Resets Groups. */ public void resetGroups() { - this.defaultGroup = null; - this.groups = new HashMap(); + //setDefaultGroup(null); + groups.setGroups(new HashMap()); } /** * Resets Users */ public void resetUsers() { - this.users = new HashMap(); + users.setUsers(new HashMap()); } /** * @return the groups */ public Map getGroups() { - return groups; + return groups.getGroups(); } /** * @return the users */ public Map getUsers() { + return users.getUsers(); + } + + /** + * @return the groups + */ + public GroupsDataHolder getGroupsObject() { + return groups; + } + /** + * @param groupsDataHolder the GroupsDataHolder to set + */ + public void setGroupsObject(GroupsDataHolder groupsDataHolder) { + groups = groupsDataHolder; + } + /** + * @return the users + */ + public UsersDataHolder getUsersObject() { return users; } + /** + * @param usersDataHolder the UsersDataHolder to set + */ + public void setUsersObject(UsersDataHolder usersDataHolder) { + users = usersDataHolder; + } /** * @return the timeStampGroups */ public long getTimeStampGroups() { - return timeStampGroups; + return groups.getTimeStampGroups(); } /** * @return the timeStampUsers */ public long getTimeStampUsers() { - return timeStampUsers; + return users.getTimeStampUsers(); } /** * @param timeStampGroups the timeStampGroups to set */ protected void setTimeStampGroups(long timeStampGroups) { - this.timeStampGroups = timeStampGroups; + groups.setTimeStampGroups(timeStampGroups); } /** * @param timeStampUsers the timeStampUsers to set */ protected void setTimeStampUsers(long timeStampUsers) { - this.timeStampUsers = timeStampUsers; + users.setTimeStampUsers(timeStampUsers); } public void setTimeStamps() { - if (groupsFile != null) - setTimeStampGroups(groupsFile.lastModified()); - if (usersFile != null) - setTimeStampUsers(usersFile.lastModified()); + if (getGroupsFile() != null) + setTimeStampGroups(getGroupsFile().lastModified()); + if (getUsersFile() != null) + setTimeStampUsers(getUsersFile().lastModified()); } } diff --git a/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/worlds/WorldsHolder.java b/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/worlds/WorldsHolder.java index 49205e4fb..1a765c7b4 100644 --- a/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/worlds/WorldsHolder.java +++ b/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/worlds/WorldsHolder.java @@ -6,7 +6,6 @@ package org.anjocaido.groupmanager.dataholder.worlds; import java.io.File; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; @@ -22,6 +21,7 @@ import org.anjocaido.groupmanager.dataholder.OverloadedWorldHolder; import org.anjocaido.groupmanager.permissions.AnjoPermissionsHandler; import org.anjocaido.groupmanager.utils.Tasks; import org.bukkit.World; +import org.bukkit.configuration.MemorySection; import org.bukkit.entity.Player; /** @@ -34,6 +34,7 @@ public class WorldsHolder { * Map with instances of loaded worlds. */ private Map worldsData = new HashMap(); + /** * Map of mirrors: * The key is the mirror. @@ -41,7 +42,9 @@ public class WorldsHolder { * * Mirror shows the same data of mirrored. */ - private Map mirrors = new HashMap(); + private Map mirrorsGroup = new HashMap(); + private Map mirrorsUser = new HashMap(); + private OverloadedWorldHolder defaultWorld; private String serverDefaultWorldName; private GroupManager plugin; @@ -85,7 +88,7 @@ public class WorldsHolder { */ for (World world: plugin.getServer().getWorlds()) if ((!worldsData.containsKey(world.getName().toLowerCase())) - && (!mirrors.containsKey(world.getName().toLowerCase()))) + && ((!mirrorsGroup.containsKey(world.getName().toLowerCase())) || (!mirrorsUser.containsKey(world.getName().toLowerCase())))) setupWorldFolder(world.getName()); /* * Loop over all folders within the worlds folder @@ -97,10 +100,11 @@ public class WorldsHolder { /* * don't load any worlds which are already loaded - * or mirrored worlds that don't need data. + * or fully mirrored worlds that don't need data. */ if (!worldsData.containsKey(folder.getName().toLowerCase()) - && !mirrors.containsKey(folder.getName().toLowerCase())) { + && ((!mirrorsGroup.containsKey(folder.getName().toLowerCase())) + || (!mirrorsUser.containsKey(folder.getName().toLowerCase())))) { loadWorld(folder.getName()); } @@ -110,7 +114,8 @@ public class WorldsHolder { @SuppressWarnings("rawtypes") public void mirrorSetUp() { - mirrors.clear(); + mirrorsGroup.clear(); + mirrorsUser.clear(); Map mirrorsMap = plugin.getGMConfig().getMirrorsMap(); if (mirrorsMap != null) { for (String source : mirrorsMap.keySet()) { @@ -122,16 +127,52 @@ public class WorldsHolder { if (mirrorsMap.get(source) instanceof ArrayList) { ArrayList mirrorList = (ArrayList) mirrorsMap.get(source); + + // These worlds fully mirror their parent for (Object o : mirrorList) { try { - mirrors.remove(o.toString().toLowerCase()); + mirrorsGroup.remove(o.toString().toLowerCase()); + mirrorsUser.remove(o.toString().toLowerCase()); } catch (Exception e) { } - mirrors.put(o.toString().toLowerCase(), getWorldData(source).getName()); + mirrorsGroup.put(o.toString().toLowerCase(), getWorldData(source).getName()); + mirrorsUser.put(o.toString().toLowerCase(), getWorldData(source).getName()); } - } else if (mirrorsMap.get(source) instanceof Object) { - String aMirror = mirrorsMap.get(source).toString(); - mirrors.put(aMirror.toLowerCase(), getWorldData(source).getName()); + } else if (mirrorsMap.get(source) instanceof MemorySection) { + MemorySection subSection = (MemorySection) mirrorsMap.get(source); + + for (String key : subSection.getKeys(true)) { + //System.out.print("Key - " + key); + + if (subSection.get(key) instanceof ArrayList) { + ArrayList mirrorList = (ArrayList) subSection.get(key); + + // These worlds have defined mirroring + for (Object o : mirrorList) { + String type = o.toString().toLowerCase(); + try { + if (type.equals("groups")) + mirrorsGroup.remove(key.toLowerCase()); + + if (type.equals("users")) + mirrorsUser.remove(key.toLowerCase()); + + } catch (Exception e) { + } + if (type.equals("groups")) + mirrorsGroup.put(key.toLowerCase(), getWorldData(source).getName()); + + if (type.equals("users")) + mirrorsUser.put(key.toLowerCase(), getWorldData(source).getName()); + } + + + + } else { + throw new IllegalStateException("Unknown mirroring format for " + key); + } + + } } } } @@ -141,16 +182,22 @@ public class WorldsHolder { * */ public void reloadAll() { + // Load global groups + GroupManager.getGlobalGroups().load(); + ArrayList alreadyDone = new ArrayList(); for (WorldDataHolder w : worldsData.values()) { if (alreadyDone.contains(w)) { continue; } - w.reload(); + if (!mirrorsGroup.containsKey(w.getName().toLowerCase())) + w.reloadGroups(); + if (!mirrorsUser.containsKey(w.getName().toLowerCase())) + w.reloadUsers(); + alreadyDone.add(w); } - // Load global groups - GroupManager.getGlobalGroups().load(); + } /** @@ -158,7 +205,10 @@ public class WorldsHolder { * @param worldName */ public void reloadWorld(String worldName) { - getWorldData(worldName).reload(); + if (!mirrorsGroup.containsKey(worldName.toLowerCase())) + getWorldData(worldName).reloadGroups(); + if (!mirrorsUser.containsKey(worldName.toLowerCase())) + getWorldData(worldName).reloadUsers(); } /** @@ -194,48 +244,50 @@ public class WorldsHolder { GroupManager.logger.severe("WHAT HAPPENED?"); continue; } - if (w.haveGroupsChanged()) { - if (overwrite || (!overwrite && (w.getTimeStampGroups() >= w.getGroupsFile().lastModified()))) { - // Backup Groups file - backupFile(w,true); - - WorldDataHolder.writeGroups(w, w.getGroupsFile()); - //w.removeGroupsChangedFlag(); - } else { - // Newer file found. - GroupManager.logger.log(Level.WARNING, "Newer Groups file found for " + w.getName() + ", but we have local changes!"); - throw new IllegalStateException("Unable to save unless you issue a '/mansave force'"); - } - } else { - //Check for newer file as no local changes. - if (w.getTimeStampGroups() < w.getGroupsFile().lastModified()) { - System.out.print("Newer Groups file found (Loading changes)!"); - // Backup Groups file - backupFile(w,true); - w.reloadGroups(); - } - } - if (w.haveUsersChanged()) { - if (overwrite || (!overwrite && (w.getTimeStampUsers() >= w.getUsersFile().lastModified()))) { - // Backup Users file - backupFile(w,false); - - WorldDataHolder.writeUsers(w, w.getUsersFile()); - //w.removeUsersChangedFlag(); - } else { - // Newer file found. - GroupManager.logger.log(Level.WARNING, "Newer Users file found for " + w.getName() + ", but we have local changes!"); - throw new IllegalStateException("Unable to save unless you issue a '/mansave force'"); - } - } else { - //Check for newer file as no local changes. - if (w.getTimeStampUsers() < w.getUsersFile().lastModified()) { - System.out.print("Newer Users file found (Loading changes)!"); - // Backup Users file - backupFile(w,false); - w.reloadUsers(); - } - } + if (!mirrorsGroup.containsKey(w.getName().toLowerCase())) + if (w.haveGroupsChanged()) { + if (overwrite || (!overwrite && (w.getTimeStampGroups() >= w.getGroupsFile().lastModified()))) { + // Backup Groups file + backupFile(w,true); + + WorldDataHolder.writeGroups(w, w.getGroupsFile()); + //w.removeGroupsChangedFlag(); + } else { + // Newer file found. + GroupManager.logger.log(Level.WARNING, "Newer Groups file found for " + w.getName() + ", but we have local changes!"); + throw new IllegalStateException("Unable to save unless you issue a '/mansave force'"); + } + } else { + //Check for newer file as no local changes. + if (w.getTimeStampGroups() < w.getGroupsFile().lastModified()) { + System.out.print("Newer Groups file found (Loading changes)!"); + // Backup Groups file + backupFile(w,true); + w.reloadGroups(); + } + } + if (!mirrorsUser.containsKey(w.getName().toLowerCase())) + if (w.haveUsersChanged()) { + if (overwrite || (!overwrite && (w.getTimeStampUsers() >= w.getUsersFile().lastModified()))) { + // Backup Users file + backupFile(w,false); + + WorldDataHolder.writeUsers(w, w.getUsersFile()); + //w.removeUsersChangedFlag(); + } else { + // Newer file found. + GroupManager.logger.log(Level.WARNING, "Newer Users file found for " + w.getName() + ", but we have local changes!"); + throw new IllegalStateException("Unable to save unless you issue a '/mansave force'"); + } + } else { + //Check for newer file as no local changes. + if (w.getTimeStampUsers() < w.getUsersFile().lastModified()) { + System.out.print("Newer Users file found (Loading changes)!"); + // Backup Users file + backupFile(w,false); + w.reloadUsers(); + } + } alreadyDone.add(w); } } @@ -271,8 +323,8 @@ public class WorldsHolder { public OverloadedWorldHolder getWorldData(String worldName) { String worldNameLowered = worldName.toLowerCase(); // If a mirror change to the real world to load. - if (mirrors.containsKey(worldNameLowered)) { - worldNameLowered = mirrors.get(worldNameLowered); + if (mirrorsGroup.containsKey(worldNameLowered)) { + worldNameLowered = mirrorsGroup.get(worldNameLowered); } OverloadedWorldHolder data = worldsData.get(worldNameLowered); @@ -353,60 +405,45 @@ public class WorldsHolder { } - public void setupWorldFolder(String worldName) { - worldsFolder = new File(plugin.getDataFolder(), "worlds"); - if (!worldsFolder.exists()) { - worldsFolder.mkdirs(); - } - - File defaultWorldFolder = new File(worldsFolder, worldName); - if (!defaultWorldFolder.exists()) { - defaultWorldFolder.mkdirs(); - } - if (defaultWorldFolder.exists()) { - File groupsFile = new File(defaultWorldFolder, "groups.yml"); - File usersFile = new File(defaultWorldFolder, "users.yml"); - File oldDataFile = new File(plugin.getDataFolder(), "data.yml"); - if (!groupsFile.exists() || groupsFile.length() == 0) { - if (oldDataFile.exists()) { - try { - Tasks.copy(oldDataFile, groupsFile); - } catch (IOException ex) { - GroupManager.logger.log(Level.SEVERE, null, ex); - } - } else { - InputStream template = plugin.getResourceAsStream("groups.yml"); - try { - Tasks.copy(template, groupsFile); - } catch (IOException ex) { - GroupManager.logger.log(Level.SEVERE, null, ex); - } - } - } - if (!usersFile.exists() || usersFile.length() == 0) { - if (oldDataFile.exists()) { - try { - Tasks.copy(oldDataFile, usersFile); - } catch (IOException ex) { - GroupManager.logger.log(Level.SEVERE, null, ex); - } - } else { - InputStream template = plugin.getResourceAsStream("users.yml"); - try { - Tasks.copy(template, usersFile); - } catch (IOException ex) { - GroupManager.logger.log(Level.SEVERE, null, ex); - } - } - } - try { - if (oldDataFile.exists()) { - oldDataFile.renameTo(new File(plugin.getDataFolder(), "NOT_USED_ANYMORE_data.yml")); - } - } catch (Exception ex) { - } - } - } + public void setupWorldFolder(String worldName) { + worldsFolder = new File(plugin.getDataFolder(), "worlds"); + if (!worldsFolder.exists()) { + worldsFolder.mkdirs(); + } + + File defaultWorldFolder = new File(worldsFolder, worldName); + if ((!defaultWorldFolder.exists()) && ((!mirrorsGroup.containsKey(worldName.toLowerCase()))) || (!mirrorsUser.containsKey(worldName.toLowerCase()))) { + defaultWorldFolder.mkdirs(); + } + if (defaultWorldFolder.exists()) { + if (!mirrorsGroup.containsKey(worldName.toLowerCase())) { + File groupsFile = new File(defaultWorldFolder, "groups.yml"); + if (!groupsFile.exists() || groupsFile.length() == 0) { + + InputStream template = plugin.getResourceAsStream("groups.yml"); + try { + Tasks.copy(template, groupsFile); + } catch (IOException ex) { + GroupManager.logger.log(Level.SEVERE, null, ex); + } + } + } + + if (!mirrorsUser.containsKey(worldName.toLowerCase())) { + File usersFile = new File(defaultWorldFolder, "users.yml"); + if (!usersFile.exists() || usersFile.length() == 0) { + + InputStream template = plugin.getResourceAsStream("users.yml"); + try { + Tasks.copy(template, usersFile); + } catch (IOException ex) { + GroupManager.logger.log(Level.SEVERE, null, ex); + } + + } + } + } + } /** * Copies the specified world data to another world @@ -451,16 +488,36 @@ public class WorldsHolder { GroupManager.logger.finest("Trying to load world " + worldName + "..."); File thisWorldFolder = new File(worldsFolder, worldName); if (thisWorldFolder.exists() && thisWorldFolder.isDirectory()) { - File groupsFile = new File(thisWorldFolder, "groups.yml"); - File usersFile = new File(thisWorldFolder, "users.yml"); - if (!groupsFile.exists()) { + + // Setup file handles, if not mirrored + File groupsFile = (mirrorsGroup.containsKey(worldName.toLowerCase()))? null : new File(thisWorldFolder, "groups.yml"); + File usersFile = (mirrorsUser.containsKey(worldName.toLowerCase()))? null : new File(thisWorldFolder, "users.yml"); + + if ((groupsFile != null) && (!groupsFile.exists())) { throw new IllegalArgumentException("Groups file for world '" + worldName + "' doesnt exist: " + groupsFile.getPath()); } - if (!usersFile.exists()) { + if ((usersFile != null) && (!usersFile.exists())) { throw new IllegalArgumentException("Users file for world '" + worldName + "' doesnt exist: " + usersFile.getPath()); } - try { - OverloadedWorldHolder thisWorldData = new OverloadedWorldHolder(WorldDataHolder.load(worldName, groupsFile, usersFile)); + + WorldDataHolder tempHolder = new WorldDataHolder(worldName); + + // Map the group object for any mirror + if (mirrorsGroup.containsKey(worldName.toLowerCase())) + tempHolder.setGroupsObject(this.getWorldData(mirrorsGroup.get(worldName.toLowerCase())).getGroupsObject()); + else + tempHolder.loadGroups(groupsFile); + + // Map the user object for any mirror + if (mirrorsUser.containsKey(worldName.toLowerCase())) + tempHolder.setUsersObject(this.getWorldData(mirrorsUser.get(worldName.toLowerCase())).getUsersObject()); + else + tempHolder.loadUsers(usersFile); + + OverloadedWorldHolder thisWorldData = new OverloadedWorldHolder(tempHolder); + + // null the object so we don't keep file handles open where we shouldn't + tempHolder = null; // Set the file TimeStamps as it will be default from the initial load. thisWorldData.setTimeStamps(); @@ -470,13 +527,7 @@ public class WorldsHolder { worldsData.put(worldName.toLowerCase(), thisWorldData); return; } - } catch (FileNotFoundException ex) { - GroupManager.logger.log(Level.SEVERE, null, ex); - return; - } catch (IOException ex) { - GroupManager.logger.log(Level.SEVERE, null, ex); - return; - } + //GroupManager.logger.severe("Failed to load world " + worldName + "..."); } } @@ -490,7 +541,7 @@ public class WorldsHolder { * @return true if world is loaded or mirrored. false if not listed */ public boolean isInList(String worldName) { - if (worldsData.containsKey(worldName.toLowerCase()) || mirrors.containsKey(worldName.toLowerCase())) { + if (worldsData.containsKey(worldName.toLowerCase()) || mirrorsGroup.containsKey(worldName.toLowerCase())) { return true; } return false; diff --git a/EssentialsGroupManager/src/org/anjocaido/groupmanager/permissions/BukkitPermissions.java b/EssentialsGroupManager/src/org/anjocaido/groupmanager/permissions/BukkitPermissions.java index 7c157a2f7..ebaadf8bd 100644 --- a/EssentialsGroupManager/src/org/anjocaido/groupmanager/permissions/BukkitPermissions.java +++ b/EssentialsGroupManager/src/org/anjocaido/groupmanager/permissions/BukkitPermissions.java @@ -287,7 +287,7 @@ public class BukkitPermissions { * List all effective permissions for this player. * * @param player - * @return + * @return List of permissions */ public List listPerms(Player player) { List perms = new ArrayList(); From 256d202d50db621eb9b89fc29291fe9d111072c6 Mon Sep 17 00:00:00 2001 From: KHobbits Date: Fri, 13 Jan 2012 22:41:47 +0000 Subject: [PATCH 16/39] Adjusting default spawn behavior. Moving option in config file. --- .../src/com/earth2me/essentials/UserData.java | 17 ----------------- Essentials/src/config.yml | 12 ++++++------ .../essentials/spawn/EssentialsSpawn.java | 2 +- .../spawn/EssentialsSpawnPlayerListener.java | 18 +++++++++++++----- 4 files changed, 20 insertions(+), 29 deletions(-) diff --git a/Essentials/src/com/earth2me/essentials/UserData.java b/Essentials/src/com/earth2me/essentials/UserData.java index 9df1b0342..d08704484 100644 --- a/Essentials/src/com/earth2me/essentials/UserData.java +++ b/Essentials/src/com/earth2me/essentials/UserData.java @@ -53,7 +53,6 @@ public abstract class UserData extends PlayerExtension implements IConf lastLogout = _getLastLogout(); lastLoginAddress = _getLastLoginAddress(); afk = getAfk(); - newplayer = getNew(); geolocation = _getGeoLocation(); isSocialSpyEnabled = _isSocialSpyEnabled(); isNPC = _isNPC(); @@ -736,22 +735,6 @@ public abstract class UserData extends PlayerExtension implements IConf } private boolean newplayer; - private boolean getNew() - { - return config.getBoolean("newplayer", true); - } - - public boolean isNew() - { - return newplayer; - } - - public void setNew(boolean set) - { - newplayer = set; - config.setProperty("newplayer", set); - config.save(); - } private String geolocation; private String _getGeoLocation() diff --git a/Essentials/src/config.yml b/Essentials/src/config.yml index 2254bdbab..877ff0c42 100644 --- a/Essentials/src/config.yml +++ b/Essentials/src/config.yml @@ -253,10 +253,7 @@ register-back-in-listener: false # +------------------------------------------------------+ # ############################################################ -# When users die, should they respawn at their first home, instead of the spawnpoint or bed? -respawn-at-home: false - -# If no home is set send you to bed or spawn when /home is used +# If no home is set, send players to spawn when /home is used spawn-if-no-home: true # Allows people to set their bed at daytime @@ -532,8 +529,11 @@ newbies: spawnpoint: newbies # Set this to lowest, if you want Multiverse to handle the respawning -# Set this to normal, if you want EssentialsSpawn to handle the respawning +# Set this to high, if you want EssentialsSpawn to handle the respawning # Set this to highest, if you want to force EssentialsSpawn to handle the respawning -respawn-listener-priority: normal +respawn-listener-priority: high + +# When users die, should they respawn at their first home or bed, instead of the spawnpoint? +respawn-at-home: false # End of File <-- No seriously, you're done with configuration. diff --git a/EssentialsSpawn/src/com/earth2me/essentials/spawn/EssentialsSpawn.java b/EssentialsSpawn/src/com/earth2me/essentials/spawn/EssentialsSpawn.java index c4cd7c727..813220a16 100644 --- a/EssentialsSpawn/src/com/earth2me/essentials/spawn/EssentialsSpawn.java +++ b/EssentialsSpawn/src/com/earth2me/essentials/spawn/EssentialsSpawn.java @@ -39,7 +39,7 @@ public class EssentialsSpawn extends JavaPlugin final EssentialsSpawnPlayerListener playerListener = new EssentialsSpawnPlayerListener(ess, spawns); pluginManager.registerEvent(Type.PLAYER_RESPAWN, playerListener, ess.getSettings().getRespawnPriority(), this); - pluginManager.registerEvent(Type.PLAYER_JOIN, playerListener, Priority.Low, this); + pluginManager.registerEvent(Type.PLAYER_JOIN, playerListener, ess.getSettings().getRespawnPriority(), this); LOGGER.info(_("loadinfo", this.getDescription().getName(), this.getDescription().getVersion(), "essentials team")); } diff --git a/EssentialsSpawn/src/com/earth2me/essentials/spawn/EssentialsSpawnPlayerListener.java b/EssentialsSpawn/src/com/earth2me/essentials/spawn/EssentialsSpawnPlayerListener.java index 530a00faa..2a9efe4b1 100644 --- a/EssentialsSpawn/src/com/earth2me/essentials/spawn/EssentialsSpawnPlayerListener.java +++ b/EssentialsSpawn/src/com/earth2me/essentials/spawn/EssentialsSpawnPlayerListener.java @@ -4,6 +4,7 @@ import static com.earth2me.essentials.I18n._; import com.earth2me.essentials.IEssentials; import com.earth2me.essentials.User; import java.util.logging.Level; +import java.util.logging.Logger; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.event.player.PlayerJoinEvent; @@ -16,6 +17,7 @@ public class EssentialsSpawnPlayerListener extends PlayerListener { private final transient IEssentials ess; private final transient SpawnStorage spawns; + private static final Logger LOGGER = Bukkit.getLogger(); public EssentialsSpawnPlayerListener(final IEssentials ess, final SpawnStorage spawns) { @@ -26,7 +28,7 @@ public class EssentialsSpawnPlayerListener extends PlayerListener @Override public void onPlayerRespawn(final PlayerRespawnEvent event) - { + { final User user = ess.getUser(event.getPlayer()); if (ess.getSettings().getRespawnAtHome()) @@ -45,8 +47,12 @@ public class EssentialsSpawnPlayerListener extends PlayerListener final Location spawn = spawns.getSpawn(user.getGroup()); if (spawn != null) { + LOGGER.log(Level.INFO, "setting respawn location"); event.setRespawnLocation(spawn); } + else { + LOGGER.log(Level.INFO, "spawn was null"); + } } @Override @@ -54,20 +60,22 @@ public class EssentialsSpawnPlayerListener extends PlayerListener { final User user = ess.getUser(event.getPlayer()); - if (!user.isNew() || user.getBedSpawnLocation() != null) + if (user.hasPlayedBefore()) { + LOGGER.log(Level.FINE, "Old player join"); return; - } - user.setNew(false); + } if (!"none".equalsIgnoreCase(ess.getSettings().getNewbieSpawn())) { - ess.scheduleSyncDelayedTask(new NewPlayerTeleport(user)); + ess.scheduleSyncDelayedTask(new NewPlayerTeleport(user), 1L); } if (ess.getSettings().getAnnounceNewPlayers()) { ess.broadcastMessage(user, ess.getSettings().getAnnounceNewPlayerFormat(user)); } + + LOGGER.log(Level.FINE, "New player join"); } From 43f1c0f89d62cf3b273045d2d3b42b53a950bc56 Mon Sep 17 00:00:00 2001 From: KHobbits Date: Fri, 13 Jan 2012 22:50:48 +0000 Subject: [PATCH 17/39] Removing debugging messages. --- .../essentials/spawn/EssentialsSpawnPlayerListener.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/EssentialsSpawn/src/com/earth2me/essentials/spawn/EssentialsSpawnPlayerListener.java b/EssentialsSpawn/src/com/earth2me/essentials/spawn/EssentialsSpawnPlayerListener.java index 2a9efe4b1..81e8f6e9e 100644 --- a/EssentialsSpawn/src/com/earth2me/essentials/spawn/EssentialsSpawnPlayerListener.java +++ b/EssentialsSpawn/src/com/earth2me/essentials/spawn/EssentialsSpawnPlayerListener.java @@ -47,12 +47,8 @@ public class EssentialsSpawnPlayerListener extends PlayerListener final Location spawn = spawns.getSpawn(user.getGroup()); if (spawn != null) { - LOGGER.log(Level.INFO, "setting respawn location"); event.setRespawnLocation(spawn); } - else { - LOGGER.log(Level.INFO, "spawn was null"); - } } @Override From 856cef32862ba9c15d45b85e00c69fba624c9aa5 Mon Sep 17 00:00:00 2001 From: KHobbits Date: Sat, 14 Jan 2012 15:04:16 +0000 Subject: [PATCH 18/39] Update Essentials/src/com/earth2me/essentials/commands/Commanditemdb.java --- .../src/com/earth2me/essentials/commands/Commanditemdb.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Essentials/src/com/earth2me/essentials/commands/Commanditemdb.java b/Essentials/src/com/earth2me/essentials/commands/Commanditemdb.java index 2fff20e8c..c35656c72 100644 --- a/Essentials/src/com/earth2me/essentials/commands/Commanditemdb.java +++ b/Essentials/src/com/earth2me/essentials/commands/Commanditemdb.java @@ -10,7 +10,7 @@ public class Commanditemdb extends EssentialsCommand { public Commanditemdb() { - super("find"); + super("itemdb"); } @Override From d0e5685ac0afb7bb69df9a17aafe0f78f5fc5aef Mon Sep 17 00:00:00 2001 From: ElgarL Date: Sat, 14 Jan 2012 15:48:55 +0000 Subject: [PATCH 19/39] Catch NullPointerErrors generated by blank permission nodes. --- EssentialsGroupManager/src/Changelog.txt | 3 ++- .../groupmanager/dataholder/WorldDataHolder.java | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/EssentialsGroupManager/src/Changelog.txt b/EssentialsGroupManager/src/Changelog.txt index 364ae5952..2ed032267 100644 --- a/EssentialsGroupManager/src/Changelog.txt +++ b/EssentialsGroupManager/src/Changelog.txt @@ -99,4 +99,5 @@ v 1.8: - Fix 'manucheckp' to correctly report if a permission is available from GroupManager or Bukkit. - Changed over to a reflection method for populating superperms as Bukkit lags when you handle permissions one at a time. - Major, MAJOR changes to support partial/full world mirroring. - You can now mirror groups.yml, users.yml or both files between different worlds. \ No newline at end of file + You can now mirror groups.yml, users.yml or both files between different worlds. + - Catch NullPointerErrors generated by blank permission nodes. \ No newline at end of file diff --git a/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/WorldDataHolder.java b/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/WorldDataHolder.java index 73c4dca0f..c9c1d2b64 100644 --- a/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/WorldDataHolder.java +++ b/EssentialsGroupManager/src/org/anjocaido/groupmanager/dataholder/WorldDataHolder.java @@ -470,7 +470,12 @@ public class WorldDataHolder { } if (thisGroupNode.get("permissions") instanceof List) { for (Object o : ((List) thisGroupNode.get("permissions"))) { - thisGrp.addPermission(o.toString()); + try { + thisGrp.addPermission(o.toString()); + } catch (NullPointerException e) { + // Ignore this entry as it's null. + //throw new IllegalArgumentException("Invalid permission node in group: " + thisGrp.getName() + " in file: " + groupsFile.getPath()); + } } } else if (thisGroupNode.get("permissions") instanceof String) { thisGrp.addPermission((String) thisGroupNode.get("permissions")); @@ -582,7 +587,12 @@ public class WorldDataHolder { thisUser.addPermission(o.toString()); } } else if (thisUserNode.get("permissions") instanceof String) { - thisUser.addPermission(thisUserNode.get("permissions").toString()); + try { + thisUser.addPermission(thisUserNode.get("permissions").toString()); + } catch (NullPointerException e) { + // Ignore this entry as it's null. + //throw new IllegalArgumentException("Invalid permission node for user: " + thisUser.getName() + " in file: " + UserFile.getPath()); + } } //SUBGROUPS LOADING From 29d4e09983a6c4602e0a8d5fbf6bf716da9ae0db Mon Sep 17 00:00:00 2001 From: KHobbits Date: Sat, 14 Jan 2012 16:36:19 +0000 Subject: [PATCH 20/39] Fixing {WorldDate} Test #1353 --- .../src/com/earth2me/essentials/textreader/KeywordReplacer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Essentials/src/com/earth2me/essentials/textreader/KeywordReplacer.java b/Essentials/src/com/earth2me/essentials/textreader/KeywordReplacer.java index 182dba9d3..782798bff 100644 --- a/Essentials/src/com/earth2me/essentials/textreader/KeywordReplacer.java +++ b/Essentials/src/com/earth2me/essentials/textreader/KeywordReplacer.java @@ -41,7 +41,7 @@ public class KeywordReplacer implements IText world = user.getLocation().getWorld().getName(); worldTime12 = DescParseTickFormat.format12(user.getWorld().getTime()); worldTime24 = DescParseTickFormat.format24(user.getWorld().getTime()); - worldDate = DateFormat.getDateInstance(DateFormat.MEDIUM, ess.getI18n().getCurrentLocale()).format(DescParseTickFormat.ticksToDate(user.getWorld().getTime())); + worldDate = DateFormat.getDateInstance(DateFormat.MEDIUM, ess.getI18n().getCurrentLocale()).format(DescParseTickFormat.ticksToDate(user.getWorld().getFullTime())); } else { From 3f6b9586b4da7fc5d9f2ec0a9c2d76d28b1b5a7c Mon Sep 17 00:00:00 2001 From: KHobbits Date: Sat, 14 Jan 2012 16:42:53 +0000 Subject: [PATCH 21/39] Fixing double charges on /home Test #1426 --- .../src/com/earth2me/essentials/commands/Commandhome.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Essentials/src/com/earth2me/essentials/commands/Commandhome.java b/Essentials/src/com/earth2me/essentials/commands/Commandhome.java index d1db6c523..55bd75d0c 100644 --- a/Essentials/src/com/earth2me/essentials/commands/Commandhome.java +++ b/Essentials/src/com/earth2me/essentials/commands/Commandhome.java @@ -50,7 +50,7 @@ public class Commandhome extends EssentialsCommand if (bed != null) { user.getTeleport().teleport(bed, charge, TeleportCause.COMMAND); - return; + throw new NoChargeException(); } } user.getTeleport().home(player, homeName.toLowerCase(Locale.ENGLISH), charge); @@ -64,10 +64,10 @@ public class Commandhome extends EssentialsCommand if (bed != null) { user.getTeleport().teleport(bed, charge, TeleportCause.COMMAND); - return; + throw new NoChargeException(); } user.getTeleport().respawn(charge, TeleportCause.COMMAND); - return; + } else if (homes.isEmpty()) { @@ -76,7 +76,6 @@ public class Commandhome extends EssentialsCommand else if (homes.size() == 1 && player.equals(user)) { user.getTeleport().home(player, homes.get(0), charge); - return; } else { From 6e82419c1573b7f8ee912862cedc65e83af2b93d Mon Sep 17 00:00:00 2001 From: KHobbits Date: Sat, 14 Jan 2012 16:53:18 +0000 Subject: [PATCH 22/39] Allowing use of aliases in help/info/motd, for command suggestions. Test #1441 --- .../src/com/earth2me/essentials/commands/Commandhelp.java | 2 +- .../src/com/earth2me/essentials/commands/Commandinfo.java | 2 +- .../src/com/earth2me/essentials/commands/Commandmotd.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Essentials/src/com/earth2me/essentials/commands/Commandhelp.java b/Essentials/src/com/earth2me/essentials/commands/Commandhelp.java index 328f692ea..769aac483 100644 --- a/Essentials/src/com/earth2me/essentials/commands/Commandhelp.java +++ b/Essentials/src/com/earth2me/essentials/commands/Commandhelp.java @@ -41,7 +41,7 @@ public class Commandhelp extends EssentialsCommand output = new KeywordReplacer(input, user, ess); } final TextPager pager = new TextPager(output); - pager.showPage(pageStr, chapterPageStr, "help", user); + pager.showPage(pageStr, chapterPageStr, commandLabel, user); } @Override diff --git a/Essentials/src/com/earth2me/essentials/commands/Commandinfo.java b/Essentials/src/com/earth2me/essentials/commands/Commandinfo.java index e75eeed9a..afe1add72 100644 --- a/Essentials/src/com/earth2me/essentials/commands/Commandinfo.java +++ b/Essentials/src/com/earth2me/essentials/commands/Commandinfo.java @@ -21,6 +21,6 @@ public class Commandinfo extends EssentialsCommand final IText input = new TextInput(sender, "info", true, ess); final IText output = new KeywordReplacer(input, sender, ess); final TextPager pager = new TextPager(output); - pager.showPage(args.length > 0 ? args[0] : null, args.length > 1 ? args[1] : null, "info", sender); + pager.showPage(args.length > 0 ? args[0] : null, args.length > 1 ? args[1] : null, commandLabel, sender); } } diff --git a/Essentials/src/com/earth2me/essentials/commands/Commandmotd.java b/Essentials/src/com/earth2me/essentials/commands/Commandmotd.java index 78ca5c306..0031504e8 100644 --- a/Essentials/src/com/earth2me/essentials/commands/Commandmotd.java +++ b/Essentials/src/com/earth2me/essentials/commands/Commandmotd.java @@ -21,6 +21,6 @@ public class Commandmotd extends EssentialsCommand final IText input = new TextInput(sender, "motd", true, ess); final IText output = new KeywordReplacer(input, sender, ess); final TextPager pager = new TextPager(output); - pager.showPage(args.length > 0 ? args[0] : null, args.length > 1 ? args[1] : null, "motd", sender); + pager.showPage(args.length > 0 ? args[0] : null, args.length > 1 ? args[1] : null, commandLabel, sender); } } From c584d74852b219b8f217a909aa124e3a0c63c30a Mon Sep 17 00:00:00 2001 From: snowleo Date: Sat, 14 Jan 2012 23:13:58 +0100 Subject: [PATCH 23/39] Temporary fix for Spawns and Jails when Worlds are loaded after Essentials. This will be replaced by BetterLocation fix in 3.0 branch. --- .../com/earth2me/essentials/Essentials.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/Essentials/src/com/earth2me/essentials/Essentials.java b/Essentials/src/com/earth2me/essentials/Essentials.java index fe550279e..223363c3a 100644 --- a/Essentials/src/com/earth2me/essentials/Essentials.java +++ b/Essentials/src/com/earth2me/essentials/Essentials.java @@ -49,6 +49,9 @@ import org.bukkit.event.Event.Priority; import org.bukkit.event.Event.Type; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerListener; +import org.bukkit.event.world.WorldListener; +import org.bukkit.event.world.WorldLoadEvent; +import org.bukkit.event.world.WorldUnloadEvent; import org.bukkit.plugin.InvalidDescriptionException; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginDescriptionFile; @@ -242,6 +245,10 @@ public class Essentials extends JavaPlugin implements IEssentials pm.registerEvent(Type.ENTITY_DEATH, entityListener, Priority.Lowest, this); pm.registerEvent(Type.ENTITY_REGAIN_HEALTH, entityListener, Priority.Lowest, this); pm.registerEvent(Type.FOOD_LEVEL_CHANGE, entityListener, Priority.Lowest, this); + + final EssentialsWorldListener worldListener = new EssentialsWorldListener(this); + pm.registerEvent(Type.WORLD_LOAD, worldListener, Priority.Monitor, this); + pm.registerEvent(Type.WORLD_UNLOAD, worldListener, Priority.Monitor, this); //TODO: Check if this should be here, and not above before reload() jails = new Jails(this); @@ -594,4 +601,32 @@ public class Essentials extends JavaPlugin implements IEssentials { return i18n; } + + private static class EssentialsWorldListener extends WorldListener implements Runnable { + private transient final IEssentials ess; + + public EssentialsWorldListener(IEssentials ess) + { + this.ess = ess; + } + + + @Override + public void onWorldLoad(WorldLoadEvent event) + { + ess.scheduleSyncDelayedTask(this); + } + + @Override + public void onWorldUnload(WorldUnloadEvent event) + { + ess.scheduleSyncDelayedTask(this); + } + + @Override + public void run() + { + ess.reload(); + } + } } From c860b1c6685240222ad10c94ca8f873a21148ac9 Mon Sep 17 00:00:00 2001 From: snowleo Date: Sat, 14 Jan 2012 23:25:52 +0100 Subject: [PATCH 24/39] Reload less --- .../com/earth2me/essentials/Essentials.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/Essentials/src/com/earth2me/essentials/Essentials.java b/Essentials/src/com/earth2me/essentials/Essentials.java index 223363c3a..5287e299b 100644 --- a/Essentials/src/com/earth2me/essentials/Essentials.java +++ b/Essentials/src/com/earth2me/essentials/Essentials.java @@ -614,13 +614,27 @@ public class Essentials extends JavaPlugin implements IEssentials @Override public void onWorldLoad(WorldLoadEvent event) { - ess.scheduleSyncDelayedTask(this); + ess.getJails().onReload(); + ess.getWarps().reloadConfig(); + for (IConf iConf : ((Essentials)ess).confList) + { + if (iConf instanceof IEssentialsModule) { + iConf.reloadConfig(); + } + } } @Override public void onWorldUnload(WorldUnloadEvent event) { - ess.scheduleSyncDelayedTask(this); + ess.getJails().onReload(); + ess.getWarps().reloadConfig(); + for (IConf iConf : ((Essentials)ess).confList) + { + if (iConf instanceof IEssentialsModule) { + iConf.reloadConfig(); + } + } } @Override From 1a0b03db4dfb814258b3ce4b45c52e8c090773c7 Mon Sep 17 00:00:00 2001 From: ElgarL Date: Sun, 15 Jan 2012 01:43:42 +0000 Subject: [PATCH 25/39] Removed '- bukkit.command' form the globalgroups permission nodes. --- EssentialsGroupManager/src/Changelog.txt | 3 ++- EssentialsGroupManager/src/globalgroups.yml | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/EssentialsGroupManager/src/Changelog.txt b/EssentialsGroupManager/src/Changelog.txt index 2ed032267..71a4c3482 100644 --- a/EssentialsGroupManager/src/Changelog.txt +++ b/EssentialsGroupManager/src/Changelog.txt @@ -100,4 +100,5 @@ v 1.8: - Changed over to a reflection method for populating superperms as Bukkit lags when you handle permissions one at a time. - Major, MAJOR changes to support partial/full world mirroring. You can now mirror groups.yml, users.yml or both files between different worlds. - - Catch NullPointerErrors generated by blank permission nodes. \ No newline at end of file + - Catch NullPointerErrors generated by blank permission nodes. + - Removed '- bukkit.command' form the globalgroups permission nodes. \ No newline at end of file diff --git a/EssentialsGroupManager/src/globalgroups.yml b/EssentialsGroupManager/src/globalgroups.yml index c9ecc2673..f21034237 100644 --- a/EssentialsGroupManager/src/globalgroups.yml +++ b/EssentialsGroupManager/src/globalgroups.yml @@ -145,7 +145,6 @@ groups: permissions: - bukkit.broadcast - bukkit.broadcast.admin - - bukkit.command - bukkit.command.give - bukkit.command.help - bukkit.command.kill From 359ea194b8d5348b5d2f0b3c67f83ee7a6539513 Mon Sep 17 00:00:00 2001 From: snowleo Date: Mon, 16 Jan 2012 00:50:52 +0100 Subject: [PATCH 26/39] Speed improvements for Move and Interact Event. --- .../essentials/EssentialsPlayerListener.java | 20 +-- .../src/com/earth2me/essentials/User.java | 125 +++++++++--------- .../src/com/earth2me/essentials/UserData.java | 5 + .../src/com/earth2me/essentials/UserMap.java | 77 ++++++----- .../src/com/earth2me/essentials/Util.java | 5 +- 5 files changed, 128 insertions(+), 104 deletions(-) diff --git a/Essentials/src/com/earth2me/essentials/EssentialsPlayerListener.java b/Essentials/src/com/earth2me/essentials/EssentialsPlayerListener.java index bb6741d30..9edc80364 100644 --- a/Essentials/src/com/earth2me/essentials/EssentialsPlayerListener.java +++ b/Essentials/src/com/earth2me/essentials/EssentialsPlayerListener.java @@ -282,22 +282,22 @@ public class EssentialsPlayerListener extends PlayerListener { final User user = ess.getUser(event.getPlayer()); user.updateActivity(true); - usePowertools(event); + if (event.getAnimationType() == PlayerAnimationType.ARM_SWING + && user.hasPowerTools() && user.arePowerToolsEnabled()) + { + usePowertools(user); + } } - private void usePowertools(final PlayerAnimationEvent event) + private void usePowertools(final User user) { - if (event.getAnimationType() != PlayerAnimationType.ARM_SWING) - { - return; - } - final User user = ess.getUser(event.getPlayer()); final ItemStack is = user.getItemInHand(); - if (is == null || is.getType() == Material.AIR || !user.arePowerToolsEnabled()) + int id; + if (is == null || (id = is.getTypeId()) == 0) { return; } - final List commandList = user.getPowertool(is); + final List commandList = user.getPowertool(id); if (commandList == null || commandList.isEmpty()) { return; @@ -317,7 +317,7 @@ public class EssentialsPlayerListener extends PlayerListener } else { - user.getServer().dispatchCommand(event.getPlayer(), command); + user.getServer().dispatchCommand(user.getBase(), command); } } } diff --git a/Essentials/src/com/earth2me/essentials/User.java b/Essentials/src/com/earth2me/essentials/User.java index daf756e5b..e23fe1de0 100644 --- a/Essentials/src/com/earth2me/essentials/User.java +++ b/Essentials/src/com/earth2me/essentials/User.java @@ -23,34 +23,37 @@ public class User extends UserData implements Comparable, IReplyTo, IUser private transient long lastOnlineActivity; private transient long lastActivity = System.currentTimeMillis(); private boolean hidden = false; - private transient Location afkPosition; + private transient Location afkPosition = null; private static final Logger logger = Logger.getLogger("Minecraft"); - + User(final Player base, final IEssentials ess) { super(base, ess); teleport = new Teleport(this, ess); - afkPosition = getLocation(); + if (isAfk()) + { + afkPosition = getLocation(); + } } - + User update(final Player base) { setBase(base); return this; } - + @Override public boolean isAuthorized(final IEssentialsCommand cmd) { return isAuthorized(cmd, "essentials."); } - + @Override public boolean isAuthorized(final IEssentialsCommand cmd, final String permissionPrefix) { return isAuthorized(permissionPrefix + (cmd.getName().equals("r") ? "msg" : cmd.getName())); } - + @Override public boolean isAuthorized(final String node) { @@ -58,20 +61,20 @@ public class User extends UserData implements Comparable, IReplyTo, IUser { return false; } - + if (isOp()) { return true; } - + if (isJailed()) { return false; } - + return ess.getPermissionsHandler().hasPermission(base, node); } - + public void healCooldown() throws Exception { final Calendar now = new GregorianCalendar(); @@ -89,13 +92,13 @@ public class User extends UserData implements Comparable, IReplyTo, IUser } setLastHealTimestamp(now.getTimeInMillis()); } - + @Override public void giveMoney(final double value) { giveMoney(value, null); } - + public void giveMoney(final double value, final CommandSender initiator) { if (value == 0) @@ -109,7 +112,7 @@ public class User extends UserData implements Comparable, IReplyTo, IUser initiator.sendMessage(_("addedToOthersAccount", Util.formatCurrency(value, ess), this.getDisplayName())); } } - + public void payUser(final User reciever, final double value) throws Exception { if (value == 0) @@ -128,13 +131,13 @@ public class User extends UserData implements Comparable, IReplyTo, IUser throw new Exception(_("notEnoughMoney")); } } - + @Override public void takeMoney(final double value) { takeMoney(value, null); } - + public void takeMoney(final double value, final CommandSender initiator) { if (value == 0) @@ -148,36 +151,36 @@ public class User extends UserData implements Comparable, IReplyTo, IUser initiator.sendMessage(_("takenFromOthersAccount", Util.formatCurrency(value, ess), this.getDisplayName())); } } - + public boolean canAfford(final double cost) { final double mon = getMoney(); return mon >= cost || isAuthorized("essentials.eco.loan"); } - + public void dispose() { this.base = new OfflinePlayer(getName(), ess); } - + @Override public void setReplyTo(final CommandSender user) { replyTo = user; } - + @Override public CommandSender getReplyTo() { return replyTo; } - + @Override public int compareTo(final User other) { return Util.stripColor(this.getDisplayName()).compareToIgnoreCase(Util.stripColor(other.getDisplayName())); } - + @Override public boolean equals(final Object object) { @@ -186,58 +189,58 @@ public class User extends UserData implements Comparable, IReplyTo, IUser return false; } return this.getName().equalsIgnoreCase(((User)object).getName()); - + } - + @Override public int hashCode() { return this.getName().hashCode(); } - + public Boolean canSpawnItem(final int itemId) { return !ess.getSettings().itemSpawnBlacklist().contains(itemId); } - + public Location getHome() throws Exception { return getHome(getHomes().get(0)); } - + public void setHome() { setHome("home", getLocation()); } - + public void setHome(final String name) { setHome(name, getLocation()); } - + @Override public void setLastLocation() { setLastLocation(getLocation()); } - + public void requestTeleport(final User player, final boolean here) { teleportRequestTime = System.currentTimeMillis(); teleportRequester = player; teleportRequestHere = here; } - + public User getTeleportRequest() { return teleportRequester; } - + public boolean isTeleportRequestHere() { return teleportRequestHere; } - + public String getNick(boolean addprefixsuffix) { final StringBuilder nickname = new StringBuilder(); @@ -261,7 +264,7 @@ public class User extends UserData implements Comparable, IReplyTo, IUser { } } - + if (addprefixsuffix && ess.getSettings().addPrefixSuffix()) { if (!ess.getSettings().disablePrefix()) @@ -283,10 +286,10 @@ public class User extends UserData implements Comparable, IReplyTo, IUser nickname.append("§f"); } } - + return nickname.toString(); } - + public void setDisplayNick() { String name = getNick(true); @@ -308,7 +311,7 @@ public class User extends UserData implements Comparable, IReplyTo, IUser logger.log(Level.INFO, "Playerlist for " + name + " was not updated. Use a shorter displayname prefix."); } } - + @Override public String getDisplayName() { @@ -318,22 +321,22 @@ public class User extends UserData implements Comparable, IReplyTo, IUser } return super.getDisplayName() == null ? super.getName() : super.getDisplayName(); } - + public Teleport getTeleport() { return teleport; } - + public long getLastOnlineActivity() { return lastOnlineActivity; } - + public void setLastOnlineActivity(final long timestamp) { lastOnlineActivity = timestamp; } - + @Override public double getMoney() { @@ -355,7 +358,7 @@ public class User extends UserData implements Comparable, IReplyTo, IUser } return super.getMoney(); } - + @Override public void setMoney(final double value) { @@ -377,7 +380,7 @@ public class User extends UserData implements Comparable, IReplyTo, IUser } super.setMoney(value); } - + @Override public void setAfk(final boolean set) { @@ -386,9 +389,13 @@ public class User extends UserData implements Comparable, IReplyTo, IUser { afkPosition = getLocation(); } + else if (!set && isAfk()) + { + afkPosition = null; + } super.setAfk(set); } - + @Override public boolean toggleAfk() { @@ -396,13 +403,13 @@ public class User extends UserData implements Comparable, IReplyTo, IUser this.setSleepingIgnored(this.isAuthorized("essentials.sleepingignored") ? true : now); return now; } - + @Override public boolean isHidden() { return hidden; } - + public void setHidden(final boolean hidden) { this.hidden = hidden; @@ -453,7 +460,7 @@ public class User extends UserData implements Comparable, IReplyTo, IUser } return false; } - + public void updateActivity(final boolean broadcast) { if (isAfk()) @@ -466,7 +473,7 @@ public class User extends UserData implements Comparable, IReplyTo, IUser } lastActivity = System.currentTimeMillis(); } - + public void checkActivity() { final long autoafkkick = ess.getSettings().getAutoAfkKick(); @@ -476,8 +483,8 @@ public class User extends UserData implements Comparable, IReplyTo, IUser final String kickReason = _("autoAfkKickReason", autoafkkick / 60.0); lastActivity = 0; kickPlayer(kickReason); - - + + for (Player player : ess.getServer().getOnlinePlayers()) { final User user = ess.getUser(player); @@ -497,12 +504,12 @@ public class User extends UserData implements Comparable, IReplyTo, IUser } } } - + public Location getAfkPosition() { return afkPosition; } - + @Override public boolean toggleGodModeEnabled() { @@ -512,36 +519,36 @@ public class User extends UserData implements Comparable, IReplyTo, IUser } return super.toggleGodModeEnabled(); } - + @Override public boolean isGodModeEnabled() { return (super.isGodModeEnabled() && !ess.getSettings().getNoGodWorlds().contains(getLocation().getWorld().getName())) || (isAfk() && ess.getSettings().getFreezeAfkPlayers()); } - + public boolean isGodModeEnabledRaw() { return super.isGodModeEnabled(); } - + public String getGroup() { return ess.getPermissionsHandler().getGroup(base); } - + public boolean inGroup(final String group) { return ess.getPermissionsHandler().inGroup(base, group); } - + public boolean canBuild() { return ess.getPermissionsHandler().canBuild(base, getGroup()); } - + public long getTeleportRequestTime() { return teleportRequestTime; - } + } } diff --git a/Essentials/src/com/earth2me/essentials/UserData.java b/Essentials/src/com/earth2me/essentials/UserData.java index d08704484..e2a365ad2 100644 --- a/Essentials/src/com/earth2me/essentials/UserData.java +++ b/Essentials/src/com/earth2me/essentials/UserData.java @@ -264,6 +264,11 @@ public abstract class UserData extends PlayerExtension implements IConf { return (List)powertools.get(stack.getTypeId()); } + + public List getPowertool(int id) + { + return (List)powertools.get(id); + } public void setPowertool(ItemStack stack, List commandList) { diff --git a/Essentials/src/com/earth2me/essentials/UserMap.java b/Essentials/src/com/earth2me/essentials/UserMap.java index f6b75c3a5..c979e5046 100644 --- a/Essentials/src/com/earth2me/essentials/UserMap.java +++ b/Essentials/src/com/earth2me/essentials/UserMap.java @@ -1,23 +1,20 @@ package com.earth2me.essentials; -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.util.concurrent.UncheckedExecutionException; import java.io.File; -import java.util.Collections; -import java.util.Locale; +import java.lang.ref.SoftReference; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; import java.util.Set; -import java.util.concurrent.ConcurrentSkipListSet; -import java.util.concurrent.ExecutionException; import org.bukkit.entity.Player; -public class UserMap extends CacheLoader implements IConf +public class UserMap implements IConf { private final transient IEssentials ess; - private final transient Cache users = CacheBuilder.newBuilder().softValues().build(this); - private final transient ConcurrentSkipListSet keys = new ConcurrentSkipListSet(); + private final transient Map> users = new HashMap>(); + //CacheBuilder.newBuilder().softValues().build(this); + //private final transient ConcurrentSkipListSet keys = new ConcurrentSkipListSet(); public UserMap(final IEssentials ess) { @@ -38,16 +35,19 @@ public class UserMap extends CacheLoader implements IConf { return; } - keys.clear(); - users.invalidateAll(); - for (String string : userdir.list()) + synchronized (users) { - if (!string.endsWith(".yml")) + users.clear(); + + for (String string : userdir.list()) { - continue; + if (!string.endsWith(".yml")) + { + continue; + } + final String name = string.substring(0, string.length() - 4); + users.put(Util.sanitizeFileName(name), null); } - final String name = string.substring(0, string.length() - 4); - keys.add(Util.sanitizeFileName(name)); } } }); @@ -55,40 +55,43 @@ public class UserMap extends CacheLoader implements IConf public boolean userExists(final String name) { - return keys.contains(Util.sanitizeFileName(name)); + return users.containsKey(Util.sanitizeFileName(name)); } public User getUser(final String name) { try { - return users.get(Util.sanitizeFileName(name)); + synchronized (users) + { + final SoftReference softRef = users.get(Util.sanitizeFileName(name)); + User user = softRef == null ? null : softRef.get(); + if (user == null) + { + user = load(name); + users.put(name, new SoftReference(user)); + } + return user; + } } - catch (ExecutionException ex) - { - return null; - } - catch (UncheckedExecutionException ex) + catch (Exception ex) { return null; } } - @Override public User load(final String name) throws Exception { for (Player player : ess.getServer().getOnlinePlayers()) { if (player.getName().equalsIgnoreCase(name)) { - keys.add(Util.sanitizeFileName(name)); return new User(player, ess); } } final File userFile = getUserFile(name); if (userFile.exists()) { - keys.add(Util.sanitizeFileName(name)); return new User(new OfflinePlayer(name, ess), ess); } throw new Exception("User not found!"); @@ -102,20 +105,28 @@ public class UserMap extends CacheLoader implements IConf public void removeUser(final String name) { - keys.remove(Util.sanitizeFileName(name)); - users.invalidate(Util.sanitizeFileName(name)); + synchronized (users) + { + users.remove(Util.sanitizeFileName(name)); + } } public Set getAllUniqueUsers() { - return Collections.unmodifiableSet(keys); + synchronized (users) + { + return new HashSet(users.keySet()); + } } public int getUniqueUsers() { - return keys.size(); + synchronized (users) + { + return users.size(); + } } - + public File getUserFile(final String name) { final File userFolder = new File(ess.getDataFolder(), "userdata"); diff --git a/Essentials/src/com/earth2me/essentials/Util.java b/Essentials/src/com/earth2me/essentials/Util.java index 6a1df197f..a8a0cb1e4 100644 --- a/Essentials/src/com/earth2me/essentials/Util.java +++ b/Essentials/src/com/earth2me/essentials/Util.java @@ -21,10 +21,11 @@ public class Util { } private final static Logger logger = Logger.getLogger("Minecraft"); + private final static Pattern INVALIDCHARS = Pattern.compile("[^a-z0-9]"); - public static String sanitizeFileName(String name) + public static String sanitizeFileName(final String name) { - return name.toLowerCase(Locale.ENGLISH).replaceAll("[^a-z0-9]", "_"); + return INVALIDCHARS.matcher(name.toLowerCase(Locale.ENGLISH)).replaceAll("_"); } public static String formatDateDiff(long date) From 91563e9dca63e8a850be0ac5accb70d10efa8b3d Mon Sep 17 00:00:00 2001 From: snowleo Date: Mon, 16 Jan 2012 01:55:04 +0100 Subject: [PATCH 27/39] Optimize Break in Protect --- .../EssentialsProtectBlockListener.java | 62 +++++++++++++++++-- 1 file changed, 58 insertions(+), 4 deletions(-) diff --git a/EssentialsProtect/src/com/earth2me/essentials/protect/EssentialsProtectBlockListener.java b/EssentialsProtect/src/com/earth2me/essentials/protect/EssentialsProtectBlockListener.java index 03ad19cb4..a1d16af25 100644 --- a/EssentialsProtect/src/com/earth2me/essentials/protect/EssentialsProtectBlockListener.java +++ b/EssentialsProtect/src/com/earth2me/essentials/protect/EssentialsProtectBlockListener.java @@ -56,7 +56,7 @@ public class EssentialsProtectBlockListener extends BlockListener final Block below = blockPlaced.getRelative(BlockFace.DOWN); if ((below.getType() == Material.RAILS || below.getType() == Material.POWERED_RAIL || below.getType() == Material.DETECTOR_RAIL) && prot.getSettingBool(ProtectConfig.prevent_block_on_rail) - && prot.getStorage().isProtected(below, user.getName())) + && isProtected(below, user)) { event.setCancelled(true); return; @@ -69,7 +69,7 @@ public class EssentialsProtectBlockListener extends BlockListener { protect.add(blockPlaced); if (prot.getSettingBool(ProtectConfig.protect_below_rails) - && !prot.getStorage().isProtected(blockPlaced.getRelative(BlockFace.DOWN), user.getName())) + && !isProtected(blockPlaced.getRelative(BlockFace.DOWN), user)) { protect.add(blockPlaced.getRelative(BlockFace.DOWN)); } @@ -82,7 +82,7 @@ public class EssentialsProtectBlockListener extends BlockListener if (prot.getSettingBool(ProtectConfig.protect_against_signs) && event.getBlockAgainst().getType() != Material.SIGN_POST && event.getBlockAgainst().getType() != Material.WALL_SIGN - && !prot.getStorage().isProtected(event.getBlockAgainst(), user.getName())) + && !isProtected(event.getBlockAgainst(), user)) { protect.add(event.getBlockAgainst()); } @@ -283,7 +283,7 @@ public class EssentialsProtectBlockListener extends BlockListener else { - final boolean isProtected = storage.isProtected(block, user.getName()); + final boolean isProtected = isProtected(block, user); if (isProtected) { event.setCancelled(true); @@ -422,4 +422,58 @@ public class EssentialsProtectBlockListener extends BlockListener } } } + + private boolean isProtected(final Block block, final User user) + { + final Material type = block.getType(); + if (prot.getSettingBool(ProtectConfig.protect_signs)) + { + if (type == Material.WALL_SIGN || type == Material.SIGN_POST) + { + return prot.getStorage().isProtected(block, user.getName()); + } + if (prot.getSettingBool(ProtectConfig.protect_against_signs)) + { + final Block up = block.getRelative(BlockFace.UP); + if (up != null && up.getType() == Material.SIGN_POST) + { + return prot.getStorage().isProtected(block, user.getName()); + } + final BlockFace[] directions = new BlockFace[] + { + BlockFace.NORTH, + BlockFace.EAST, + BlockFace.SOUTH, + BlockFace.WEST + }; + for (BlockFace blockFace : directions) + { + final Block signblock = block.getRelative(blockFace); + if (signblock.getType() == Material.WALL_SIGN) + { + final org.bukkit.material.Sign signMat = (org.bukkit.material.Sign)signblock.getState().getData(); + if (signMat != null && signMat.getFacing() == blockFace) + { + return prot.getStorage().isProtected(block, user.getName()); + } + } + } + } + } + if (prot.getSettingBool(ProtectConfig.protect_rails)) { + if (type == Material.RAILS || type == Material.POWERED_RAIL || type == Material.DETECTOR_RAIL) + { + return prot.getStorage().isProtected(block, user.getName()); + } + if (prot.getSettingBool(ProtectConfig.protect_below_rails)) + { + final Block up = block.getRelative(BlockFace.UP); + if (up != null && (type == Material.RAILS || type == Material.POWERED_RAIL || type == Material.DETECTOR_RAIL)) + { + return prot.getStorage().isProtected(block, user.getName()); + } + } + } + return false; + } } From 81ec87d893db63a749828fba9af851ed07076cca Mon Sep 17 00:00:00 2001 From: snowleo Date: Mon, 16 Jan 2012 02:12:20 +0100 Subject: [PATCH 28/39] Revert changes to Usermap --- .../src/com/earth2me/essentials/UserMap.java | 76 ++++++++----------- 1 file changed, 32 insertions(+), 44 deletions(-) diff --git a/Essentials/src/com/earth2me/essentials/UserMap.java b/Essentials/src/com/earth2me/essentials/UserMap.java index c979e5046..d15438c7d 100644 --- a/Essentials/src/com/earth2me/essentials/UserMap.java +++ b/Essentials/src/com/earth2me/essentials/UserMap.java @@ -1,20 +1,22 @@ package com.earth2me.essentials; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.util.concurrent.UncheckedExecutionException; import java.io.File; -import java.lang.ref.SoftReference; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; +import java.util.Collections; import java.util.Set; +import java.util.concurrent.ConcurrentSkipListSet; +import java.util.concurrent.ExecutionException; import org.bukkit.entity.Player; -public class UserMap implements IConf +public class UserMap extends CacheLoader implements IConf { private final transient IEssentials ess; - private final transient Map> users = new HashMap>(); - //CacheBuilder.newBuilder().softValues().build(this); - //private final transient ConcurrentSkipListSet keys = new ConcurrentSkipListSet(); + private final transient Cache users = CacheBuilder.newBuilder().softValues().build(this); + private final transient ConcurrentSkipListSet keys = new ConcurrentSkipListSet(); public UserMap(final IEssentials ess) { @@ -35,19 +37,16 @@ public class UserMap implements IConf { return; } - synchronized (users) + keys.clear(); + users.invalidateAll(); + for (String string : userdir.list()) { - users.clear(); - - for (String string : userdir.list()) + if (!string.endsWith(".yml")) { - if (!string.endsWith(".yml")) - { - continue; - } - final String name = string.substring(0, string.length() - 4); - users.put(Util.sanitizeFileName(name), null); + continue; } + final String name = string.substring(0, string.length() - 4); + keys.add(Util.sanitizeFileName(name)); } } }); @@ -55,43 +54,40 @@ public class UserMap implements IConf public boolean userExists(final String name) { - return users.containsKey(Util.sanitizeFileName(name)); + return keys.contains(Util.sanitizeFileName(name)); } public User getUser(final String name) { try { - synchronized (users) - { - final SoftReference softRef = users.get(Util.sanitizeFileName(name)); - User user = softRef == null ? null : softRef.get(); - if (user == null) - { - user = load(name); - users.put(name, new SoftReference(user)); - } - return user; - } + return users.get(Util.sanitizeFileName(name)); } - catch (Exception ex) + catch (ExecutionException ex) + { + return null; + } + catch (UncheckedExecutionException ex) { return null; } } + @Override public User load(final String name) throws Exception { for (Player player : ess.getServer().getOnlinePlayers()) { if (player.getName().equalsIgnoreCase(name)) { + keys.add(Util.sanitizeFileName(name)); return new User(player, ess); } } final File userFile = getUserFile(name); if (userFile.exists()) { + keys.add(Util.sanitizeFileName(name)); return new User(new OfflinePlayer(name, ess), ess); } throw new Exception("User not found!"); @@ -105,28 +101,20 @@ public class UserMap implements IConf public void removeUser(final String name) { - synchronized (users) - { - users.remove(Util.sanitizeFileName(name)); - } + keys.remove(Util.sanitizeFileName(name)); + users.invalidate(Util.sanitizeFileName(name)); } public Set getAllUniqueUsers() { - synchronized (users) - { - return new HashSet(users.keySet()); - } + return Collections.unmodifiableSet(keys); } public int getUniqueUsers() { - synchronized (users) - { - return users.size(); - } + return keys.size(); } - + public File getUserFile(final String name) { final File userFolder = new File(ess.getDataFolder(), "userdata"); From f26cccb663b3cd255bbfe3b72499b0efdd3da279 Mon Sep 17 00:00:00 2001 From: snowleo Date: Mon, 16 Jan 2012 04:51:27 +0100 Subject: [PATCH 29/39] Optimize TextInput to cache motd and info textfiles. --- .../textreader/KeywordReplacer.java | 13 ++-- .../essentials/textreader/TextInput.java | 77 +++++++++++++------ 2 files changed, 62 insertions(+), 28 deletions(-) diff --git a/Essentials/src/com/earth2me/essentials/textreader/KeywordReplacer.java b/Essentials/src/com/earth2me/essentials/textreader/KeywordReplacer.java index 782798bff..a7aab67ba 100644 --- a/Essentials/src/com/earth2me/essentials/textreader/KeywordReplacer.java +++ b/Essentials/src/com/earth2me/essentials/textreader/KeywordReplacer.java @@ -4,6 +4,7 @@ import com.earth2me.essentials.DescParseTickFormat; import com.earth2me.essentials.IEssentials; import com.earth2me.essentials.User; import java.text.DateFormat; +import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; @@ -16,15 +17,17 @@ import org.bukkit.plugin.Plugin; public class KeywordReplacer implements IText { private final transient IText input; + private final transient List replaced; private final transient IEssentials ess; - + public KeywordReplacer(final IText input, final CommandSender sender, final IEssentials ess) { this.input = input; + this.replaced = new ArrayList(this.input.getLines().size()); this.ess = ess; replaceKeywords(sender); } - + private void replaceKeywords(final CommandSender sender) { String displayName, ipAddress, balance, mails, world; @@ -98,7 +101,7 @@ public class KeywordReplacer implements IText date = DateFormat.getDateInstance(DateFormat.MEDIUM, ess.getI18n().getCurrentLocale()).format(new Date()); time = DateFormat.getTimeInstance(DateFormat.MEDIUM, ess.getI18n().getCurrentLocale()).format(new Date()); - + version = ess.getServer().getVersion(); for (int i = 0; i < input.getLines().size(); i++) @@ -120,14 +123,14 @@ public class KeywordReplacer implements IText line = line.replace("{WORLDDATE}", worldDate); line = line.replace("{PLUGINS}", plugins); line = line.replace("{VERSION}", version); - input.getLines().set(i, line); + replaced.add(line); } } @Override public List getLines() { - return input.getLines(); + return replaced; } @Override diff --git a/Essentials/src/com/earth2me/essentials/textreader/TextInput.java b/Essentials/src/com/earth2me/essentials/textreader/TextInput.java index b25c30d51..316a0b576 100644 --- a/Essentials/src/com/earth2me/essentials/textreader/TextInput.java +++ b/Essentials/src/com/earth2me/essentials/textreader/TextInput.java @@ -4,6 +4,7 @@ import com.earth2me.essentials.IEssentials; import com.earth2me.essentials.User; import com.earth2me.essentials.Util; import java.io.*; +import java.lang.ref.SoftReference; import java.util.*; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -11,9 +12,11 @@ import org.bukkit.entity.Player; public class TextInput implements IText { - private final transient List lines = new ArrayList(); - private final transient List chapters = new ArrayList(); - private final transient Map bookmarks = new HashMap(); + private final transient List lines; + private final transient List chapters; + private final transient Map bookmarks; + private final transient long lastChange; + private final static HashMap> cache = new HashMap>(); public TextInput(final CommandSender sender, final String filename, final boolean createFile, final IEssentials ess) throws IOException { @@ -34,33 +37,62 @@ public class TextInput implements IText } if (file.exists()) { - final BufferedReader bufferedReader = new BufferedReader(new FileReader(file)); - try + lastChange = file.lastModified(); + boolean readFromfile; + synchronized (cache) { - int lineNumber = 0; - while (bufferedReader.ready()) + final SoftReference inputRef = cache.get(file.getName()); + TextInput input; + if (inputRef == null || (input = inputRef.get()) == null || input.lastChange < lastChange) { - final String line = bufferedReader.readLine(); - if (line == null) - { - break; - } - if (line.length() > 0 && line.charAt(0) == '#') - { - bookmarks.put(line.substring(1).toLowerCase(Locale.ENGLISH).replaceAll("&[0-9a-f]", ""), lineNumber); - chapters.add(line.substring(1).replace('&', '§').replace("§§", "&")); - } - lines.add(line.replace('&', '§').replace("§§", "&")); - lineNumber++; + lines = new ArrayList(); + chapters = new ArrayList(); + bookmarks = new HashMap(); + cache.put(file.getName(), new SoftReference(this)); + readFromfile = true; + } + else + { + lines = Collections.unmodifiableList(input.getLines()); + chapters = Collections.unmodifiableList(input.getChapters()); + bookmarks = Collections.unmodifiableMap(input.getBookmarks()); + readFromfile = false; } } - finally + if (readFromfile) { - bufferedReader.close(); + final BufferedReader bufferedReader = new BufferedReader(new FileReader(file)); + try + { + int lineNumber = 0; + while (bufferedReader.ready()) + { + final String line = bufferedReader.readLine(); + if (line == null) + { + break; + } + if (line.length() > 0 && line.charAt(0) == '#') + { + bookmarks.put(line.substring(1).toLowerCase(Locale.ENGLISH).replaceAll("&[0-9a-f]", ""), lineNumber); + chapters.add(line.substring(1).replace('&', '§').replace("§§", "&")); + } + lines.add(line.replace('&', '§').replace("§§", "&")); + lineNumber++; + } + } + finally + { + bufferedReader.close(); + } } } else { + lastChange = 0; + lines = Collections.emptyList(); + chapters = Collections.emptyList(); + bookmarks = Collections.emptyMap(); if (createFile) { final InputStream input = ess.getResource(filename + ".txt"); @@ -68,8 +100,7 @@ public class TextInput implements IText try { final byte[] buffer = new byte[1024]; - int length = 0; - length = input.read(buffer); + int length = input.read(buffer); while (length > 0) { output.write(buffer, 0, length); From 149ce7d74d3481b120b236ae00831226cbd07380 Mon Sep 17 00:00:00 2001 From: KHobbits Date: Mon, 16 Jan 2012 18:00:43 +0000 Subject: [PATCH 30/39] Adding support for Vault as a fallback economy method. --- Essentials/nbproject/pmd.settings | 1 + Essentials/nbproject/project.properties | 4 +- .../essentials/register/payment/Methods.java | 53 ++-- .../register/payment/methods/VaultEco.java | 272 ++++++++++++++++++ lib/Vault.jar | Bin 0 -> 180183 bytes 5 files changed, 306 insertions(+), 24 deletions(-) create mode 100644 Essentials/src/com/earth2me/essentials/register/payment/methods/VaultEco.java create mode 100644 lib/Vault.jar diff --git a/Essentials/nbproject/pmd.settings b/Essentials/nbproject/pmd.settings index 29baf7ea1..cea9eaa13 100644 --- a/Essentials/nbproject/pmd.settings +++ b/Essentials/nbproject/pmd.settings @@ -1,3 +1,4 @@ DoNotUseThreads LongVariable SignatureDeclareThrowsException +LocalVariableCouldBeFinal diff --git a/Essentials/nbproject/project.properties b/Essentials/nbproject/project.properties index 87e522254..0c8b44650 100644 --- a/Essentials/nbproject/project.properties +++ b/Essentials/nbproject/project.properties @@ -76,6 +76,7 @@ file.reference.MultiCurrency.jar=../lib/MultiCurrency.jar file.reference.Permissions3.jar=../lib/Permissions3.jar file.reference.PermissionsBukkit-1.2.jar=../lib/PermissionsBukkit-1.2.jar file.reference.PermissionsEx.jar=../lib/PermissionsEx.jar +file.reference.Vault.jar=../lib/Vault.jar includes=** jar.archive.disabled=${jnlp.enabled} jar.compress=true @@ -93,7 +94,8 @@ javac.classpath=\ ${file.reference.lombok-0.10.1.jar}:\ ${reference.EssentialsGroupManager.jar}:\ ${file.reference.bukkit.jar}:\ - ${file.reference.craftbukkit.jar} + ${file.reference.craftbukkit.jar}:\ + ${file.reference.Vault.jar} # Space-separated list of extra javac options javac.compilerargs= javac.deprecation=false diff --git a/Essentials/src/com/earth2me/essentials/register/payment/Methods.java b/Essentials/src/com/earth2me/essentials/register/payment/Methods.java index 32acc0442..f8729923e 100644 --- a/Essentials/src/com/earth2me/essentials/register/payment/Methods.java +++ b/Essentials/src/com/earth2me/essentials/register/payment/Methods.java @@ -7,23 +7,23 @@ import org.bukkit.plugin.PluginManager; /** - * The Methods initializes Methods that utilize the Method interface - * based on a "first come, first served" basis. + * The + * Methods initializes Methods that utilize the Method interface based on a "first come, first served" + * basis. * * Allowing you to check whether a payment method exists or not. * - * Methods also allows you to set a preferred method of payment before it captures - * payment plugins in the initialization process. + * Methods also allows you to set a preferred method of payment before it captures payment plugins in the initialization + * process. * - * in bukkit.yml: - *
+ * in
+ * bukkit.yml: 
  *  economy:
  *      preferred: "iConomy"
  * 
* - * @author: Nijikokun (@nijikokun) - * @copyright: Copyright (C) 2011 - * @license: AOL license + * @author: Nijikokun (@nijikokun) @copyright: Copyright (C) 2011 @license: AOL license + * */ public class Methods { @@ -52,6 +52,7 @@ public class Methods addMethod("BOSEconomy", new com.earth2me.essentials.register.payment.methods.BOSE7()); addMethod("Currency", new com.earth2me.essentials.register.payment.methods.MCUR()); Dependencies.add("MultiCurrency"); + addMethod("Vault", new com.earth2me.essentials.register.payment.methods.VaultEco()); } /** @@ -78,6 +79,7 @@ public class Methods /** * Use to get version of Register plugin + * * @return version */ public static String getVersion() @@ -86,10 +88,11 @@ public class Methods } /** - * Returns an array of payment method names that have been loaded - * through the _init method. + * Returns an array of payment method names that have been loaded through the + * _init method. * - * @return Set - Array of payment methods that are loaded. + * @return + * Set - Array of payment methods that are loaded. * @see #setMethod(org.bukkit.plugin.Plugin) */ public static Set getDependencies() @@ -98,8 +101,8 @@ public class Methods } /** - * Interprets Plugin class data to verify whether it is compatible with an existing payment - * method to use for payments and other various economic activity. + * Interprets Plugin class data to verify whether it is compatible with an existing payment method to use for + * payments and other various economic activity. * * @param plugin Plugin data from bukkit, Internal Class file. * @return Method or Null @@ -127,7 +130,8 @@ public class Methods /** * Verifies if Register has set a payment method for usage yet. * - * @return boolean + * @return + * boolean * @see #setMethod(org.bukkit.plugin.Plugin) * @see #checkDisabled(org.bukkit.plugin.Plugin) */ @@ -137,11 +141,11 @@ public class Methods } /** - * Checks Plugin Class against a multitude of checks to verify it's usability - * as a payment method. + * Checks Plugin Class against a multitude of checks to verify it's usability as a payment method. * * @param PluginManager the plugin manager for the server - * @return boolean True on success, False on failure. + * @return + * boolean True on success, False on failure. */ public static boolean setMethod(PluginManager manager) { @@ -242,7 +246,8 @@ public class Methods /** * Sets the preferred economy * - * @return boolean + * @return + * boolean */ public static boolean setPreferred(String check) { @@ -258,7 +263,9 @@ public class Methods /** * Grab the existing and initialized (hopefully) Method Class. * - * @return Method or Null + * @return + * Method or + * Null */ public static Method getMethod() { @@ -266,11 +273,11 @@ public class Methods } /** - * Verify is a plugin is disabled, only does this if we there is an existing payment - * method initialized in Register. + * Verify is a plugin is disabled, only does this if we there is an existing payment method initialized in Register. * * @param method Plugin data from bukkit, Internal Class file. - * @return boolean + * @return + * boolean */ public static boolean checkDisabled(Plugin method) { diff --git a/Essentials/src/com/earth2me/essentials/register/payment/methods/VaultEco.java b/Essentials/src/com/earth2me/essentials/register/payment/methods/VaultEco.java new file mode 100644 index 000000000..64160891c --- /dev/null +++ b/Essentials/src/com/earth2me/essentials/register/payment/methods/VaultEco.java @@ -0,0 +1,272 @@ +package com.earth2me.essentials.register.payment.methods; + +import net.milkbowl.vault.Vault; +import net.milkbowl.vault.economy.Economy; + +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.RegisteredServiceProvider; + +import com.earth2me.essentials.register.payment.Method; + +public class VaultEco implements Method { + + private Vault vault; + private Economy economy; + + @Override + public Vault getPlugin() { + return this.vault; + } + + @Override + public boolean createAccount(String name, Double amount) { + if (hasAccount(name)) { + return false; + } + + return false; + } + + @Override + public String getName() { + + return this.vault.getDescription().getName().concat(" - Economy: ").concat(economy == null ? "NoEco" : economy.getName()); + } + + @Override + public String getVersion() { + return this.vault.getDescription().getVersion(); + } + + @Override + public int fractionalDigits() { + return 0; + } + + @Override + public String format(double amount) { + return this.economy.format(amount); + } + + @Override + public boolean hasBanks() { + return this.economy.hasBankSupport(); + } + + @Override + public boolean hasBank(String bank) { + return this.economy.getBanks().contains(bank); + } + + @Override + public boolean hasAccount(String name) { + return this.economy.hasAccount(name); + } + + @Override + public boolean hasBankAccount(String bank, String name) { + return this.economy.isBankOwner(bank, name).transactionSuccess() + || this.economy.isBankMember(bank, name).transactionSuccess(); + } + + @Override + public boolean createAccount(String name) { + return this.economy.createBank(name, "").transactionSuccess(); + } + + public boolean createAccount(String name, double balance) { + if(!this.economy.createBank(name, "").transactionSuccess()) { + return false; + } + return this.economy.bankDeposit(name, balance).transactionSuccess(); + } + + @Override + public MethodAccount getAccount(String name) { + if(!hasAccount(name)) + return null; + + return new VaultAccount(name, this.economy); + } + + @Override + public MethodBankAccount getBankAccount(String bank, String name) { + if(!hasBankAccount(bank, name)) + return null; + + return new VaultBankAccount(bank, economy); + } + + @Override + public boolean isCompatible(Plugin plugin) { + RegisteredServiceProvider ecoPlugin = plugin.getServer().getServicesManager().getRegistration(net.milkbowl.vault.economy.Economy.class); + return plugin instanceof Vault && ecoPlugin != null && !ecoPlugin.getProvider().getName().equals("Essentials Economy"); + } + + @Override + public void setPlugin(Plugin plugin) { + this.vault = (Vault) plugin; + RegisteredServiceProvider economyProvider = this.vault.getServer().getServicesManager().getRegistration(net.milkbowl.vault.economy.Economy.class); + if (economyProvider != null) { + this.economy = economyProvider.getProvider(); + } + } + + public class VaultAccount implements MethodAccount { + private final String name; + private final Economy economy; + + public VaultAccount(String name, Economy economy) { + this.name = name; + this.economy = economy; + } + + @Override + public double balance() { + return this.economy.getBalance(this.name); + } + + @Override + public boolean set(double amount) { + if(!this.economy.withdrawPlayer(this.name, this.balance()).transactionSuccess()) { + return false; + } + if(amount == 0) { + return true; + } + return this.economy.depositPlayer(this.name, amount).transactionSuccess(); + } + + @Override + public boolean add(double amount) { + return this.economy.depositPlayer(this.name, amount).transactionSuccess(); + } + + @Override + public boolean subtract(double amount) { + return this.economy.withdrawPlayer(this.name, amount).transactionSuccess(); + } + + @Override + public boolean multiply(double amount) { + double balance = this.balance(); + return this.set(balance * amount); + } + + @Override + public boolean divide(double amount) { + double balance = this.balance(); + return this.set(balance / amount); + } + + @Override + public boolean hasEnough(double amount) { + return (this.balance() >= amount); + } + + @Override + public boolean hasOver(double amount) { + return (this.balance() > amount); + } + + @Override + public boolean hasUnder(double amount) { + return (this.balance() < amount); + } + + @Override + public boolean isNegative() { + return (this.balance() < 0); + } + + @Override + public boolean remove() { + return this.set(0.0); + } + } + + public class VaultBankAccount implements MethodBankAccount { + + private final String bank; + private final Economy economy; + + public VaultBankAccount(String bank, Economy economy) { + this.bank = bank; + this.economy = economy; + } + + @Override + public String getBankName() { + return this.bank; + } + + @Override + public int getBankId() { + return -1; + } + + @Override + public double balance() { + return this.economy.bankBalance(this.bank).balance; + } + + @Override + public boolean set(double amount) { + if(!this.economy.bankWithdraw(this.bank, this.balance()).transactionSuccess()) { + return false; + } + if(amount == 0) { + return true; + } + return this.economy.bankDeposit(this.bank, amount).transactionSuccess(); + } + + @Override + public boolean add(double amount) { + return this.economy.bankDeposit(this.bank, amount).transactionSuccess(); + } + + @Override + public boolean subtract(double amount) { + return this.economy.bankWithdraw(this.bank, amount).transactionSuccess(); + } + + @Override + public boolean multiply(double amount) { + double balance = this.balance(); + return this.set(balance * amount); + } + + @Override + public boolean divide(double amount) { + double balance = this.balance(); + return this.set(balance / amount); + } + + @Override + public boolean hasEnough(double amount) { + return (this.balance() >= amount); + } + + @Override + public boolean hasOver(double amount) { + return (this.balance() > amount); + } + + @Override + public boolean hasUnder(double amount) { + return (this.balance() < amount); + } + + @Override + public boolean isNegative() { + return (this.balance() < 0); + } + + @Override + public boolean remove() { + return this.set(0.0); + } + + } +} \ No newline at end of file diff --git a/lib/Vault.jar b/lib/Vault.jar new file mode 100644 index 0000000000000000000000000000000000000000..96b1625c428039021a7eaa50641665144c81a1cb GIT binary patch literal 180183 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

8H%wmZ`h30I{tM@q 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%0D!iV1f`UqibU1`e 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�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 ?K)G=T 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 literal 0 HcmV?d00001 From cd2f819c29a786f693f90b352af99defc2ae69b5 Mon Sep 17 00:00:00 2001 From: snowleo Date: Mon, 16 Jan 2012 19:11:41 +0100 Subject: [PATCH 31/39] Cleanup --- Essentials/nbproject/pmd.settings | 1 - .../register/payment/methods/VaultEco.java | 414 ++++++++++-------- 2 files changed, 235 insertions(+), 180 deletions(-) diff --git a/Essentials/nbproject/pmd.settings b/Essentials/nbproject/pmd.settings index cea9eaa13..29baf7ea1 100644 --- a/Essentials/nbproject/pmd.settings +++ b/Essentials/nbproject/pmd.settings @@ -1,4 +1,3 @@ DoNotUseThreads LongVariable SignatureDeclareThrowsException -LocalVariableCouldBeFinal diff --git a/Essentials/src/com/earth2me/essentials/register/payment/methods/VaultEco.java b/Essentials/src/com/earth2me/essentials/register/payment/methods/VaultEco.java index 64160891c..c7757f66b 100644 --- a/Essentials/src/com/earth2me/essentials/register/payment/methods/VaultEco.java +++ b/Essentials/src/com/earth2me/essentials/register/payment/methods/VaultEco.java @@ -8,265 +8,321 @@ import org.bukkit.plugin.RegisteredServiceProvider; import com.earth2me.essentials.register.payment.Method; -public class VaultEco implements Method { - private Vault vault; - private Economy economy; +public class VaultEco implements Method +{ + private Vault vault; + private Economy economy; @Override - public Vault getPlugin() { - return this.vault; - } - - @Override - public boolean createAccount(String name, Double amount) { - if (hasAccount(name)) { - return false; - } - - return false; - } - - @Override - public String getName() { - - return this.vault.getDescription().getName().concat(" - Economy: ").concat(economy == null ? "NoEco" : economy.getName()); - } + public Vault getPlugin() + { + return this.vault; + } @Override - public String getVersion() { - return this.vault.getDescription().getVersion(); - } + public boolean createAccount(String name, Double amount) + { + if (hasAccount(name)) + { + return false; + } + + return false; + } @Override - public int fractionalDigits() { - return 0; - } + public String getName() + { + + return this.vault.getDescription().getName().concat(" - Economy: ").concat(economy == null ? "NoEco" : economy.getName()); + } @Override - public String format(double amount) { - return this.economy.format(amount); - } + public String getVersion() + { + return this.vault.getDescription().getVersion(); + } @Override - public boolean hasBanks() { - return this.economy.hasBankSupport(); - } + public int fractionalDigits() + { + return 0; + } @Override - public boolean hasBank(String bank) { - return this.economy.getBanks().contains(bank); - } + public String format(double amount) + { + return this.economy.format(amount); + } @Override - public boolean hasAccount(String name) { - return this.economy.hasAccount(name); - } + public boolean hasBanks() + { + return this.economy.hasBankSupport(); + } @Override - public boolean hasBankAccount(String bank, String name) { - return this.economy.isBankOwner(bank, name).transactionSuccess() - || this.economy.isBankMember(bank, name).transactionSuccess(); - } + public boolean hasBank(String bank) + { + return this.economy.getBanks().contains(bank); + } @Override - public boolean createAccount(String name) { - return this.economy.createBank(name, "").transactionSuccess(); - } - - public boolean createAccount(String name, double balance) { - if(!this.economy.createBank(name, "").transactionSuccess()) { - return false; - } - return this.economy.bankDeposit(name, balance).transactionSuccess(); - } + public boolean hasAccount(String name) + { + return this.economy.hasAccount(name); + } @Override - public MethodAccount getAccount(String name) { - if(!hasAccount(name)) - return null; - - return new VaultAccount(name, this.economy); - } + public boolean hasBankAccount(String bank, String name) + { + return this.economy.isBankOwner(bank, name).transactionSuccess() + || this.economy.isBankMember(bank, name).transactionSuccess(); + } @Override - public MethodBankAccount getBankAccount(String bank, String name) { - if(!hasBankAccount(bank, name)) - return null; + public boolean createAccount(String name) + { + return this.economy.createBank(name, "").transactionSuccess(); + } - return new VaultBankAccount(bank, economy); - } + public boolean createAccount(String name, double balance) + { + if (!this.economy.createBank(name, "").transactionSuccess()) + { + return false; + } + return this.economy.bankDeposit(name, balance).transactionSuccess(); + } @Override - public boolean isCompatible(Plugin plugin) { - RegisteredServiceProvider ecoPlugin = plugin.getServer().getServicesManager().getRegistration(net.milkbowl.vault.economy.Economy.class); + public MethodAccount getAccount(String name) + { + if (!hasAccount(name)) + { + return null; + } + + return new VaultAccount(name, this.economy); + } + + @Override + public MethodBankAccount getBankAccount(String bank, String name) + { + if (!hasBankAccount(bank, name)) + { + return null; + } + + return new VaultBankAccount(bank, economy); + } + + @Override + public boolean isCompatible(Plugin plugin) + { + RegisteredServiceProvider ecoPlugin = plugin.getServer().getServicesManager().getRegistration(net.milkbowl.vault.economy.Economy.class); return plugin instanceof Vault && ecoPlugin != null && !ecoPlugin.getProvider().getName().equals("Essentials Economy"); - } + } @Override - public void setPlugin(Plugin plugin) { - this.vault = (Vault) plugin; - RegisteredServiceProvider economyProvider = this.vault.getServer().getServicesManager().getRegistration(net.milkbowl.vault.economy.Economy.class); - if (economyProvider != null) { - this.economy = economyProvider.getProvider(); - } - } + public void setPlugin(Plugin plugin) + { + this.vault = (Vault)plugin; + RegisteredServiceProvider economyProvider = this.vault.getServer().getServicesManager().getRegistration(net.milkbowl.vault.economy.Economy.class); + if (economyProvider != null) + { + this.economy = economyProvider.getProvider(); + } + } - public class VaultAccount implements MethodAccount { - private final String name; - private final Economy economy; - public VaultAccount(String name, Economy economy) { - this.name = name; - this.economy = economy; - } + public class VaultAccount implements MethodAccount + { + private final String name; + private final Economy economy; + + public VaultAccount(String name, Economy economy) + { + this.name = name; + this.economy = economy; + } @Override - public double balance() { - return this.economy.getBalance(this.name); - } + public double balance() + { + return this.economy.getBalance(this.name); + } @Override - public boolean set(double amount) { - if(!this.economy.withdrawPlayer(this.name, this.balance()).transactionSuccess()) { - return false; - } - if(amount == 0) { - return true; - } - return this.economy.depositPlayer(this.name, amount).transactionSuccess(); - } + public boolean set(double amount) + { + if (!this.economy.withdrawPlayer(this.name, this.balance()).transactionSuccess()) + { + return false; + } + if (amount == 0) + { + return true; + } + return this.economy.depositPlayer(this.name, amount).transactionSuccess(); + } @Override - public boolean add(double amount) { - return this.economy.depositPlayer(this.name, amount).transactionSuccess(); - } + public boolean add(double amount) + { + return this.economy.depositPlayer(this.name, amount).transactionSuccess(); + } @Override - public boolean subtract(double amount) { - return this.economy.withdrawPlayer(this.name, amount).transactionSuccess(); - } + public boolean subtract(double amount) + { + return this.economy.withdrawPlayer(this.name, amount).transactionSuccess(); + } @Override - public boolean multiply(double amount) { - double balance = this.balance(); - return this.set(balance * amount); - } + public boolean multiply(double amount) + { + double balance = this.balance(); + return this.set(balance * amount); + } @Override - public boolean divide(double amount) { - double balance = this.balance(); - return this.set(balance / amount); - } + public boolean divide(double amount) + { + double balance = this.balance(); + return this.set(balance / amount); + } @Override - public boolean hasEnough(double amount) { - return (this.balance() >= amount); - } + public boolean hasEnough(double amount) + { + return (this.balance() >= amount); + } @Override - public boolean hasOver(double amount) { - return (this.balance() > amount); - } + public boolean hasOver(double amount) + { + return (this.balance() > amount); + } @Override - public boolean hasUnder(double amount) { - return (this.balance() < amount); - } + public boolean hasUnder(double amount) + { + return (this.balance() < amount); + } @Override - public boolean isNegative() { - return (this.balance() < 0); - } + public boolean isNegative() + { + return (this.balance() < 0); + } @Override - public boolean remove() { - return this.set(0.0); - } - } + public boolean remove() + { + return this.set(0.0); + } + } - public class VaultBankAccount implements MethodBankAccount { - private final String bank; - private final Economy economy; + public class VaultBankAccount implements MethodBankAccount + { + private final String bank; + private final Economy economy; - public VaultBankAccount(String bank, Economy economy) { - this.bank = bank; - this.economy = economy; - } + public VaultBankAccount(String bank, Economy economy) + { + this.bank = bank; + this.economy = economy; + } @Override - public String getBankName() { - return this.bank; - } + public String getBankName() + { + return this.bank; + } @Override - public int getBankId() { - return -1; - } + public int getBankId() + { + return -1; + } @Override - public double balance() { - return this.economy.bankBalance(this.bank).balance; - } + public double balance() + { + return this.economy.bankBalance(this.bank).balance; + } @Override - public boolean set(double amount) { - if(!this.economy.bankWithdraw(this.bank, this.balance()).transactionSuccess()) { - return false; - } - if(amount == 0) { - return true; - } - return this.economy.bankDeposit(this.bank, amount).transactionSuccess(); - } + public boolean set(double amount) + { + if (!this.economy.bankWithdraw(this.bank, this.balance()).transactionSuccess()) + { + return false; + } + if (amount == 0) + { + return true; + } + return this.economy.bankDeposit(this.bank, amount).transactionSuccess(); + } @Override - public boolean add(double amount) { - return this.economy.bankDeposit(this.bank, amount).transactionSuccess(); - } + public boolean add(double amount) + { + return this.economy.bankDeposit(this.bank, amount).transactionSuccess(); + } @Override - public boolean subtract(double amount) { - return this.economy.bankWithdraw(this.bank, amount).transactionSuccess(); - } + public boolean subtract(double amount) + { + return this.economy.bankWithdraw(this.bank, amount).transactionSuccess(); + } @Override - public boolean multiply(double amount) { - double balance = this.balance(); - return this.set(balance * amount); - } + public boolean multiply(double amount) + { + double balance = this.balance(); + return this.set(balance * amount); + } @Override - public boolean divide(double amount) { - double balance = this.balance(); - return this.set(balance / amount); - } + public boolean divide(double amount) + { + double balance = this.balance(); + return this.set(balance / amount); + } @Override - public boolean hasEnough(double amount) { - return (this.balance() >= amount); - } + public boolean hasEnough(double amount) + { + return (this.balance() >= amount); + } @Override - public boolean hasOver(double amount) { - return (this.balance() > amount); - } + public boolean hasOver(double amount) + { + return (this.balance() > amount); + } @Override - public boolean hasUnder(double amount) { - return (this.balance() < amount); - } + public boolean hasUnder(double amount) + { + return (this.balance() < amount); + } @Override - public boolean isNegative() { - return (this.balance() < 0); - } + public boolean isNegative() + { + return (this.balance() < 0); + } @Override - public boolean remove() { - return this.set(0.0); - } - - } + public boolean remove() + { + return this.set(0.0); + } + } } \ No newline at end of file From 0fb07d4cc16f541989a5a9860aa36b7f6bf7389b Mon Sep 17 00:00:00 2001 From: KHobbits Date: Mon, 16 Jan 2012 18:58:44 +0000 Subject: [PATCH 32/39] Removing debugging message from player login. --- .../src/com/earth2me/essentials/EssentialsPlayerListener.java | 1 - 1 file changed, 1 deletion(-) diff --git a/Essentials/src/com/earth2me/essentials/EssentialsPlayerListener.java b/Essentials/src/com/earth2me/essentials/EssentialsPlayerListener.java index 9edc80364..b6cb728d2 100644 --- a/Essentials/src/com/earth2me/essentials/EssentialsPlayerListener.java +++ b/Essentials/src/com/earth2me/essentials/EssentialsPlayerListener.java @@ -183,7 +183,6 @@ public class EssentialsPlayerListener extends PlayerListener { if (event.getResult() != Result.ALLOWED && event.getResult() != Result.KICK_FULL && event.getResult() != Result.KICK_BANNED) { - LOGGER.log(Level.INFO, "Disconnecting user " + event.getPlayer().toString() + " due to " + event.getResult().toString()); return; } User user = ess.getUser(event.getPlayer()); From 26d63f36668cd111706f6406f0d10f8d08197020 Mon Sep 17 00:00:00 2001 From: snowleo Date: Mon, 16 Jan 2012 20:29:55 +0100 Subject: [PATCH 33/39] Strip colors from chat, if the user does not have the permission. --- Essentials/src/com/earth2me/essentials/Util.java | 2 +- .../essentials/chat/EssentialsChatPlayerListenerLowest.java | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Essentials/src/com/earth2me/essentials/Util.java b/Essentials/src/com/earth2me/essentials/Util.java index a8a0cb1e4..eeb2dd6d6 100644 --- a/Essentials/src/com/earth2me/essentials/Util.java +++ b/Essentials/src/com/earth2me/essentials/Util.java @@ -478,7 +478,7 @@ public class Util } return buf.toString(); } - private static transient final Pattern COLOR_PATTERN = Pattern.compile("(?i)\u00A7[0-9A-F]"); + private static transient final Pattern COLOR_PATTERN = Pattern.compile("\u00A7+[0-9A-FKa-fk]"); public static String stripColor(final String input) { diff --git a/EssentialsChat/src/com/earth2me/essentials/chat/EssentialsChatPlayerListenerLowest.java b/EssentialsChat/src/com/earth2me/essentials/chat/EssentialsChatPlayerListenerLowest.java index de5757951..82241a1f5 100644 --- a/EssentialsChat/src/com/earth2me/essentials/chat/EssentialsChatPlayerListenerLowest.java +++ b/EssentialsChat/src/com/earth2me/essentials/chat/EssentialsChatPlayerListenerLowest.java @@ -2,6 +2,7 @@ package com.earth2me.essentials.chat; import com.earth2me.essentials.IEssentials; import com.earth2me.essentials.User; +import com.earth2me.essentials.Util; import java.util.Locale; import java.util.Map; import org.bukkit.Server; @@ -32,6 +33,8 @@ public class EssentialsChatPlayerListenerLowest extends EssentialsChatPlayer if (user.isAuthorized("essentials.chat.color")) { event.setMessage(event.getMessage().replaceAll("&([0-9a-f])", "\u00a7$1")); + } else { + event.setMessage(Util.stripColor(event.getMessage())); } event.setFormat(ess.getSettings().getChatFormat(user.getGroup()).replace('&', '\u00a7').replace("\u00a7\u00a7", "&").replace("{DISPLAYNAME}", "%1$s").replace("{GROUP}", user.getGroup()).replace("{MESSAGE}", "%2$s").replace("{WORLDNAME}", user.getWorld().getName()).replace("{SHORTWORLDNAME}", user.getWorld().getName().substring(0, 1).toUpperCase(Locale.ENGLISH))); } From 813e66b4f2f2759065f98c5f472bad9a0e0bf2db Mon Sep 17 00:00:00 2001 From: snowleo Date: Mon, 16 Jan 2012 20:38:53 +0100 Subject: [PATCH 34/39] Fix /nick command colors --- .../src/com/earth2me/essentials/commands/Commandnick.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Essentials/src/com/earth2me/essentials/commands/Commandnick.java b/Essentials/src/com/earth2me/essentials/commands/Commandnick.java index 95ad5ea84..b9fbccb37 100644 --- a/Essentials/src/com/earth2me/essentials/commands/Commandnick.java +++ b/Essentials/src/com/earth2me/essentials/commands/Commandnick.java @@ -2,6 +2,7 @@ package com.earth2me.essentials.commands; import static com.earth2me.essentials.I18n._; import com.earth2me.essentials.User; +import com.earth2me.essentials.Util; import java.util.Locale; import org.bukkit.Server; import org.bukkit.command.CommandSender; @@ -65,9 +66,10 @@ public class Commandnick extends EssentialsCommand { if (user == null || user.isAuthorized("essentials.nick.color")) { - return nick.replace('&', '\u00a7').replace("\u00a7\u00a7", "&"); + return nick.replace('&', '\u00a7').replaceAll("\u00a7+k", ""); + } else { + return Util.stripColor(nick); } - return nick; } private void resetAllNicknames(final Server server) @@ -86,7 +88,7 @@ public class Commandnick extends EssentialsCommand private void setNickname(final Server server, final User target, final String nick) throws Exception { - if (nick.matches("[^a-zA-Z_0-9]")) + if (!nick.matches("^[a-zA-Z_0-9\u00a7]+$")) { throw new Exception(_("nickNamesAlpha")); } From 4435121c840aac5de4ac69b3463515f5cde060da Mon Sep 17 00:00:00 2001 From: KHobbits Date: Mon, 16 Jan 2012 19:42:15 +0000 Subject: [PATCH 35/39] Stripping vanilla colours from /msg --- .../essentials/commands/Commandmsg.java | 33 ++++++++++--------- .../essentials/chat/EssentialsChatPlayer.java | 11 ++++--- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/Essentials/src/com/earth2me/essentials/commands/Commandmsg.java b/Essentials/src/com/earth2me/essentials/commands/Commandmsg.java index 34754ec9c..63bce435a 100644 --- a/Essentials/src/com/earth2me/essentials/commands/Commandmsg.java +++ b/Essentials/src/com/earth2me/essentials/commands/Commandmsg.java @@ -4,6 +4,7 @@ import com.earth2me.essentials.Console; import static com.earth2me.essentials.I18n._; import com.earth2me.essentials.IReplyTo; import com.earth2me.essentials.User; +import com.earth2me.essentials.Util; import java.util.List; import org.bukkit.Server; import org.bukkit.command.CommandSender; @@ -34,11 +35,11 @@ public class Commandmsg extends EssentialsCommand } } - String message = getFinalArg(args, 1); - String translatedMe = _("me"); + final String message = Util.stripColor(getFinalArg(args, 1)); + final String translatedMe = _("me"); - IReplyTo replyTo = sender instanceof Player ? ess.getUser((Player)sender) : Console.getConsoleReplyTo(); - String senderName = sender instanceof Player ? ((Player)sender).getDisplayName() : Console.NAME; + final IReplyTo replyTo = sender instanceof Player ? ess.getUser((Player)sender) : Console.getConsoleReplyTo(); + final String senderName = sender instanceof Player ? ((Player)sender).getDisplayName() : Console.NAME; if (args[0].equalsIgnoreCase(Console.NAME)) { @@ -50,38 +51,38 @@ public class Commandmsg extends EssentialsCommand return; } - List matches = server.matchPlayer(args[0]); + final List matchedPlayers = server.matchPlayer(args[0]); - if (matches.isEmpty()) + if (matchedPlayers.isEmpty()) { throw new Exception(_("playerNotFound")); } int i = 0; - for (Player p : matches) + for (Player matchedPlayer : matchedPlayers) { - final User u = ess.getUser(p); + final User u = ess.getUser(matchedPlayer); if (u.isHidden()) { i++; } } - if (i == matches.size()) + if (i == matchedPlayers.size()) { throw new Exception(_("playerNotFound")); } - for (Player p : matches) + for (Player matchedPlayer : matchedPlayers) { - sender.sendMessage(_("msgFormat", translatedMe, p.getDisplayName(), message)); - final User u = ess.getUser(p); - if (sender instanceof Player && (u.isIgnoredPlayer(((Player)sender).getName()) || u.isHidden())) + sender.sendMessage(_("msgFormat", translatedMe, matchedPlayer.getDisplayName(), message)); + final User matchedUser = ess.getUser(matchedPlayer); + if (sender instanceof Player && (matchedUser.isIgnoredPlayer(((Player)sender).getName()) || matchedUser.isHidden())) { continue; } - p.sendMessage(_("msgFormat", senderName, translatedMe, message)); - replyTo.setReplyTo(ess.getUser(p)); - ess.getUser(p).setReplyTo(sender); + matchedPlayer.sendMessage(_("msgFormat", senderName, translatedMe, message)); + replyTo.setReplyTo(ess.getUser(matchedPlayer)); + ess.getUser(matchedPlayer).setReplyTo(sender); } } } diff --git a/EssentialsChat/src/com/earth2me/essentials/chat/EssentialsChatPlayer.java b/EssentialsChat/src/com/earth2me/essentials/chat/EssentialsChatPlayer.java index 2d6e1ae4e..432e2309c 100644 --- a/EssentialsChat/src/com/earth2me/essentials/chat/EssentialsChatPlayer.java +++ b/EssentialsChat/src/com/earth2me/essentials/chat/EssentialsChatPlayer.java @@ -112,7 +112,7 @@ public abstract class EssentialsChatPlayer extends PlayerListener logger.info(_("localFormat", sender.getName(), event.getMessage())); final Location loc = sender.getLocation(); final World world = loc.getWorld(); - + for (Player onlinePlayer : server.getOnlinePlayers()) { String type = "[L]"; @@ -123,11 +123,14 @@ public abstract class EssentialsChatPlayer extends PlayerListener continue; } if (!user.equals(sender)) - { + { final Location playerLoc = user.getLocation(); - if (playerLoc.getWorld() != world) { continue; } + if (playerLoc.getWorld() != world) + { + continue; + } final double delta = playerLoc.distanceSquared(loc); - + if (delta > radius) { if (user.isAuthorized("essentials.chat.spy")) From 1f2c669eca786debc3ee5ee8a3de5ad98bd6470a Mon Sep 17 00:00:00 2001 From: KHobbits Date: Mon, 16 Jan 2012 20:25:36 +0000 Subject: [PATCH 36/39] Stripping vanilla colour from /helpop and /mail Adding support for &k in EssChat Adding support for colour in /msg and /r - New perm: essentials.msg.color --- .../essentials/commands/Commandhelpop.java | 3 +- .../essentials/commands/Commandmail.java | 5 ++-- .../essentials/commands/Commandme.java | 7 ++++- .../essentials/commands/Commandmsg.java | 14 ++++++++- .../essentials/commands/Commandr.java | 29 +++++++++++++++++-- .../EssentialsChatPlayerListenerLowest.java | 2 +- 6 files changed, 51 insertions(+), 9 deletions(-) diff --git a/Essentials/src/com/earth2me/essentials/commands/Commandhelpop.java b/Essentials/src/com/earth2me/essentials/commands/Commandhelpop.java index f558a1b52..20cd5cdd3 100644 --- a/Essentials/src/com/earth2me/essentials/commands/Commandhelpop.java +++ b/Essentials/src/com/earth2me/essentials/commands/Commandhelpop.java @@ -2,6 +2,7 @@ package com.earth2me.essentials.commands; import static com.earth2me.essentials.I18n._; import com.earth2me.essentials.User; +import com.earth2me.essentials.Util; import java.util.logging.Level; import org.bukkit.Server; import org.bukkit.entity.Player; @@ -22,7 +23,7 @@ public class Commandhelpop extends EssentialsCommand throw new NotEnoughArgumentsException(); } - final String message = _("helpOp", user.getDisplayName(), getFinalArg(args, 0)); + final String message = _("helpOp", user.getDisplayName(), Util.stripColor(getFinalArg(args, 0))); logger.log(Level.INFO, message); for (Player onlinePlayer : server.getOnlinePlayers()) { diff --git a/Essentials/src/com/earth2me/essentials/commands/Commandmail.java b/Essentials/src/com/earth2me/essentials/commands/Commandmail.java index 6a66186e7..4a9928b85 100644 --- a/Essentials/src/com/earth2me/essentials/commands/Commandmail.java +++ b/Essentials/src/com/earth2me/essentials/commands/Commandmail.java @@ -2,6 +2,7 @@ package com.earth2me.essentials.commands; import static com.earth2me.essentials.I18n._; import com.earth2me.essentials.User; +import com.earth2me.essentials.Util; import java.util.List; import org.bukkit.ChatColor; import org.bukkit.Server; @@ -58,7 +59,7 @@ public class Commandmail extends EssentialsCommand } if (!u.isIgnoredPlayer(user.getName())) { - u.addMail(user.getName() + ": " + getFinalArg(args, 2)); + u.addMail(user.getName() + ": " + Util.stripColor(getFinalArg(args, 2))); } user.sendMessage(_("mailSent")); return; @@ -69,7 +70,7 @@ public class Commandmail extends EssentialsCommand { throw new Exception(_("noPerm","essentials.mail.sendall")); } - ess.scheduleAsyncDelayedTask(new SendAll(user.getName() + ": " + getFinalArg(args, 1))); + ess.scheduleAsyncDelayedTask(new SendAll(user.getName() + ": " + Util.stripColor(getFinalArg(args, 1)))); user.sendMessage(_("mailSent")); return; } diff --git a/Essentials/src/com/earth2me/essentials/commands/Commandme.java b/Essentials/src/com/earth2me/essentials/commands/Commandme.java index 7ae87251d..3cdddf99c 100644 --- a/Essentials/src/com/earth2me/essentials/commands/Commandme.java +++ b/Essentials/src/com/earth2me/essentials/commands/Commandme.java @@ -2,6 +2,7 @@ package com.earth2me.essentials.commands; import static com.earth2me.essentials.I18n._; import com.earth2me.essentials.User; +import com.earth2me.essentials.Util; import org.bukkit.Server; @@ -28,8 +29,12 @@ public class Commandme extends EssentialsCommand String message = getFinalArg(args, 0); if (user.isAuthorized("essentials.chat.color")) { - message = message.replaceAll("&([0-9a-f])", "§$1"); + message = message.replaceAll("&([0-9a-fk])", "§$1"); } + else { + message = Util.stripColor(message); + } + ess.broadcastMessage(user, _("action", user.getDisplayName(), message)); } diff --git a/Essentials/src/com/earth2me/essentials/commands/Commandmsg.java b/Essentials/src/com/earth2me/essentials/commands/Commandmsg.java index 63bce435a..de4b921e6 100644 --- a/Essentials/src/com/earth2me/essentials/commands/Commandmsg.java +++ b/Essentials/src/com/earth2me/essentials/commands/Commandmsg.java @@ -26,6 +26,7 @@ public class Commandmsg extends EssentialsCommand throw new NotEnoughArgumentsException(); } + String message = getFinalArg(args, 1); if (sender instanceof Player) { User user = ess.getUser(sender); @@ -33,9 +34,20 @@ public class Commandmsg extends EssentialsCommand { throw new Exception(_("voiceSilenced")); } + if (user.isAuthorized("essentials.msg.color")) + { + message = message.replaceAll("&([0-9a-fk])", "§$1"); + } + else + { + message = Util.stripColor(message); + } + } + else + { + message = message.replaceAll("&([0-9a-fk])", "§$1"); } - final String message = Util.stripColor(getFinalArg(args, 1)); final String translatedMe = _("me"); final IReplyTo replyTo = sender instanceof Player ? ess.getUser((Player)sender) : Console.getConsoleReplyTo(); diff --git a/Essentials/src/com/earth2me/essentials/commands/Commandr.java b/Essentials/src/com/earth2me/essentials/commands/Commandr.java index 1da198444..9e30225ac 100644 --- a/Essentials/src/com/earth2me/essentials/commands/Commandr.java +++ b/Essentials/src/com/earth2me/essentials/commands/Commandr.java @@ -4,6 +4,7 @@ import com.earth2me.essentials.Console; import static com.earth2me.essentials.I18n._; import com.earth2me.essentials.IReplyTo; import com.earth2me.essentials.User; +import com.earth2me.essentials.Util; import org.bukkit.Server; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -24,9 +25,31 @@ public class Commandr extends EssentialsCommand throw new NotEnoughArgumentsException(); } - final String message = getFinalArg(args, 0); - final IReplyTo replyTo = sender instanceof Player ? ess.getUser((Player)sender) : Console.getConsoleReplyTo(); - final String senderName = sender instanceof Player ? ((Player)sender).getDisplayName() : Console.NAME; + String message = getFinalArg(args, 0); + IReplyTo replyTo; + String senderName; + + if (sender instanceof Player) + { + User user = ess.getUser(sender); + if (user.isAuthorized("essentials.msg.color")) + { + message = message.replaceAll("&([0-9a-fk])", "§$1"); + } + else + { + message = Util.stripColor(message); + } + replyTo = user; + senderName = user.getDisplayName(); + } + else + { + message = message.replaceAll("&([0-9a-fk])", "§$1"); + replyTo = Console.getConsoleReplyTo(); + senderName = Console.NAME; + } + final CommandSender target = replyTo.getReplyTo(); final String targetName = target instanceof Player ? ((Player)target).getDisplayName() : Console.NAME; diff --git a/EssentialsChat/src/com/earth2me/essentials/chat/EssentialsChatPlayerListenerLowest.java b/EssentialsChat/src/com/earth2me/essentials/chat/EssentialsChatPlayerListenerLowest.java index 82241a1f5..5c674d05d 100644 --- a/EssentialsChat/src/com/earth2me/essentials/chat/EssentialsChatPlayerListenerLowest.java +++ b/EssentialsChat/src/com/earth2me/essentials/chat/EssentialsChatPlayerListenerLowest.java @@ -32,7 +32,7 @@ public class EssentialsChatPlayerListenerLowest extends EssentialsChatPlayer final User user = ess.getUser(event.getPlayer()); if (user.isAuthorized("essentials.chat.color")) { - event.setMessage(event.getMessage().replaceAll("&([0-9a-f])", "\u00a7$1")); + event.setMessage(event.getMessage().replaceAll("&([0-9a-fk])", "\u00a7$1")); } else { event.setMessage(Util.stripColor(event.getMessage())); } From a66ffd892172092d0ee95086267c22bdd6fbc31f Mon Sep 17 00:00:00 2001 From: snowleo Date: Tue, 17 Jan 2012 01:45:03 +0100 Subject: [PATCH 37/39] Shorter names for enchantment signs. --- Essentials/src/com/earth2me/essentials/Enchantments.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Essentials/src/com/earth2me/essentials/Enchantments.java b/Essentials/src/com/earth2me/essentials/Enchantments.java index 3553142fa..5ce10d75d 100644 --- a/Essentials/src/com/earth2me/essentials/Enchantments.java +++ b/Essentials/src/com/earth2me/essentials/Enchantments.java @@ -57,7 +57,9 @@ public class Enchantments ENCHANTMENTS.put("firearrow", Enchantment.ARROW_FIRE); ENCHANTMENTS.put("arrowdamage", Enchantment.ARROW_DAMAGE); ENCHANTMENTS.put("arrowknockback", Enchantment.ARROW_KNOCKBACK); + ENCHANTMENTS.put("arrowkb", Enchantment.ARROW_KNOCKBACK); ENCHANTMENTS.put("infinitearrows", Enchantment.ARROW_INFINITE); + ENCHANTMENTS.put("infarrows", Enchantment.ARROW_INFINITE); } public static Enchantment getByName(String name) { From 8fc66842905549f7ed9e7906f7be65e6c7a30cbb Mon Sep 17 00:00:00 2001 From: KHobbits Date: Tue, 17 Jan 2012 17:24:22 +0000 Subject: [PATCH 38/39] Auto Update GM Version --- build.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/build.xml b/build.xml index 6d99694fb..c69c0f075 100644 --- a/build.xml +++ b/build.xml @@ -2,6 +2,7 @@ + From 89c41b0508edd847316a293926fb56a7b249e6ba Mon Sep 17 00:00:00 2001 From: ElgarL Date: Tue, 17 Jan 2012 18:03:16 +0000 Subject: [PATCH 39/39] v 1.9: Optimize populating Bukkit perms so we no longer calculate the child nodes (Bukkit already does this). --- EssentialsGroupManager/src/Changelog.txt | 4 +- .../permissions/AnjoPermissionsHandler.java | 61 ++++++++++++------- .../permissions/BukkitPermissions.java | 18 ++++-- .../PermissionsReaderInterface.java | 2 + EssentialsGroupManager/src/plugin.yml | 3 +- 5 files changed, 58 insertions(+), 30 deletions(-) diff --git a/EssentialsGroupManager/src/Changelog.txt b/EssentialsGroupManager/src/Changelog.txt index 71a4c3482..876dc28b7 100644 --- a/EssentialsGroupManager/src/Changelog.txt +++ b/EssentialsGroupManager/src/Changelog.txt @@ -101,4 +101,6 @@ v 1.8: - Major, MAJOR changes to support partial/full world mirroring. You can now mirror groups.yml, users.yml or both files between different worlds. - Catch NullPointerErrors generated by blank permission nodes. - - Removed '- bukkit.command' form the globalgroups permission nodes. \ No newline at end of file + - Removed '- bukkit.command' form the globalgroups permission nodes. +v 1.9: + - Optimize populating Bukkit perms so we no longer calculate the child nodes (Bukkit already does this). \ No newline at end of file diff --git a/EssentialsGroupManager/src/org/anjocaido/groupmanager/permissions/AnjoPermissionsHandler.java b/EssentialsGroupManager/src/org/anjocaido/groupmanager/permissions/AnjoPermissionsHandler.java index b23fc01d4..167103796 100644 --- a/EssentialsGroupManager/src/org/anjocaido/groupmanager/permissions/AnjoPermissionsHandler.java +++ b/EssentialsGroupManager/src/org/anjocaido/groupmanager/permissions/AnjoPermissionsHandler.java @@ -90,13 +90,24 @@ public class AnjoPermissionsHandler extends PermissionsReaderInterface { /** * Returns All permissions (including inheritance and sub groups) for the - * player. + * player, including child nodes from Bukkit. * * @param userName * @return List of all players permissions. */ @Override public List getAllPlayersPermissions(String userName) { + return getAllPlayersPermissions(userName, true); + } + /** + * Returns All permissions (including inheritance and sub groups) for the + * player. With or without Bukkit child nodes. + * + * @param userName + * @return List of all players permissions. + */ + @Override + public List getAllPlayersPermissions(String userName, Boolean includeChildren) { List playerPermArray = new ArrayList(); @@ -104,14 +115,16 @@ public class AnjoPermissionsHandler extends PermissionsReaderInterface { if ((!playerPermArray.contains(perm)) && (!playerPermArray.contains("-" + perm))) { playerPermArray.add(perm); - Map children = GroupManager.BukkitPermissions.getAllChildren(perm, playerPermArray); - - if (children != null) { - for (String child : children.keySet()) { - if (children.get(child)) - if ((!playerPermArray.contains(child)) && (!playerPermArray.contains("-" + child))) { - playerPermArray.add(child); - } + if (includeChildren) { + Map children = GroupManager.BukkitPermissions.getAllChildren(perm, playerPermArray); + + if (children != null) { + for (String child : children.keySet()) { + if (children.get(child)) + if ((!playerPermArray.contains(child)) && (!playerPermArray.contains("-" + child))) { + playerPermArray.add(child); + } + } } } } @@ -122,12 +135,14 @@ public class AnjoPermissionsHandler extends PermissionsReaderInterface { if ((!playerPermArray.contains(perm)) && (!playerPermArray.contains("-" + perm))) { playerPermArray.add(perm); - Map children = GroupManager.BukkitPermissions.getAllChildren(perm, playerPermArray); - if (children != null) { - for (String child : children.keySet()) { - if (children.get(child)) - if ((!playerPermArray.contains(child)) && (!playerPermArray.contains("-" + child))) - playerPermArray.add(child); + if (includeChildren) { + Map children = GroupManager.BukkitPermissions.getAllChildren(perm, playerPermArray); + if (children != null) { + for (String child : children.keySet()) { + if (children.get(child)) + if ((!playerPermArray.contains(child)) && (!playerPermArray.contains("-" + child))) + playerPermArray.add(child); + } } } } @@ -137,13 +152,15 @@ public class AnjoPermissionsHandler extends PermissionsReaderInterface { if ((!playerPermArray.contains(perm)) && (!playerPermArray.contains("-" + perm))) { playerPermArray.add(perm); - Map children = GroupManager.BukkitPermissions.getAllChildren(perm, playerPermArray); - if (children != null) { - for (String child : children.keySet()) { - if (children.get(child)) - if ((!playerPermArray.contains(child)) && (!playerPermArray.contains("-" + child))) { - playerPermArray.add(child); - } + if (includeChildren) { + Map children = GroupManager.BukkitPermissions.getAllChildren(perm, playerPermArray); + if (children != null) { + for (String child : children.keySet()) { + if (children.get(child)) + if ((!playerPermArray.contains(child)) && (!playerPermArray.contains("-" + child))) { + playerPermArray.add(child); + } + } } } } diff --git a/EssentialsGroupManager/src/org/anjocaido/groupmanager/permissions/BukkitPermissions.java b/EssentialsGroupManager/src/org/anjocaido/groupmanager/permissions/BukkitPermissions.java index ebaadf8bd..91f9a9bd6 100644 --- a/EssentialsGroupManager/src/org/anjocaido/groupmanager/permissions/BukkitPermissions.java +++ b/EssentialsGroupManager/src/org/anjocaido/groupmanager/permissions/BukkitPermissions.java @@ -79,7 +79,7 @@ public class BukkitPermissions { public BukkitPermissions(GroupManager plugin) { this.plugin = plugin; - //this.collectPermissions(); + this.collectPermissions(); this.registerEvents(); this.updateAllPlayers(); @@ -105,15 +105,20 @@ public class BukkitPermissions { manager.registerEvent(Event.Type.PLUGIN_DISABLE, serverListener, Event.Priority.Normal, plugin); } - /* + public void collectPermissions() { registeredPermissions.clear(); + /* for (Plugin bukkitPlugin : Bukkit.getServer().getPluginManager().getPlugins()) { for (Permission permission : bukkitPlugin.getDescription().getPermissions()) registeredPermissions.push(permission); } + */ + + registeredPermissions = new LinkedList(Bukkit.getPluginManager().getPermissions()); + } - */ + public void updatePermissions(Player player) { this.updatePermissions(player, null); @@ -194,7 +199,7 @@ public class BukkitPermissions { // Add all permissions for this player (GM only) // child nodes will be calculated by Bukkit. - List playerPermArray = worldData.getPermissionsHandler().getAllPlayersPermissions(player.getName()); + List playerPermArray = worldData.getPermissionsHandler().getAllPlayersPermissions(player.getName(), false); Map newPerms = new HashMap(); for (String permission : playerPermArray) { @@ -210,6 +215,7 @@ public class BukkitPermissions { */ newPerms.put(permission, value); } + //player.recalculatePermissions(); /** @@ -373,13 +379,13 @@ public class BukkitPermissions { if (!GroupManager.isLoaded()) return; - //collectPermissions(); + collectPermissions(); updateAllPlayers(); } @Override public void onPluginDisable(PluginDisableEvent event) { - // collectPermissions(); + collectPermissions(); // updateAllPlayers(); } } diff --git a/EssentialsGroupManager/src/org/anjocaido/groupmanager/permissions/PermissionsReaderInterface.java b/EssentialsGroupManager/src/org/anjocaido/groupmanager/permissions/PermissionsReaderInterface.java index c0383c160..cf11bd37f 100644 --- a/EssentialsGroupManager/src/org/anjocaido/groupmanager/permissions/PermissionsReaderInterface.java +++ b/EssentialsGroupManager/src/org/anjocaido/groupmanager/permissions/PermissionsReaderInterface.java @@ -234,4 +234,6 @@ public abstract class PermissionsReaderInterface { ////////////////////////////// public abstract List getAllPlayersPermissions(String userName); + + public abstract List getAllPlayersPermissions(String userName, Boolean includeChildren); } diff --git a/EssentialsGroupManager/src/plugin.yml b/EssentialsGroupManager/src/plugin.yml index da5164ce1..dc9b4c29c 100644 --- a/EssentialsGroupManager/src/plugin.yml +++ b/EssentialsGroupManager/src/plugin.yml @@ -1,11 +1,12 @@ name: GroupManager -version: "1.8 (Phoenix)" +version: GMBuildVer (Phoenix) main: org.anjocaido.groupmanager.GroupManager website: http://www.anjocaido.info/ description: Provides on-the-fly system for permissions system created by Nijikokun. But all in memory, and with flat-file saving schedule. authors: - AnjoCaido - Gabriel Couto + - ElgarL commands: manuadd: description: Move a player to desired group.(Adds to the file if not exists)