diff --git a/Figures for documentation/Resolution of permission tree.PNG b/Figures for documentation/Resolution of permission tree.PNG
new file mode 100644
index 0000000..ffe7147
Binary files /dev/null and b/Figures for documentation/Resolution of permission tree.PNG differ
diff --git a/Figures for documentation/Resolution of permission tree.pptx b/Figures for documentation/Resolution of permission tree.pptx
new file mode 100644
index 0000000..c63d4aa
Binary files /dev/null and b/Figures for documentation/Resolution of permission tree.pptx differ
diff --git a/Figures for documentation/rscPermissions-WebUI Concept.png b/Figures for documentation/rscPermissions-WebUI Concept.png
new file mode 100644
index 0000000..286ac97
Binary files /dev/null and b/Figures for documentation/rscPermissions-WebUI Concept.png differ
diff --git a/nb-configuration.xml b/nb-configuration.xml
new file mode 100644
index 0000000..b9cdb90
--- /dev/null
+++ b/nb-configuration.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+ ______JDK_1.7_Update_51
+
+
diff --git a/nbactions.xml b/nbactions.xml
new file mode 100644
index 0000000..9177298
--- /dev/null
+++ b/nbactions.xml
@@ -0,0 +1,46 @@
+
+
+
+ run
+
+ jar
+
+
+ process-classes
+ org.codehaus.mojo:exec-maven-plugin:1.2.1:exec
+
+
+ -classpath %classpath ru.simsonic.rscPermissions.MainPluginClass
+ java
+
+
+
+ debug
+
+ jar
+
+
+ process-classes
+ org.codehaus.mojo:exec-maven-plugin:1.2.1:exec
+
+
+ -Xdebug -Xrunjdwp:transport=dt_socket,server=n,address=${jpda.address} -classpath %classpath ru.simsonic.rscPermissions.MainPluginClass
+ java
+ true
+
+
+
+ profile
+
+ jar
+
+
+ process-classes
+ org.codehaus.mojo:exec-maven-plugin:1.2.1:exec
+
+
+ -classpath %classpath ru.simsonic.rscPermissions.MainPluginClass
+ java
+
+
+
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..67284fe
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,170 @@
+
+ 4.0.0
+
+ ru.simsonic
+ rscPermissions
+ 0.8.10b
+ jar
+
+ rscPermissions
+ http://maven.apache.org
+
+
+ UTF-8
+
+
+
+ ${project.artifactId}_v${project.version}
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.1
+
+ 1.7
+ 1.7
+ true
+ false
+ true
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 2.4
+
+
+ false
+
+
+ .*
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 2.2
+
+
+ package
+
+ shade
+
+
+ true
+
+
+ ru.simsonic:rscAPI
+
+
+ false
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 2.9.1
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+ public
+ true
+
+
+
+
+
+ src/main/resources
+ true
+
+
+
+
+
+
+ bukkit-repo
+ http://repo.bukkit.org/content/groups/public
+
+
+
+ sk89q-repo
+ http://maven.sk89q.com/repo/
+
+
+ Residence
+ http://ci.drtshock.net/plugin/repository/everything/
+
+
+
+
+ voxile.ru
+ http://ci.voxile.ru/plugin/repository/everything/
+
+
+
+
+ mysql
+ mysql-connector-java
+ 5.1.30
+ runtime
+
+
+ org.bukkit
+ bukkit
+ 1.7.5-R0.1-SNAPSHOT
+ provided
+ jar
+
+
+ com.sk89q
+ worldedit
+ 5.6
+ compile
+ jar
+
+
+ com.sk89q
+ worldguard
+ 5.9.1-SNAPSHOT
+ provided
+ jar
+
+
+ net.t00thpick1
+ Residence
+ 3.0-SNAPSHOT
+ provided
+ jar
+
+
+ ru.simsonic
+ rscAPI
+ 1.2.0
+ compile
+ jar
+
+
+
\ No newline at end of file
diff --git a/src/main/java/ru/simsonic/rscPermissions/AsyncPlayerInfo.java b/src/main/java/ru/simsonic/rscPermissions/AsyncPlayerInfo.java
new file mode 100644
index 0000000..763a7c1
--- /dev/null
+++ b/src/main/java/ru/simsonic/rscPermissions/AsyncPlayerInfo.java
@@ -0,0 +1,97 @@
+package ru.simsonic.rscPermissions;
+import java.util.Set;
+import java.util.UUID;
+import org.bukkit.Location;
+import org.bukkit.entity.Player;
+import ru.simsonic.rscPermissions.DataTypes.RowInheritance;
+import ru.simsonic.rscPermissions.DataTypes.RowPermission;
+import ru.simsonic.rscPermissions.LocalCacheTree.ResolutionLeaf;
+
+public class AsyncPlayerInfo
+{
+ public Player player;
+ public String name;
+ public UUID uuid;
+ public int expirience;
+ public Location location;
+ public Set regions;
+ public AsyncPlayerInfo()
+ {
+ }
+ public AsyncPlayerInfo(String playerName)
+ {
+ this.name = playerName;
+ }
+ public AsyncPlayerInfo(UUID playerUniqueId)
+ {
+ this.uuid = playerUniqueId;
+ }
+ public AsyncPlayerInfo(Player player, Set regions)
+ {
+ if(player != null)
+ {
+ this.player = player;
+ try
+ {
+ // minecraft <= 1.7
+ this.name = player.getName();
+ } catch(RuntimeException | NoSuchMethodError ex) {
+ // minecraft >= 1.8
+ }
+ try
+ {
+ // minecraft >= 1.8
+ this.uuid = player.getUniqueId();
+ } catch(RuntimeException | NoSuchMethodError ex) {
+ // minecraft <= 1.7
+ }
+ this.expirience = player.getLevel();
+ this.location = player.getLocation();
+ this.regions = regions;
+ }
+ }
+ public boolean isPlayerEntityApplicable(String entity)
+ {
+ // Test by UUID (minecraft >= 1.8)
+ try
+ {
+ if(this.uuid.compareTo(UUID.fromString(entity)) == 0)
+ return true;
+ } catch(RuntimeException ex) {
+ // Server doesn't support this yet
+ }
+ // Test by name (minecraft <= 1.7)
+ try
+ {
+ if(this.name.equalsIgnoreCase(entity))
+ return true;
+ } catch(RuntimeException ex) {
+ // Server already doesn't support this
+ }
+ return false;
+ }
+ public boolean isPlayerPermissionApplicable(RowPermission row)
+ {
+ if(isPlayerEntityApplicable(row.entity) || "".equals(row.entity))
+ return (row.destination.IsLocationApplicable(location, regions, null) && row.expirience <= expirience);
+ return false;
+ }
+ public boolean isGroupPermissionApplicable(RowPermission row, ResolutionLeaf leaf)
+ {
+ if(row.entity.equalsIgnoreCase(leaf.group) || "".equals(row.entity))
+ return (row.destination.IsLocationApplicable(location, regions, leaf.instance) && row.expirience <= expirience);
+ return false;
+ }
+ public boolean isPlayerInheritanceApplicable(RowInheritance row)
+ {
+ if(isPlayerEntityApplicable(row.entity))
+ return (row.destination.IsLocationApplicable(location, regions, row.instance) && row.expirience <= expirience);
+ return false;
+ }
+ public boolean isGroupInheritanceApplicable(RowInheritance row, ResolutionLeaf leaf)
+ {
+ if(row.entity.equalsIgnoreCase(leaf.group))
+ return (row.destination.IsLocationApplicable(location, regions, leaf.instance) && row.expirience <= expirience);
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/simsonic/rscPermissions/Backends/Backend.java b/src/main/java/ru/simsonic/rscPermissions/Backends/Backend.java
new file mode 100644
index 0000000..fb77218
--- /dev/null
+++ b/src/main/java/ru/simsonic/rscPermissions/Backends/Backend.java
@@ -0,0 +1,26 @@
+package ru.simsonic.rscPermissions.Backends;
+import ru.simsonic.rscPermissions.DataTypes.RowEntity;
+import ru.simsonic.rscPermissions.DataTypes.RowInheritance;
+import ru.simsonic.rscPermissions.DataTypes.RowLadder;
+import ru.simsonic.rscPermissions.DataTypes.RowPermission;
+import ru.simsonic.rscPermissions.DataTypes.RowServer;
+import ru.simsonic.rscPermissions.LocalCacheData;
+
+public interface Backend
+{
+ public abstract boolean canRead();
+ public abstract boolean canWrite();
+
+ public abstract void fetchIntoCache(LocalCacheData cache);
+ public abstract RowEntity[] fetchEntities();
+ public abstract RowPermission[] fetchPermissions();
+ public abstract RowInheritance[] fetchInheritance();
+ public abstract RowLadder[] fetchLadders();
+ public abstract RowServer[] fetchServers();
+
+ public abstract void insertExampleRows();
+ public abstract void updateEntityText(String entity, boolean entity_type, String text, boolean isPrefix);
+ public abstract void setUserRank(String user, String ladder, int rank);
+ public abstract void dropUserFromLadder(String user, String ladder);
+ public abstract void addUserParentGroup(String user, String newGroup);
+}
\ No newline at end of file
diff --git a/src/main/java/ru/simsonic/rscPermissions/Backends/BackendMySQL.java b/src/main/java/ru/simsonic/rscPermissions/Backends/BackendMySQL.java
new file mode 100644
index 0000000..79137d8
--- /dev/null
+++ b/src/main/java/ru/simsonic/rscPermissions/Backends/BackendMySQL.java
@@ -0,0 +1,351 @@
+package ru.simsonic.rscPermissions.Backends;
+import ru.simsonic.utilities.*;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.logging.Level;
+import ru.simsonic.rscPermissions.DataTypes.Destination;
+import ru.simsonic.rscPermissions.DataTypes.RowEntity;
+import ru.simsonic.rscPermissions.DataTypes.RowEntity.EntityType;
+import ru.simsonic.rscPermissions.DataTypes.RowInheritance;
+import ru.simsonic.rscPermissions.DataTypes.RowLadder;
+import ru.simsonic.rscPermissions.DataTypes.RowPermission;
+import ru.simsonic.rscPermissions.DataTypes.RowReward;
+import ru.simsonic.rscPermissions.DataTypes.RowServer;
+import ru.simsonic.rscPermissions.LocalCacheData;
+import ru.simsonic.rscPermissions.MainPluginClass;
+import ru.simsonic.rscPermissions.Rewards;
+import ru.simsonic.rscPermissions.Settings;
+
+public class BackendMySQL extends ConnectionMySQL implements Backend
+{
+ protected final MainPluginClass plugin;
+ protected static enum WorkMode { read, write, none, }
+ protected WorkMode RememberWork;
+ public BackendMySQL(MainPluginClass plugin)
+ {
+ this.plugin = plugin;
+ }
+ public synchronized void Initialize(String name, String database, String username, String password, String workmode, String prefixes)
+ {
+ super.Initialize(name, database, username, password, prefixes);
+ switch(workmode.toLowerCase())
+ {
+ case "fullaccess":
+ RememberWork = WorkMode.write;
+ break;
+ case "readonly":
+ RememberWork = WorkMode.read;
+ break;
+ case "none":
+ default:
+ RememberWork = WorkMode.none;
+ break;
+ }
+ }
+ @Override
+ public synchronized boolean canRead()
+ {
+ return (RememberWork != WorkMode.none) ? (isConnected() ? true : Connect()) : false;
+ }
+ @Override
+ public synchronized boolean canWrite()
+ {
+ return (RememberWork == WorkMode.write) ? (isConnected() ? true : Connect()) : false;
+ }
+ @Override
+ public synchronized boolean Connect()
+ {
+ if(RememberWork == WorkMode.none)
+ return false;
+ if(super.Connect())
+ {
+ createTablesIfNotExist();
+ cleanupTables();
+ updateServerInfo();
+ return true;
+ }
+ return false;
+ }
+ @Override
+ public synchronized ResultSet executeQuery(String query)
+ {
+ if(canRead() == false)
+ return null;
+ return super.executeQuery(query);
+ }
+ @Override
+ public synchronized boolean executeUpdate(String query)
+ {
+ if(canWrite() == false)
+ return false;
+ return super.executeUpdate(query);
+ }
+ private void createTablesIfNotExist()
+ {
+ executeUpdate(loadResourceSQLT("Initialize_main_v1"));
+ if(plugin.settings.isRewardsEnabled())
+ executeUpdate(loadResourceSQLT("Initialize_rewards_v1"));
+ }
+ private void cleanupTables()
+ {
+ executeUpdate(loadResourceSQLT("Cleanup_tables"));
+ }
+ private void updateServerInfo()
+ {
+ final String mMode = plugin.settings.getMaintenanceMode();
+ setupQueryTemplate("{SERVERID}", plugin.getServer().getServerId());
+ setupQueryTemplate("{PLUGIN_VER}", plugin.getDescription().getVersion());
+ setupQueryTemplate("{DEFAULT}", plugin.settings.getDefaultGroup());
+ setupQueryTemplate("{OP}", plugin.settings.isAsteriskOP() ? "1" : "0");
+ setupQueryTemplate("{DELAY}", Integer.toString(plugin.settings.getAutoReloadDelayTicks() / 20));
+ setupQueryTemplate("{mMode}", "".equals(mMode) ? "NULL" : "\"" + mMode + "\"");
+ setupQueryTemplate("{USE_R}", plugin.settings.isRewardsEnabled() ? "1" : "0");
+ setupQueryTemplate("{CFG_VER}", Integer.toString(plugin.settings.CurrentVersion));
+ executeUpdate(loadResourceSQLT("Update_server_info"));
+ }
+ @Override
+ public synchronized void fetchIntoCache(LocalCacheData cache)
+ {
+ cleanupTables();
+ MainPluginClass.consoleLog.log(Level.INFO,
+ "[rscp] Fetched {0}e, {1}p, {2}i, {3}l, {4}s from \"{5}\".",
+ new Object[]
+ {
+ Integer.toString(cache.ImportEntities(fetchEntities())),
+ Integer.toString(cache.ImportPermissions(fetchPermissions())),
+ Integer.toString(cache.ImportInheritance(fetchInheritance())),
+ Integer.toString(cache.ImportLadders(fetchLadders())),
+ Integer.toString(cache.ImportServers(fetchServers())),
+ RememberName,
+ });
+ }
+ @Override
+ public synchronized RowEntity[] fetchEntities()
+ {
+ final ArrayList result = new ArrayList<>();
+ final ResultSet rs = executeQuery("SELECT * FROM `{DATABASE}`.`{PREFIX}entities`;");
+ try
+ {
+ while(rs.next())
+ {
+ RowEntity row = new RowEntity();
+ row.id = rs.getInt("id");
+ row.entity = rs.getString("entity");
+ row.entity_type = EntityType.byValue(rs.getInt("entity_type"));
+ row.prefix = rs.getString("prefix");
+ row.suffix = rs.getString("suffix");
+ result.add(row);
+ }
+ rs.close();
+ } catch(SQLException ex) {
+ MainPluginClass.consoleLog.log(Level.WARNING, "[rscp] Exception in rs2e(): {0}", ex.getLocalizedMessage());
+ }
+ return result.toArray(new RowEntity[result.size()]);
+ }
+ @Override
+ public synchronized RowPermission[] fetchPermissions()
+ {
+ final ArrayList result = new ArrayList<>();
+ final ResultSet rs = executeQuery("SELECT * FROM `{DATABASE}`.`{PREFIX}permissions`;");
+ final String serverId = plugin.getServer().getServerId();
+ try
+ {
+ while(rs.next())
+ {
+ for(Destination destination : Destination.ParseDestinations(rs.getString("destination")))
+ {
+ if(destination.IsServerIdApplicable(serverId) == false)
+ continue;
+ RowPermission row = new RowPermission();
+ row.id = rs.getInt("id");
+ row.entity = rs.getString("entity");
+ row.entity_type = EntityType.byValue(rs.getInt("entity_type"));
+ row.permission = rs.getString("permission");
+ row.value = rs.getBoolean("value");
+ row.destination = destination;
+ row.expirience = rs.getInt("expirience");
+ row.lifetime = rs.getTimestamp("lifetime");
+ result.add(row);
+ }
+ }
+ rs.close();
+ } catch(SQLException ex) {
+ MainPluginClass.consoleLog.log(Level.WARNING, "[rscp] Exception in rs2p(): {0}", ex.getLocalizedMessage());
+ }
+ return result.toArray(new RowPermission[result.size()]);
+ }
+ @Override
+ public synchronized RowInheritance[] fetchInheritance()
+ {
+ final ArrayList result = new ArrayList<>();
+ final ResultSet rs = executeQuery("SELECT * FROM `{DATABASE}`.`{PREFIX}inheritance`;");
+ final String serverId = plugin.getServer().getServerId();
+ try
+ {
+ while(rs.next())
+ {
+ for(Destination destination : Destination.ParseDestinations(rs.getString("destination")))
+ {
+ if(destination.IsServerIdApplicable(serverId) == false)
+ continue;
+ RowInheritance row = new RowInheritance();
+ row.id = rs.getInt("id");
+ row.entity = rs.getString("entity");
+ row.parent = rs.getString("parent");
+ String[] breaked = row.parent.split(Settings.separatorRegExp);
+ if(breaked.length == 2)
+ {
+ row.parent = breaked[0];
+ row.instance = breaked[1];
+ }
+ row.child_type = EntityType.byValue(rs.getInt("inheritance_type"));
+ row.priority = rs.getInt("inheritance_priority");
+ row.destination = destination;
+ row.expirience = rs.getInt("expirience");
+ row.lifetime = rs.getTimestamp("lifetime");
+ result.add(row);
+ }
+ }
+ rs.close();
+ } catch(SQLException ex) {
+ MainPluginClass.consoleLog.log(Level.WARNING, "[rscp] Exception in rs2i(): {0}", ex.getLocalizedMessage());
+ }
+ return result.toArray(new RowInheritance[result.size()]);
+ }
+ @Override
+ public synchronized RowLadder[] fetchLadders()
+ {
+ final ArrayList result = new ArrayList<>();
+ final ResultSet rs = executeQuery("SELECT * FROM `{DATABASE}`.`{PREFIX}ladders`;");
+ try
+ {
+ while(rs.next())
+ {
+ RowLadder row = new RowLadder();
+ row.id = rs.getInt("id");
+ row.climber = rs.getString("climber");
+ if("".equals(row.climber))
+ row.climber = null;
+ row.climber_type = EntityType.byValue(rs.getInt("climber_type"));
+ row.ladder = rs.getString("ladder");
+ String[] breaked = row.ladder.split(Settings.separatorRegExp);
+ if(breaked.length == 2)
+ {
+ row.ladder = breaked[0];
+ row.instance = breaked[1];
+ }
+ row.rank = rs.getInt("rank");
+ result.add(row);
+ }
+ rs.close();
+ } catch(SQLException ex) {
+ MainPluginClass.consoleLog.log(Level.WARNING, "[rscp] Exception in rs2l(): {0}", ex.getLocalizedMessage());
+ }
+ return result.toArray(new RowLadder[result.size()]);
+ }
+ @Override
+ public synchronized RowServer[] fetchServers()
+ {
+ final ArrayList result = new ArrayList<>();
+ final ResultSet rs = executeQuery("SELECT * FROM `{DATABASE}`.`{PREFIX}servers`;");
+ try
+ {
+ while(rs.next())
+ {
+ RowServer row = new RowServer();
+ row.serverId = rs.getString("serverId");
+ // PARSE OTHER COLUMNS HERE
+ result.add(row);
+ }
+ rs.close();
+ } catch(SQLException ex) {
+ MainPluginClass.consoleLog.log(Level.WARNING, "[rscp] Exception in rs2s(): {0}", ex.getLocalizedMessage());
+ }
+ return result.toArray(new RowServer[result.size()]);
+ }
+ public synchronized void fetchRewards(Rewards rewardHelper)
+ {
+ final ArrayList result = new ArrayList<>();
+ final ResultSet rs = executeQuery("SELECT * FROM `{DATABASE}`.`{PREFIX}rewards`;");
+ try
+ {
+ while(rs.next())
+ {
+ RowReward row = new RowReward();
+ row.id = rs.getInt("id");
+ row.user = rs.getString("user").toLowerCase();
+ row.code = rs.getString("code");
+ row.activated = rs.getBoolean("activated");
+ if(row.activated)
+ continue;
+ row.activated_timestamp = rs.getTimestamp("activated_timestamp");
+ row.execute_commands = rs.getString("execute_commands");
+ row.command_permissions = rs.getString("command_permissions");
+ row.add_group = rs.getString("add_group");
+ row.add_group_destination = rs.getString("add_group_destination");
+ row.add_group_expirience = rs.getInt("add_group_expirience");
+ row.add_group_interval = rs.getString("add_group_interval");
+ result.add(row);
+ }
+ rs.close();
+ } catch(SQLException ex) {
+ MainPluginClass.consoleLog.log(Level.WARNING, "[rscp] Exception in rs2r(): {0}", ex.getLocalizedMessage());
+ }
+ MainPluginClass.consoleLog.log(Level.INFO, "[rscp] Fetched {0} unused reward codes.",
+ Integer.toString(result.size()));
+ rewardHelper.ImportRewards(result.toArray(new RowReward[result.size()]));
+ }
+ @Override
+ public synchronized void insertExampleRows()
+ {
+ executeUpdate(loadResourceSQLT("Insert_example_rows_v1"));
+ }
+ @Override
+ public synchronized void updateEntityText(String entity, boolean entity_type, String text, boolean isPrefix)
+ {
+ if("".equals(entity))
+ return;
+ if("".equals(text) || "\"\"".equals(text))
+ text = null;
+ setupQueryTemplate("{ENTITY}", entity);
+ setupQueryTemplate("{ENTITY_TYPE}", entity_type ? "1" : "0");
+ setupQueryTemplate("{TEXT_TYPE}", isPrefix ? "prefix" : "suffix");
+ setupQueryTemplate("{TEXT}", (text != null) ? "'" + text + "'" : "NULL");
+ executeUpdate(loadResourceSQLT("Update_entity_text"));
+ }
+ @Override
+ public synchronized void setUserRank(String user, String ladder, int rank)
+ {
+ if("".equals(user) || "".equals(ladder))
+ return;
+ setupQueryTemplate("{USER}", user);
+ setupQueryTemplate("{LADDER}", ladder);
+ setupQueryTemplate("{RANK}", Integer.toString(rank));
+ executeUpdate(loadResourceSQLT("Set_user_rank"));
+ }
+ @Override
+ public synchronized void dropUserFromLadder(String user, String ladder)
+ {
+ String instance = "";
+ String[] breaked = ladder.split(Settings.separatorRegExp);
+ if(breaked.length == 2)
+ {
+ ladder = breaked[0];
+ instance = breaked[1];
+ }
+ if("".equals(user) || "".equals(ladder))
+ return;
+ setupQueryTemplate("{USER}", user);
+ setupQueryTemplate("{LADDER}", ladder);
+ setupQueryTemplate("{INSTANCE}", instance);
+ executeUpdate(loadResourceSQLT("Drop_user_from_ladder"));
+ }
+ @Override
+ public synchronized void addUserParentGroup(String user, String newGroup)
+ {
+ setupQueryTemplate("{USER}", user);
+ setupQueryTemplate("{PARENT}", newGroup);
+ executeUpdate("INSERT INTO `{DATABASE}`.`{PREFIX}inheritance` (`entity`, `parent`, `inheritance_type`) VALUES ('{USER}', '{PARENT}', b'1');");
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/simsonic/rscPermissions/CommandHelper.java b/src/main/java/ru/simsonic/rscPermissions/CommandHelper.java
new file mode 100644
index 0000000..d117b4a
--- /dev/null
+++ b/src/main/java/ru/simsonic/rscPermissions/CommandHelper.java
@@ -0,0 +1,331 @@
+package ru.simsonic.rscPermissions;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Map;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+import org.bukkit.permissions.PermissionAttachment;
+import ru.simsonic.rscPermissions.Importers.PermissionsEx_YAML;
+
+public class CommandHelper
+{
+ private final MainPluginClass plugin;
+ public final Rewards rewardHelper;
+ public final Ladders ladderHelper;
+ public CommandHelper(final MainPluginClass rscp)
+ {
+ this.plugin = rscp;
+ rewardHelper = new Rewards(rscp);
+ ladderHelper = new Ladders(rscp);
+ }
+ public void onCommand(CommandSender sender, Command cmd, String label, String[] args) throws CommandHelperAnswerException
+ {
+ switch(cmd.getName().toLowerCase())
+ {
+ case "rscp":
+ onCommandHub(sender, args);
+ return;
+ case "promote":
+ if(args.length >= 1)
+ {
+ ladderHelper.executePromotion(sender, args[0], (args.length >= 2) ? args[1] : null, true);
+ return;
+ }
+ throw new CommandHelperAnswerException("/promote ");
+ case "demote":
+ if(args.length >= 1)
+ {
+ ladderHelper.executePromotion(sender, args[0], (args.length >= 2) ? args[1] : null, false);
+ return;
+ }
+ throw new CommandHelperAnswerException("/demote ");
+ case "reward":
+ if(sender instanceof Player)
+ {
+ String reward = (args.length >= 1) ? args[0] : null;
+ rewardHelper.executeReward((Player)sender, reward);
+ return;
+ }
+ throw new CommandHelperAnswerException("This command cannot be run from console.");
+ }
+ }
+ private void onCommandHub(CommandSender sender, String[] args) throws CommandHelperAnswerException
+ {
+ final ArrayList help = new ArrayList<>();
+ if(sender.hasPermission("rscp.admin"))
+ {
+ help.add("/rscp (user|group|ladder) -- PermissionsEx-like admin commands");
+ help.add("/rscp (promote|demote) -- admin promotion/demotion commands");
+ }
+ if(sender.hasPermission("rscp.admin.lock"))
+ help.add("/rscp (lock|unlock) -- maintenance mode control");
+ if(sender.hasPermission("rscp.admin"))
+ {
+ help.add("/rscp (examplerows|import) -- possible useful things");
+ help.add("/rscp (debug|fetch|reload) -- admin stuff");
+ }
+ help.add("/rscp (help) -- show these notes");
+ if(help.size() > 0)
+ help.add(0, "{MAGENTA}Usage:");
+ help.add(0, plugin.getDescription().getName() + " v" + plugin.getDescription().getVersion());
+ help.add(1, "Perfect Superperms manager for multiserver environments");
+ if(sender.hasPermission("rscp.admin"))
+ help.add(2, "{_DS}Current serverId is \'{_LS}" + plugin.getServer().getServerId() + "{_DS}\' (server.properties)");
+ help.add("{_LG}" + plugin.getDescription().getWebsite());
+ if(args.length == 0)
+ throw new CommandHelperAnswerException(help);
+ switch(args[0].toLowerCase())
+ {
+ case "user":
+ onCommandHubUser(sender, args);
+ return;
+ case "group":
+ onCommandHubGroup(sender, args);
+ return;
+ case "ladder":
+ onCommandHubLadder(sender, args);
+ return;
+ case "promote":
+ /* rscp promote */
+ if(args.length < 3)
+ throw new CommandHelperAnswerException("/rscp promote ");
+ ladderHelper.executePromotion(sender, args[1], args[2], true);
+ return;
+ case "demote":
+ /* rscp demote */
+ if(args.length < 3)
+ throw new CommandHelperAnswerException("/rscp demote ");
+ ladderHelper.executePromotion(sender, args[1], args[2], false);
+ return;
+ case "lock":
+ /* rscp lock [mMode] */
+ if(sender.hasPermission("rscp.lock"))
+ {
+ final String mMode = (args.length >= 2) ? args[1] : "default";
+ String mmon = "Maintenance mode enabled";
+ mmon = plugin.getConfig().getString("language.mModes.locked.default.mmon", mmon);
+ mmon = plugin.getConfig().getString("language.mModes.locked." + mMode + ".mmon", mmon);
+ plugin.maintenance.setMaintenanceMode(mMode);
+ throw new CommandHelperAnswerException(mmon);
+ }
+ return;
+ case "unlock":
+ /* rscp unlock */
+ if(sender.hasPermission("rscp.lock"))
+ {
+ String mmoff = "Maintenance mode disabled";
+ mmoff = plugin.getConfig().getString("language.mModes.unlocked", mmoff);
+ plugin.maintenance.setMaintenanceMode(null);
+ throw new CommandHelperAnswerException(mmoff);
+ }
+ break;
+ case "examplerows":
+ /* rscp examplerows */
+ if(sender.hasPermission("rscp.admin"))
+ {
+ plugin.connectionList.threadInsertExampleRows(sender);
+ throw new CommandHelperAnswerException("Example rows have been added into database.");
+ }
+ return;
+ case "import":
+ /* rscp import pex */
+ if(sender.hasPermission("rscp.admin"))
+ {
+ if(args.length > 1)
+ switch(args[1].toLowerCase())
+ {
+ case "pex-yaml":
+ if(args.length == 2)
+ break;
+ // TO DO HERE
+ PermissionsEx_YAML importer_pex = new PermissionsEx_YAML(plugin, args[2]);
+ plugin.connectionList.threadFetchTablesData();
+ throw new CommandHelperAnswerException(new String[]
+ {
+ "Data has been imported successfully!",
+ "Entities: {MAGENTA}" + Integer.toString(importer_pex.getEntities().length),
+ "Permissions: {MAGENTA}" + Integer.toString(importer_pex.getPermissions().length),
+ "Inheritance: {MAGENTA}" + Integer.toString(importer_pex.getInheritance().length),
+ "Ladders: {MAGENTA}" + Integer.toString(importer_pex.getLadders().length),
+ "{_DR}{_B}FAKE :p - all this is undone yet!",
+ });
+ case "pex-sql":
+ plugin.connectionList.threadMigrateFromPExSQL(sender);
+ throw new CommandHelperAnswerException("Trying to import PEX database into rscPermissions...");
+ }
+ throw new CommandHelperAnswerException(new String[]
+ {
+ "Usage: {GOLD}/rscp import [options]",
+ "Available importers:",
+ "{_LR}pex-yaml{_LS} (PermissionsEx)",
+ "{_LG}pex-sql{_LS} (PermissionsEx)",
+ });
+ }
+ return;
+ case "fetch":
+ /* rscp fetch */
+ if(sender.hasPermission("rscp.admin.reload"))
+ {
+ plugin.connectionList.threadFetchTablesData();
+ throw new CommandHelperAnswerException("Tables have been fetched.");
+ }
+ return;
+ case "reload":
+ /* rscp reload */
+ if(sender.hasPermission("rscp.admin.reload"))
+ {
+ plugin.getServer().getPluginManager().disablePlugin(plugin);
+ plugin.getServer().getPluginManager().enablePlugin(plugin);
+ throw new CommandHelperAnswerException("Plugin has been reloaded.");
+ }
+ return;
+ case "update":
+ /* rscp update */
+ if(sender.hasPermission("rscp.admin"))
+ throw new CommandHelperAnswerException(plugin.doUpdate(sender));
+ return;
+ case "debug":
+ /* rscp debug [yes|on|no|off|toggle] */
+ if(sender.hasPermission("rscp.admin"))
+ throw new CommandHelperAnswerException("Not implemented yet.");
+ return;
+ case "help":
+ default:
+ throw new CommandHelperAnswerException(help);
+ }
+ }
+ private void onCommandHubUser(CommandSender sender, String[] args) throws CommandHelperAnswerException
+ {
+ if(sender.hasPermission("rscp.admin") == false)
+ throw new CommandHelperAnswerException("Not enough permissions.");
+ final String[] help = new String[]
+ {
+ "rscPermissions command hub (user section).",
+ "{MAGENTA}Usage:",
+ "/rscp user list permissions",
+ "/rscp user list groups",
+ // "/rscp user list ranks",
+ "/rscp user prefix [prefix]",
+ "/rscp user suffix [suffix]",
+ };
+ if(args.length < 3)
+ throw new CommandHelperAnswerException(help);
+ final Player player = plugin.getServer().getPlayerExact(args[1]);
+ if(player == null)
+ throw new CommandHelperAnswerException("Player should be online");
+ final ArrayList list = new ArrayList<>();
+ switch(args[2].toLowerCase())
+ {
+ case "list":
+ if(args.length < 4)
+ throw new CommandHelperAnswerException(help);
+ switch(args[3].toLowerCase())
+ {
+ case "permissions":
+ list.add("{MAGENTA}Permission list for {_YL}" + player.getName());
+ final PermissionAttachment pa = plugin.attachments.get(player);
+ if(pa == null)
+ break;
+ final Map pv = pa.getPermissions();
+ if(pv == null)
+ break;
+ final ArrayList sorted_keys = new ArrayList<>(pv.keySet());
+ Collections.sort(sorted_keys);
+ for(String perm : sorted_keys)
+ if(pv.containsKey(perm))
+ list.add((pv.get(perm) ? "{_LG}" : "{_LR}") + perm);
+ throw new CommandHelperAnswerException(list);
+ case "groups":
+ list.add("{MAGENTA}Group list for {_YL}" + player.getName() + "{MAGENTA}:");
+ ArrayList groups = plugin.cache.getUserGroups(player.getName());
+ for(String group : groups)
+ list.add("{_LG}" + group);
+ throw new CommandHelperAnswerException(list);
+ /*
+ case "ranks":
+ list.add("{MAGENTA}Ranks of player {_YL}" + player.getName() + "{MAGENTA}:");
+ throw new CommandHelperAnswerException(list);
+ */
+ }
+ throw new CommandHelperAnswerException(list);
+ case "prefix":
+ if(args.length > 3)
+ {
+ plugin.API.setPlayerPrefix(null, player.getName(), args[3]);
+ list.add("{MAGENTA}Prefix for user {_YL}" + player.getName() +
+ " {MAGENTA}has been set to \"{_R}" + plugin.cache.userGetPrefix(player.getName()) + "{MAGENTA}\".");
+ } else
+ list.add("{MAGENTA}Prefix for user {_YL}" + player.getName() +
+ " {MAGENTA}is \"{_R}" + plugin.cache.userGetPrefix(player.getName()) + "{MAGENTA}\".");
+ throw new CommandHelperAnswerException(list);
+ case "suffix":
+ if(args.length > 3)
+ {
+ plugin.API.setPlayerSuffix(null, player.getName(), args[3]);
+ list.add("{MAGENTA}Suffix for user {_YL}" + player.getName() +
+ " {MAGENTA}has been set to \"{_R}" + plugin.cache.userGetSuffix(player.getName()) + "{MAGENTA}\".");
+ } else
+ list.add("{MAGENTA}Suffix for user {_YL}" + player.getName() +
+ " {MAGENTA}is \"{_R}" + plugin.cache.userGetSuffix(player.getName()) + "{MAGENTA}\".");
+ throw new CommandHelperAnswerException(list);
+ }
+ }
+ private void onCommandHubGroup(CommandSender sender, String[] args) throws CommandHelperAnswerException
+ {
+ if(sender.hasPermission("rscp.admin") == false)
+ throw new CommandHelperAnswerException("Not enough permissions.");
+ final String[] help = new String[]
+ {
+ "rscPermissions command hub (group section).",
+ "{MAGENTA}Usage:",
+ // "/rscp group list permissions",
+ // "/rscp group list ranks",
+ "/rscp group prefix [prefix]",
+ "/rscp group suffix [suffix]",
+ };
+ if(args.length < 3)
+ throw new CommandHelperAnswerException(help);
+ final String group = args[1];
+ final ArrayList list = new ArrayList<>();
+ switch(args[2].toLowerCase())
+ {
+ case "prefix":
+ if(args.length > 3)
+ {
+ plugin.API.setGroupPrefix(null, group, args[3]);
+ list.add("{MAGENTA}Prefix for group {_YL}" + group +
+ " {MAGENTA}has been set to \"{_R}" + plugin.cache.groupGetPrefix(group) + "{MAGENTA}\".");
+ } else
+ list.add("{MAGENTA}Prefix for group {_YL}" + group +
+ " {MAGENTA}is \"{_R}" + plugin.cache.groupGetPrefix(group) + "{MAGENTA}\".");
+ throw new CommandHelperAnswerException(list);
+ case "suffix":
+ if(args.length > 3)
+ {
+ plugin.API.setGroupSuffix(null, group, args[3]);
+ list.add("{MAGENTA}Suffix for group {_YL}" + group +
+ " {MAGENTA}has been set to \"{_R}" + plugin.cache.groupGetSuffix(group) + "{MAGENTA}\".");
+ } else
+ list.add("{MAGENTA}Suffix for group {_YL}" + group +
+ " {MAGENTA}is \"{_R}" + plugin.cache.groupGetSuffix(group) + "{MAGENTA}\".");
+ throw new CommandHelperAnswerException(list);
+ }
+ }
+ private void onCommandHubLadder(CommandSender sender, String[] args) throws CommandHelperAnswerException
+ {
+ if(sender.hasPermission("rscp.admin") == false)
+ throw new CommandHelperAnswerException("Not enough permissions.");
+ final String[] help = new String[]
+ {
+ "rscPermissions command hub (ladder section).",
+ "{MAGENTA}Usage:",
+ // "/rscp ladder list groups",
+ // "/rscp ladder list users",
+ };
+ if(args.length < 3)
+ throw new CommandHelperAnswerException(help);
+ final String ladder = args[1];
+ throw new CommandHelperAnswerException("dummy :p)");
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/simsonic/rscPermissions/CommandHelperAnswerException.java b/src/main/java/ru/simsonic/rscPermissions/CommandHelperAnswerException.java
new file mode 100644
index 0000000..4ec68bf
--- /dev/null
+++ b/src/main/java/ru/simsonic/rscPermissions/CommandHelperAnswerException.java
@@ -0,0 +1,23 @@
+package ru.simsonic.rscPermissions;
+import java.util.List;
+
+public class CommandHelperAnswerException extends Exception
+{
+ private final String[] message;
+ public CommandHelperAnswerException(String message)
+ {
+ this.message = new String[]{message};
+ }
+ public CommandHelperAnswerException(String[] messages)
+ {
+ this.message = messages;
+ }
+ public CommandHelperAnswerException(List messages)
+ {
+ this.message = messages.toArray(new String[messages.size()]);
+ }
+ public String[] getMessageArray()
+ {
+ return message;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/simsonic/rscPermissions/ConnectionHelper.java b/src/main/java/ru/simsonic/rscPermissions/ConnectionHelper.java
new file mode 100644
index 0000000..8480796
--- /dev/null
+++ b/src/main/java/ru/simsonic/rscPermissions/ConnectionHelper.java
@@ -0,0 +1,130 @@
+package ru.simsonic.rscPermissions;
+import ru.simsonic.utilities.ConnectionMySQL;
+import java.util.logging.Level;
+import org.bukkit.command.CommandSender;
+import org.bukkit.scheduler.BukkitRunnable;
+import ru.simsonic.rscPermissions.Backends.BackendMySQL;
+
+public class ConnectionHelper extends BackendMySQL
+{
+ private ConnectionHelper nextInChain = null;
+ public ConnectionHelper(MainPluginClass rscp, ConnectionHelper nextInChain)
+ {
+ super(rscp);
+ this.nextInChain = nextInChain;
+ }
+ protected synchronized BackendMySQL findConnectedNode()
+ {
+ for(ConnectionHelper result = this; result != null; result = result.nextInChain)
+ if(result.isConnected())
+ return (BackendMySQL)result;
+ return null;
+ }
+ @Override
+ public void Disconnect()
+ {
+ if(nextInChain != null)
+ {
+ nextInChain.Disconnect();
+ nextInChain = null;
+ }
+ super.Disconnect();
+ }
+ public Thread threadFetchTablesData()
+ {
+ final Thread result = new Thread()
+ {
+ @Override
+ public void run()
+ {
+ // Fetch tables
+ final ConnectionMySQL connection = findConnectedNode();
+ if(connection == null)
+ return;
+ fetchIntoCache(plugin.cache);
+ if(plugin.settings.isRewardsEnabled())
+ fetchRewards(plugin.commandExecutor.rewardHelper);
+ // Update permissions for online players
+ try
+ {
+ Runnable syncTask = new Runnable()
+ {
+ @Override
+ public synchronized void run()
+ {
+ plugin.recalculateOnlinePlayers();
+ notify();
+ }
+ };
+ synchronized(syncTask)
+ {
+ plugin.getServer().getScheduler().runTask(plugin, syncTask);
+ syncTask.wait();
+ }
+ } catch(InterruptedException ex) {
+ MainPluginClass.consoleLog.log(Level.SEVERE, "[rscp] Exception in FetchTables(): {0}", ex.getLocalizedMessage());
+ }
+ plugin.cache.calculateStartupPermissions();
+ }
+ };
+ result.start();
+ return result;
+ }
+ public Thread threadInsertExampleRows(final CommandSender sender)
+ {
+ final Thread result = new Thread()
+ {
+ @Override
+ public void run()
+ {
+ setName("InsertExampleRows");
+ final BackendMySQL backend = findConnectedNode();
+ if(backend == null || !backend.canWrite())
+ return;
+ backend.insertExampleRows();
+ plugin.getServer().getScheduler().runTask(plugin, new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ sender.sendMessage("Database tables were filled with example rows.");
+ }
+ });
+ }
+ };
+ result.start();
+ return result;
+ }
+ public Thread threadMigrateFromPExSQL(final CommandSender sender)
+ {
+ final Thread result = new Thread()
+ {
+ @Override
+ public void run()
+ {
+ try
+ {
+ setName("MigrateFromPExSQL");
+ final BackendMySQL backend = findConnectedNode();
+ if(backend == null || !backend.canWrite())
+ return;
+ backend.executeUpdate(loadResourceSQLT("Migrate_from_PermissionsEx"));
+ threadFetchTablesData().join();
+ plugin.getServer().getScheduler().runTask(plugin, new BukkitRunnable()
+ {
+ @Override
+ public void run()
+ {
+ plugin.Message(sender, "Migration from PermissionsEx (MySQL backend) done!");
+ plugin.Message(sender, "Check the latest database row for new data.");
+ }
+ });
+ } catch(InterruptedException ex) {
+ MainPluginClass.consoleLog.log(Level.SEVERE, "[rscp] Exception in MigrateFromPExSQL(): {0}", ex.getLocalizedMessage());
+ }
+ }
+ };
+ result.start();
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/simsonic/rscPermissions/DataTypes/AbstractRow.java b/src/main/java/ru/simsonic/rscPermissions/DataTypes/AbstractRow.java
new file mode 100644
index 0000000..13e5a93
--- /dev/null
+++ b/src/main/java/ru/simsonic/rscPermissions/DataTypes/AbstractRow.java
@@ -0,0 +1,20 @@
+package ru.simsonic.rscPermissions.DataTypes;
+import java.util.regex.Pattern;
+
+public abstract class AbstractRow
+{
+ public int id = 0;
+ public static enum Table
+ {
+ entities, permissions, inheritance, ladders, unknown;
+ }
+ public abstract Table getTable();
+ private static final Pattern patternUUID = Pattern.compile(
+ "" + "(?:[a-f\\d]{8}(?:-[a-f\\d]{4}){3}-[a-f\\d]{12})" + "");
+ private static boolean isCorrectUUID(String entityName)
+ {
+ if(entityName == null)
+ return false;
+ return patternUUID.matcher("" + entityName.toLowerCase() + "").find();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/simsonic/rscPermissions/DataTypes/Destination.java b/src/main/java/ru/simsonic/rscPermissions/DataTypes/Destination.java
new file mode 100644
index 0000000..f65526e
--- /dev/null
+++ b/src/main/java/ru/simsonic/rscPermissions/DataTypes/Destination.java
@@ -0,0 +1,106 @@
+package ru.simsonic.rscPermissions.DataTypes;
+import java.util.ArrayList;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.bukkit.Location;
+import org.bukkit.World;
+import ru.simsonic.rscPermissions.Settings;
+import ru.simsonic.utilities.LanguageUtility;
+
+public class Destination
+{
+ private final String region;
+ private final String world;
+ private final String serverId;
+ private Destination()
+ {
+ this.region = null;
+ this.world = null;
+ this.serverId = null;
+ }
+ private Destination(String region, String world, String serverId)
+ {
+ this.region = region;
+ this.world = world;
+ this.serverId = serverId;
+ }
+ public boolean IsServerIdApplicable(String serverId)
+ {
+ return wildcardTesting(serverId, this.serverId);
+ }
+ public boolean IsLocationApplicable(Location location, Set regions, String instantiator)
+ {
+ if(location != null)
+ {
+ if(location.getWorld() != null)
+ if(IsWorldApplicable(location.getWorld(), instantiator))
+ return IsRegionApplicable(regions, instantiator);
+ } else {
+ if(this.world == null)
+ return IsRegionApplicable(regions, instantiator);
+ }
+ return false;
+ }
+ private boolean IsWorldApplicable(World world, String instantiator)
+ {
+ if(this.world == null || "".equals(this.world) || "*".equals(this.world))
+ return true;
+ final String instantiated = (instantiator != null && !"".equals(instantiator)) ?
+ this.world.replaceAll(Settings.instantiatorRegExp, instantiator) :
+ this.world;
+ return wildcardTesting(world.getName(), instantiated);
+ }
+ private boolean IsRegionApplicable(Set regions, String instantiator)
+ {
+ if(this.region == null || "".equals(this.region) || "*".equals(this.region))
+ return true;
+ final String instantiated = (instantiator != null && !"".equals(instantiator)) ?
+ this.region.replaceAll(Settings.instantiatorRegExp, instantiator) :
+ this.region;
+ for(String regionId : regions)
+ if(wildcardTesting(regionId, instantiated))
+ return true;
+ return false;
+ }
+ private static boolean wildcardTesting(String testing, String pattern)
+ {
+ if(pattern == null || "".equals(pattern))
+ return true;
+ if(testing == null || "".equals(testing))
+ return false;
+ return LanguageUtility.wildcardMatch(
+ "" + testing.toLowerCase() + "",
+ "" + pattern.toLowerCase() + "");
+ }
+ public static Destination[] ParseDestinations(String destinations)
+ {
+ if(destinations == null)
+ return new Destination[] { new Destination() };
+ if(destinations.isEmpty())
+ return new Destination[] { new Destination() };
+ final String[] destinationsList = destinations.split("\\s*(?:;|,|\\r|\\n)+\\s*");
+ final ArrayList result = new ArrayList(destinationsList.length);
+ for(int nDestination = 0; nDestination < destinationsList.length; nDestination += 1)
+ if(destinationsList[nDestination].isEmpty() == false)
+ result.add(ParseDestination(destinationsList[nDestination]));
+ return result.toArray(new Destination[result.size()]);
+ }
+ private static final Pattern patternDestination = Pattern.compile(
+ "" + "(?:((?:\\w|\\*|\\?)*):)?((?:\\w|\\*|\\?)*)?(?:@((?:\\w|\\*|\\?)*))?" + "");
+ private static Destination ParseDestination(String destination)
+ {
+ Matcher match = patternDestination.matcher("" + destination + "");
+ if(match.find())
+ {
+ final String group1 = match.group(1);
+ final String group2 = match.group(2);
+ final String group3 = match.group(3);
+ final String region = (group1 == null || "".equals(group1)) ? "*" : group1;
+ final String world = (group2 == null || "".equals(group2)) ? "*" : group2;
+ final String serverId = (group3 == null || "".equals(group3)) ? "*" : group3;
+ return new Destination(region, world, serverId);
+ }
+ return new Destination();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/simsonic/rscPermissions/DataTypes/RowEntity.java b/src/main/java/ru/simsonic/rscPermissions/DataTypes/RowEntity.java
new file mode 100644
index 0000000..3f1c1b4
--- /dev/null
+++ b/src/main/java/ru/simsonic/rscPermissions/DataTypes/RowEntity.java
@@ -0,0 +1,33 @@
+package ru.simsonic.rscPermissions.DataTypes;
+
+public class RowEntity extends AbstractRow
+{
+ public enum EntityType
+ {
+ groupName(0),
+ playerName(1),
+ playerUniqueId(2),
+ unknown(-1);
+ public static EntityType byValue(int value)
+ {
+ for(EntityType constant : EntityType.values())
+ if(constant.value == value)
+ return constant;
+ return unknown;
+ }
+ private final int value;
+ private EntityType(int value)
+ {
+ this.value = value;
+ }
+ }
+ public String entity;
+ public EntityType entity_type;
+ public String prefix;
+ public String suffix;
+ @Override
+ public Table getTable()
+ {
+ return Table.entities;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/simsonic/rscPermissions/DataTypes/RowInheritance.java b/src/main/java/ru/simsonic/rscPermissions/DataTypes/RowInheritance.java
new file mode 100644
index 0000000..fab1f37
--- /dev/null
+++ b/src/main/java/ru/simsonic/rscPermissions/DataTypes/RowInheritance.java
@@ -0,0 +1,25 @@
+package ru.simsonic.rscPermissions.DataTypes;
+import java.sql.Timestamp;
+import ru.simsonic.rscPermissions.DataTypes.RowEntity.EntityType;
+
+public class RowInheritance extends AbstractRow implements Comparable
+{
+ public String entity;
+ public String parent;
+ public String instance;
+ public EntityType child_type;
+ public int priority;
+ public Destination destination;
+ public int expirience;
+ public Timestamp lifetime;
+ @Override
+ public int compareTo(RowInheritance t)
+ {
+ return (priority != t.priority) ? priority - t.priority : parent.compareTo(t.parent);
+ }
+ @Override
+ public Table getTable()
+ {
+ return Table.inheritance;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/simsonic/rscPermissions/DataTypes/RowLadder.java b/src/main/java/ru/simsonic/rscPermissions/DataTypes/RowLadder.java
new file mode 100644
index 0000000..f496673
--- /dev/null
+++ b/src/main/java/ru/simsonic/rscPermissions/DataTypes/RowLadder.java
@@ -0,0 +1,45 @@
+package ru.simsonic.rscPermissions.DataTypes;
+import ru.simsonic.rscPermissions.DataTypes.RowEntity.EntityType;
+
+public class RowLadder extends AbstractRow implements Comparable
+{
+ public String climber;
+ public EntityType climber_type;
+ public String ladder;
+ public String instance;
+ public int rank;
+ @Override
+ public int compareTo(RowLadder t)
+ {
+ return rank - t.rank;
+ }
+ public RowLadder nextNode;
+ public RowLadder prevNode;
+ public int getLadderTopRank()
+ {
+ int result = rank;
+ for(RowLadder row = nextNode; row != null; row = nextNode)
+ result = row.rank;
+ return result;
+ }
+ public int getLadderBottomRank()
+ {
+ int result = rank;
+ for(RowLadder row = prevNode; row != null; row = prevNode)
+ result = row.rank;
+ return result;
+ }
+ public RowLadder getActualNode(int userRank)
+ {
+ RowLadder result = this;
+ for(; result.nextNode != null; result = result.nextNode)
+ if(result.nextNode.rank > userRank)
+ break;
+ return result;
+ }
+ @Override
+ public Table getTable()
+ {
+ return Table.ladders;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/simsonic/rscPermissions/DataTypes/RowPermission.java b/src/main/java/ru/simsonic/rscPermissions/DataTypes/RowPermission.java
new file mode 100644
index 0000000..95412a8
--- /dev/null
+++ b/src/main/java/ru/simsonic/rscPermissions/DataTypes/RowPermission.java
@@ -0,0 +1,19 @@
+package ru.simsonic.rscPermissions.DataTypes;
+import java.sql.Timestamp;
+import ru.simsonic.rscPermissions.DataTypes.RowEntity.EntityType;
+
+public class RowPermission extends AbstractRow
+{
+ public String entity;
+ public EntityType entity_type;
+ public String permission;
+ public boolean value;
+ public Destination destination;
+ public int expirience;
+ public Timestamp lifetime;
+ @Override
+ public Table getTable()
+ {
+ return Table.permissions;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/simsonic/rscPermissions/DataTypes/RowReward.java b/src/main/java/ru/simsonic/rscPermissions/DataTypes/RowReward.java
new file mode 100644
index 0000000..d306885
--- /dev/null
+++ b/src/main/java/ru/simsonic/rscPermissions/DataTypes/RowReward.java
@@ -0,0 +1,17 @@
+package ru.simsonic.rscPermissions.DataTypes;
+import java.sql.Timestamp;
+
+public class RowReward
+{
+ public int id;
+ public String user;
+ public String code;
+ public boolean activated;
+ public Timestamp activated_timestamp;
+ public String execute_commands;
+ public String command_permissions;
+ public String add_group;
+ public String add_group_destination;
+ public int add_group_expirience;
+ public String add_group_interval;
+}
\ No newline at end of file
diff --git a/src/main/java/ru/simsonic/rscPermissions/DataTypes/RowServer.java b/src/main/java/ru/simsonic/rscPermissions/DataTypes/RowServer.java
new file mode 100644
index 0000000..20860dc
--- /dev/null
+++ b/src/main/java/ru/simsonic/rscPermissions/DataTypes/RowServer.java
@@ -0,0 +1,6 @@
+package ru.simsonic.rscPermissions.DataTypes;
+
+public class RowServer
+{
+ public String serverId;
+}
\ No newline at end of file
diff --git a/src/main/java/ru/simsonic/rscPermissions/Importers/BaseImporter.java b/src/main/java/ru/simsonic/rscPermissions/Importers/BaseImporter.java
new file mode 100644
index 0000000..0eb4b0e
--- /dev/null
+++ b/src/main/java/ru/simsonic/rscPermissions/Importers/BaseImporter.java
@@ -0,0 +1,27 @@
+package ru.simsonic.rscPermissions.Importers;
+import java.util.ArrayList;
+import ru.simsonic.rscPermissions.DataTypes.*;
+
+public abstract class BaseImporter
+{
+ protected ArrayList imported_e = new ArrayList<>();
+ protected ArrayList imported_p = new ArrayList<>();
+ protected ArrayList imported_i = new ArrayList<>();
+ protected ArrayList imported_l = new ArrayList<>();
+ public RowEntity[] getEntities()
+ {
+ return imported_e.toArray(new RowEntity[imported_e.size()]);
+ }
+ public RowPermission[] getPermissions()
+ {
+ return imported_p.toArray(new RowPermission[imported_p.size()]);
+ }
+ public RowInheritance[] getInheritance()
+ {
+ return imported_i.toArray(new RowInheritance[imported_i.size()]);
+ }
+ public RowLadder[] getLadders()
+ {
+ return imported_l.toArray(new RowLadder[imported_l.size()]);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/simsonic/rscPermissions/Importers/PermissionsEx_YAML.java b/src/main/java/ru/simsonic/rscPermissions/Importers/PermissionsEx_YAML.java
new file mode 100644
index 0000000..a4940c5
--- /dev/null
+++ b/src/main/java/ru/simsonic/rscPermissions/Importers/PermissionsEx_YAML.java
@@ -0,0 +1,23 @@
+package ru.simsonic.rscPermissions.Importers;
+import java.io.File;
+import org.bukkit.configuration.ConfigurationSection;
+import org.bukkit.configuration.file.FileConfiguration;
+import org.bukkit.configuration.file.YamlConfiguration;
+import org.bukkit.plugin.Plugin;
+
+public final class PermissionsEx_YAML extends BaseImporter
+{
+ public PermissionsEx_YAML(Plugin plugin, String fileName)
+ {
+ if(fileName == null || "".equals(fileName))
+ fileName = "permissions.yml";
+ try
+ {
+ final FileConfiguration config = YamlConfiguration.loadConfiguration(new File(plugin.getDataFolder(), fileName));
+ final ConfigurationSection csGroups = config.getConfigurationSection("groups");
+ final ConfigurationSection csUsers = config.getConfigurationSection("users");
+ final ConfigurationSection csWorlds = config.getConfigurationSection("worlds");
+ } catch(NullPointerException ex) {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/simsonic/rscPermissions/Ladders.java b/src/main/java/ru/simsonic/rscPermissions/Ladders.java
new file mode 100644
index 0000000..9386580
--- /dev/null
+++ b/src/main/java/ru/simsonic/rscPermissions/Ladders.java
@@ -0,0 +1,120 @@
+package ru.simsonic.rscPermissions;
+import java.util.ArrayList;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+import ru.simsonic.rscPermissions.Backends.BackendMySQL;
+import ru.simsonic.rscPermissions.DataTypes.RowLadder;
+
+public class Ladders
+{
+ private final MainPluginClass plugin;
+ public Ladders(MainPluginClass rscp)
+ {
+ this.plugin = rscp;
+ }
+ public String[] executePromotion(CommandSender sender, String user, String ladder, boolean bPromote)
+ {
+ if(plugin.connectionList == null)
+ return null;
+ final BackendMySQL connection = plugin.connectionList.findConnectedNode();
+ if(connection == null || !connection.canWrite())
+ return null;
+ if("".equals(user) || (user == null))
+ return new String[] { (bPromote ? "/promote" : "/demote") + " " };
+ if("".equals(ladder) || (ladder == null))
+ return new String[] { "You should specify ladder to promote on." };
+ final Player player = plugin.getServer().getPlayerExact(user);
+ if(player == null)
+ return new String[] { "Player must be online." };
+ String template = ladder;
+ String instance = "";
+ String[] breaked = ladder.split(Settings.separatorRegExp);
+ if(breaked.length == 2)
+ {
+ template = breaked[0].toLowerCase();
+ instance = breaked[1];
+ }
+ final String perm_onself = bPromote ? "rscp.promote-self." : "rscp.demote-self.";
+ final String perm_sender = bPromote ? "rscp.promote." : "rscp.demote.";
+ final String perm_target = bPromote ? "rscp.promotable." : "rscp.demotable.";
+ boolean bOS = sender.hasPermission(perm_onself + "*");
+ boolean bSP = sender.hasPermission(perm_sender + "*");
+ boolean bUP = player.hasPermission(perm_target + "*");
+ if(!"".equals(instance))
+ {
+ bOS = bOS || sender.hasPermission(perm_onself + template + ".*");
+ bOS = bOS || sender.hasPermission(perm_onself + template + "." + instance.toLowerCase());
+ bSP = bSP || sender.hasPermission(perm_sender + template + ".*");
+ bSP = bSP || sender.hasPermission(perm_sender + template + "." + instance.toLowerCase());
+ bUP = bUP || player.hasPermission(perm_target + template + ".*");
+ bUP = bUP || player.hasPermission(perm_target + template + "." + instance.toLowerCase());
+ } else {
+ bOS = bOS || sender.hasPermission(perm_onself + template);
+ bSP = bSP || sender.hasPermission(perm_sender + template);
+ bUP = bUP || player.hasPermission(perm_target + template);
+ }
+ if(sender instanceof Player)
+ if(player != (Player)sender)
+ bOS = false;
+ boolean bPromotionAllowed = bOS || (bSP && bUP) || sender.hasPermission("rscp.admin.promote");
+ if(bPromotionAllowed == false)
+ {
+ if(bSP == false)
+ return new String[] { "You are not allowed to promote/demote on this ladder." };
+ if(bUP == false)
+ return new String[] { "Player is not promotable on this ladder." };
+ }
+ int rank = plugin.cache.getUserRank(user, template, instance);
+ final ArrayList ladderArray = plugin.cache.buildLadderTemplate(template);
+ if(ladderArray.isEmpty())
+ return new String[] { "There is no such ladder." };
+ RowLadder position = ladderArray.get(0).getActualNode(rank);
+ if(bPromote)
+ {
+ if(position.nextNode != null)
+ position = position.nextNode;
+ } else
+ if(position.prevNode != null)
+ position = position.prevNode;
+ if(position.instance != null)
+ if(Settings.instantiator.equals(position.instance))
+ {
+ if("".equals(instance))
+ return new String[] { "Operation requires ladder instance (.)." };
+ } else
+ instance = ("".equals(position.instance) ? null : position.instance);
+ else
+ instance = null;
+ ladder = position.ladder + ((instance != null) ? "." + instance : "");
+ connection.dropUserFromLadder(user, ladder);
+ connection.setUserRank(user, ladder, position.rank);
+ final String parent = position.climber + ((instance != null) ? "." + instance : "");
+ if(position.climber != null)
+ connection.addUserParentGroup(user, parent);
+ connection.fetchIntoCache(plugin.cache);
+ plugin.cache.calculatePlayerPermissions(player);
+ final String resultP = "{GOLD}You have been " +
+ ((position.climber != null) ?
+ (
+ (bPromote ? "promoted" : "demoted") + " to the group {_LS}" +
+ position.climber + ((instance != null) ? " {_DS}(" + instance + ") " : "") +
+ "{GOLD}on"
+ ) : "removed from"
+ ) + " the ladder {_LG}" + position.ladder + ((instance != null) ? "{_DS}(" + instance + ")" : "") + "{GOLD} by " +
+ (
+ (sender instanceof Player) ?
+ ((player == (Player)sender) ? "yourself" : "{_YL}" + sender.getName())
+ : "server"
+ ) + "{GOLD}.";
+ final String resultS = "{GOLD}User {_YL}" + player.getName() + " {GOLD}has been " +
+ ((position.climber != null) ?
+ (
+ (bPromote ? "promoted" : "demoted") + " to the group {_LS}" +
+ position.climber + ((instance != null) ? "{_DS}(" + instance + ") " : "") +
+ "{GOLD}on"
+ ) : "removed from"
+ ) + " the ladder {_LG}" + position.ladder + ((instance != null) ? "{_DS}(" + instance + ")" : "") + "{GOLD}.";
+ plugin.Message(player, resultP);
+ return new String [] { resultS };
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/simsonic/rscPermissions/LocalCacheData.java b/src/main/java/ru/simsonic/rscPermissions/LocalCacheData.java
new file mode 100644
index 0000000..9f6a849
--- /dev/null
+++ b/src/main/java/ru/simsonic/rscPermissions/LocalCacheData.java
@@ -0,0 +1,95 @@
+package ru.simsonic.rscPermissions;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.concurrent.ConcurrentHashMap;
+import ru.simsonic.rscPermissions.DataTypes.*;
+import ru.simsonic.rscPermissions.DataTypes.RowEntity.EntityType;
+
+public class LocalCacheData
+{
+ protected final MainPluginClass plugin;
+ protected final HashMap entities_g = new HashMap<>();
+ protected final HashMap entities_u = new HashMap<>();
+ protected final ConcurrentHashMap prefixes_u = new ConcurrentHashMap<>();
+ protected final ConcurrentHashMap suffixes_u = new ConcurrentHashMap<>();
+ protected final ArrayList permissions_p2g = new ArrayList<>();
+ protected final ArrayList permissions_p2u = new ArrayList<>();
+ protected final ArrayList inheritance_g2g = new ArrayList<>();
+ protected final ArrayList inheritance_g2u = new ArrayList<>();
+ protected final ArrayList ladders_g = new ArrayList<>();
+ protected final ArrayList ladders_u = new ArrayList<>();
+ protected final ArrayList servers = new ArrayList<>();
+ protected LocalCacheData(MainPluginClass rscp)
+ {
+ this.plugin = rscp;
+ }
+ public synchronized int ImportEntities(RowEntity[] rows)
+ {
+ entities_g.clear();
+ entities_u.clear();
+ if(rows == null)
+ return 0;
+ for(RowEntity row : rows)
+ {
+ if(row.entity_type == EntityType.groupName)
+ entities_g.put(row.entity.toLowerCase(), row);
+ else
+ entities_u.put(row.entity.toLowerCase(), row);
+ }
+ return entities_g.size() + entities_u.size();
+ }
+ public synchronized int ImportPermissions(RowPermission[] rows)
+ {
+ permissions_p2g.clear();
+ permissions_p2u.clear();
+ if(rows == null)
+ return 0;
+ for(RowPermission row : rows)
+ {
+ if(row.entity_type == EntityType.groupName)
+ permissions_p2g.add(row);
+ else
+ permissions_p2u.add(row);
+ }
+ return permissions_p2g.size() + permissions_p2u.size();
+ }
+ public synchronized int ImportInheritance(RowInheritance[] rows)
+ {
+ inheritance_g2g.clear();
+ inheritance_g2u.clear();
+ if(rows == null)
+ return 0;
+ for(RowInheritance row : rows)
+ {
+ if(row.child_type == EntityType.groupName)
+ inheritance_g2g.add(row);
+ else
+ inheritance_g2u.add(row);
+ }
+ return inheritance_g2g.size() + inheritance_g2u.size();
+ }
+ public synchronized int ImportLadders(RowLadder[] rows)
+ {
+ ladders_g.clear();
+ ladders_u.clear();
+ if(rows == null)
+ return 0;
+ for(RowLadder row : rows)
+ {
+ if(row.climber_type == EntityType.groupName)
+ ladders_g.add(row);
+ else
+ ladders_u.add(row);
+ }
+ return ladders_g.size() + ladders_u.size();
+ }
+ public synchronized int ImportServers(RowServer[] rows)
+ {
+ servers.clear();
+ if(rows == null)
+ return 0;
+ servers.addAll(Arrays.asList(rows));
+ return servers.size();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/simsonic/rscPermissions/LocalCacheFunctions.java b/src/main/java/ru/simsonic/rscPermissions/LocalCacheFunctions.java
new file mode 100644
index 0000000..3bc4461
--- /dev/null
+++ b/src/main/java/ru/simsonic/rscPermissions/LocalCacheFunctions.java
@@ -0,0 +1,99 @@
+package ru.simsonic.rscPermissions;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import ru.simsonic.rscPermissions.DataTypes.RowEntity;
+import ru.simsonic.rscPermissions.DataTypes.RowInheritance;
+import ru.simsonic.rscPermissions.DataTypes.RowLadder;
+import ru.simsonic.rscPermissions.DataTypes.RowPermission;
+
+public class LocalCacheFunctions extends LocalCacheTree
+{
+ public LocalCacheFunctions(MainPluginClass rscp)
+ {
+ super(rscp);
+ }
+ public String userGetPrefix(String user)
+ {
+ return prefixes_u.get(user);
+ }
+ public String userGetSuffix(String user)
+ {
+ return suffixes_u.get(user);
+ }
+ public synchronized String groupGetPrefix(String group)
+ {
+ if(group == null || "".equals(group))
+ return null;
+ RowEntity entity = entities_g.get(group.toLowerCase());
+ return (entity != null) ? entity.prefix : null;
+ }
+ public synchronized String groupGetSuffix(String group)
+ {
+ if(group == null || "".equals(group))
+ return null;
+ RowEntity entity = entities_g.get(group.toLowerCase());
+ return (entity != null) ? entity.suffix : null;
+ }
+ public synchronized ArrayList buildLadderTemplate(String ladder)
+ {
+ final ArrayList result = new ArrayList<>();
+ RowLadder prev = null;
+ for(RowLadder row : ladders_g)
+ if(row.ladder.equalsIgnoreCase(ladder))
+ {
+ if(prev != null)
+ {
+ prev.nextNode = row;
+ row.prevNode = prev;
+ }
+ result.add(row);
+ prev = row;
+ }
+ Collections.sort(result);
+ return result;
+ }
+ public synchronized int getUserRank(String user, String ladder, String instance)
+ {
+ for(RowLadder row : ladders_u)
+ if(row.climber.equalsIgnoreCase(user) && row.ladder.equalsIgnoreCase(ladder))
+ if(instance == null || "".equals(instance))
+ {
+ if(row.instance == null || "".equals(row.instance))
+ return row.rank;
+ } else
+ if(instance.equalsIgnoreCase(row.instance))
+ return row.rank;
+ return 0;
+ }
+ public synchronized ArrayList getUserGroups(String player)
+ {
+ final ArrayList tree = mapTrees.get(player.toLowerCase());
+ if(tree == null)
+ return null;
+ final ArrayList result = new ArrayList<>();
+ for(ResolutionLeaf leaf : tree)
+ result.add(leaf.instance != null ? leaf.group + Settings.separator + leaf.instance : leaf.group);
+ return result;
+ }
+ public synchronized Set getAllPossibleGroups()
+ {
+ Set result = new HashSet<>();
+ for(RowEntity row : entities_g.values())
+ result.add(row.entity.toLowerCase());
+ for(RowPermission row : permissions_p2g)
+ result.add(row.entity.toLowerCase());
+ for(RowInheritance row : inheritance_g2g)
+ {
+ result.add(row.entity.toLowerCase());
+ result.add(row.parent.toLowerCase());
+ }
+ for(RowInheritance row : inheritance_g2u)
+ result.add(row.parent.toLowerCase());
+ for(RowLadder row : ladders_g)
+ if(row.climber != null)
+ result.add(row.climber.toLowerCase());
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/simsonic/rscPermissions/LocalCacheTree.java b/src/main/java/ru/simsonic/rscPermissions/LocalCacheTree.java
new file mode 100644
index 0000000..829647b
--- /dev/null
+++ b/src/main/java/ru/simsonic/rscPermissions/LocalCacheTree.java
@@ -0,0 +1,180 @@
+package ru.simsonic.rscPermissions;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import org.bukkit.entity.Player;
+import ru.simsonic.rscPermissions.DataTypes.RowEntity;
+import ru.simsonic.rscPermissions.DataTypes.RowInheritance;
+import ru.simsonic.rscPermissions.DataTypes.RowLadder;
+import ru.simsonic.rscPermissions.DataTypes.RowPermission;
+import ru.simsonic.utilities.LanguageUtility;
+
+public class LocalCacheTree extends LocalCacheData
+{
+ protected static class ResolutionLeaf
+ {
+ public String group;
+ public String instance;
+ public RowInheritance row;
+ }
+ protected LocalCacheTree(MainPluginClass rscp)
+ {
+ super(rscp);
+ }
+ protected final ConcurrentHashMap> mapTrees = new ConcurrentHashMap<>();
+ protected final ConcurrentHashMap> mapPermissions = new ConcurrentHashMap<>();
+ protected final RowInheritance defaultInheritance = new RowInheritance();
+ public void updateDefaultInheritance()
+ {
+ defaultInheritance.parent = plugin.settings.getDefaultGroup();
+ String[] breaked = defaultInheritance.parent.split(Settings.separatorRegExp);
+ if(breaked.length == 2)
+ {
+ defaultInheritance.parent = breaked[0];
+ defaultInheritance.instance = breaked[1];
+ } else
+ defaultInheritance.instance = null;
+ }
+ public synchronized void clear()
+ {
+ mapTrees.clear();
+ mapPermissions.clear();
+ prefixes_u.clear();
+ suffixes_u.clear();
+ entities_g.clear();
+ entities_u.clear();
+ permissions_p2g.clear();
+ permissions_p2u.clear();
+ inheritance_g2g.clear();
+ inheritance_g2u.clear();
+ ladders_g.clear();
+ ladders_u.clear();
+ }
+ public synchronized void calculateStartupPermissions()
+ {
+ final HashSet playerEntities = new HashSet<>();
+ // Undefined player
+ playerEntities.add(""); // Зачем я его тут добавил?!?
+ // Defined players (in any table)
+ playerEntities.addAll(entities_u.keySet());
+ for(RowPermission row : permissions_p2u)
+ playerEntities.add(row.entity);
+ for(RowInheritance row : inheritance_g2u)
+ playerEntities.add(row.entity);
+ for(RowLadder row : ladders_u)
+ playerEntities.add(row.climber);
+ // Recalculate
+ for(String entityNameOrUUID : playerEntities)
+ calculateBasePermissions(entityNameOrUUID);
+ }
+ public synchronized void calculateBasePermissions(String playerName)
+ {
+ AsyncPlayerInfo p2rc = new AsyncPlayerInfo(playerName);
+ HashMap list = treeToPermissions(p2rc);
+ mapPermissions.put(playerName, list);
+ }
+ public synchronized void calculateBasePermissions(UUID playerUniqueId)
+ {
+ AsyncPlayerInfo p2rc = new AsyncPlayerInfo(playerUniqueId);
+ HashMap list = treeToPermissions(p2rc);
+ mapPermissions.put(playerUniqueId.toString().replace("-", "").toLowerCase(), list);
+ }
+ public void calculatePlayerPermissions(Player player)
+ {
+ final AsyncPlayerInfo api = new AsyncPlayerInfo(player, plugin.regionListProvider.GetRegionList(player));
+ plugin.recalculatingPlayers.offer(api);
+ }
+ protected synchronized HashMap treeToPermissions(AsyncPlayerInfo p2rc)
+ {
+ final HashMap permissions = new HashMap<>();
+ String prefix = "";
+ String suffix = "";
+ ArrayList tree = buildUserTree(p2rc);
+ if(p2rc.name != null)
+ mapTrees.put(p2rc.name.toLowerCase(), tree);
+ // Group permissions
+ for(ResolutionLeaf leaf : tree)
+ {
+ for(RowPermission row : permissions_p2g)
+ if(p2rc.isGroupPermissionApplicable(row, leaf))
+ {
+ String permission = row.permission;
+ // Additional processing
+ if(permission.contains(Settings.instantiator) && (leaf.instance != null))
+ permission = permission.replaceAll(Settings.instantiatorRegExp, leaf.instance);
+ permissions.put(permission, row.value);
+ }
+ RowEntity entity = entities_g.get(leaf.group.toLowerCase());
+ if(entity != null)
+ {
+ if(entity.prefix != null && !"".equals(entity.prefix))
+ prefix = entity.prefix.replace("%", prefix);
+ if(entity.suffix != null && !"".equals(entity.suffix))
+ suffix = entity.suffix.replace("%", suffix);
+ prefix = prefix.replaceAll(Settings.instantiatorRegExp, leaf.instance);
+ suffix = suffix.replaceAll(Settings.instantiatorRegExp, leaf.instance);
+ }
+ }
+ // User permissions
+ for(RowPermission row : permissions_p2u)
+ if(p2rc.isPlayerPermissionApplicable(row))
+ permissions.put(row.permission, row.value);
+ if(p2rc.name != null)
+ {
+ RowEntity entity = entities_u.get(p2rc.name.toLowerCase());
+ if(entity != null)
+ {
+ if(entity.prefix != null && !"".equals(entity.prefix))
+ prefix = entity.prefix.replace("%", prefix);
+ if(entity.suffix != null && !"".equals(entity.suffix))
+ suffix = entity.suffix.replace("%", suffix);
+ }
+ prefixes_u.put(p2rc.name, LanguageUtility.processStringStatic(prefix));
+ suffixes_u.put(p2rc.name, LanguageUtility.processStringStatic(suffix));
+ }
+ return permissions;
+ }
+ private ArrayList buildUserTree(AsyncPlayerInfo p2rc)
+ {
+ // User's direct inheritance
+ ArrayList parentRows = new ArrayList<>();
+ for(RowInheritance row : inheritance_g2u)
+ if(p2rc.isPlayerInheritanceApplicable(row))
+ parentRows.add(row);
+ Collections.sort(parentRows);
+ // Indirect default group
+ if(parentRows.isEmpty() || plugin.settings.isDefaultForever())
+ parentRows.add(0, defaultInheritance);
+ ArrayList resultTree = new ArrayList<>();
+ // Parent deep inheritances
+ for(RowInheritance row : parentRows)
+ {
+ ResolutionLeaf newleaf = new ResolutionLeaf();
+ newleaf.group = row.parent;
+ newleaf.instance = row.instance;
+ newleaf.row = row;
+ buildGroupTree(p2rc, newleaf, resultTree);
+ }
+ return resultTree;
+ }
+ private void buildGroupTree(AsyncPlayerInfo p2rc, ResolutionLeaf findAndOpen, ArrayList result)
+ {
+ ArrayList parentRows = new ArrayList<>(inheritance_g2g.size() >> 2);
+ for(RowInheritance row : inheritance_g2g)
+ if(p2rc.isGroupInheritanceApplicable(row, findAndOpen))
+ parentRows.add(row);
+ Collections.sort(parentRows);
+ for(RowInheritance row : parentRows)
+ {
+ ResolutionLeaf newleaf = new ResolutionLeaf();
+ newleaf.group = row.parent;
+ newleaf.instance = (row.instance != null) ? row.instance : findAndOpen.instance;
+ newleaf.row = row;
+ buildGroupTree(p2rc, newleaf, result);
+ }
+ result.add(findAndOpen);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/simsonic/rscPermissions/MainPluginClass.java b/src/main/java/ru/simsonic/rscPermissions/MainPluginClass.java
new file mode 100644
index 0000000..f107ede
--- /dev/null
+++ b/src/main/java/ru/simsonic/rscPermissions/MainPluginClass.java
@@ -0,0 +1,332 @@
+package ru.simsonic.rscPermissions;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import net.gravitydevelopment.updater.Updater;
+import org.bukkit.Bukkit;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+import org.bukkit.command.ConsoleCommandSender;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerExpChangeEvent;
+import org.bukkit.event.player.PlayerJoinEvent;
+import org.bukkit.event.player.PlayerLevelChangeEvent;
+import org.bukkit.event.player.PlayerLoginEvent;
+import org.bukkit.event.player.PlayerQuitEvent;
+import org.bukkit.permissions.PermissionAttachment;
+import org.bukkit.plugin.java.JavaPlugin;
+import org.bukkit.scheduler.BukkitScheduler;
+import org.mcstats.MetricsLite;
+import ru.simsonic.utilities.LanguageUtility;
+import ru.simsonic.utilities.MovingPlayersCatcher;
+
+public final class MainPluginClass extends JavaPlugin implements Listener
+{
+ private static final int projectNumberInDBO = 55450;
+ private static final String chatPrefix = "{_YL}[rscp] {GOLD}";
+ public static final Logger consoleLog = Logger.getLogger("Minecraft");
+ public final rscpAPI API = new rscpAPI(this);
+ public final Settings settings = new Settings(this);
+ public final LocalCacheFunctions cache = new LocalCacheFunctions(this);
+ public final CommandHelper commandExecutor = new CommandHelper(this);
+ public final MaintenanceMode maintenance = new MaintenanceMode(this);
+ public final RegionListProviders regionListProvider = new RegionListProviders(this);
+ private final MovingPlayersCatcher movedPlayers = new MovingPlayersCatcher();
+ public ConnectionHelper connectionList;
+ public Thread threadPermissions;
+ private Updater updater;
+ private MetricsLite metrics;
+ public final HashMap attachments = new HashMap<>();
+ public final LinkedBlockingQueue recalculatingPlayers = new LinkedBlockingQueue<>();
+ // private final HashSet verbosePlayers = new HashSet<>();
+ @Override
+ public void onLoad()
+ {
+ settings.onLoad();
+ consoleLog.log(Level.INFO, "[rscp] This server\'s ID is \'{0}\'. You can change it in server.properties.", getServer().getServerId());
+ consoleLog.log(Level.INFO, "[rscp] rscPermissions has been loaded.");
+ }
+ @Override
+ public void onEnable()
+ {
+ settings.readSettings();
+ connectionList = settings.getConnectionChain();
+ if(connectionList == null)
+ {
+ consoleLog.log(Level.WARNING, "[rscp] No MySQL servers were specified in config.yml, disabling...");
+ getServer().getPluginManager().disablePlugin(this);
+ return;
+ }
+ // Register event's dispatcher
+ getServer().getPluginManager().registerEvents(this, this);
+ getServer().getPluginManager().registerEvents(maintenance, this);
+ getServer().getPluginManager().registerEvents(movedPlayers, this);
+ // WorldGuard, Residence and other possible region list providers
+ regionListProvider.integrate();
+ // Start all needed threads
+ cache.updateDefaultInheritance();
+ StartRecalcThread();
+ RegionFinderThreadStart();
+ connectionList.threadFetchTablesData();
+ // Automatic updater
+ if(settings.isUpdatable())
+ {
+ updater = new Updater(this, projectNumberInDBO, this.getFile(), Updater.UpdateType.NO_DOWNLOAD, false);
+ if(updater.getResult() == Updater.UpdateResult.UPDATE_AVAILABLE)
+ infoAboutUpdate(getServer().getConsoleSender());
+ else
+ updater = null;
+ }
+ // Metrics
+ if(settings.isUseMetrics())
+ {
+ try
+ {
+ metrics = new MetricsLite(this);
+ metrics.start();
+ consoleLog.info("[rscp] Metrics enabled.");
+ } catch(IOException ex) {
+ consoleLog.log(Level.INFO, "[rscp][Metrics] Exception: {0}", ex.getLocalizedMessage());
+ }
+ }
+ consoleLog.info("[rscp] rscPermissions has been successfully enabled.");
+ }
+ private void infoAboutUpdate(CommandSender sender)
+ {
+ if(updater != null)
+ {
+ if(sender instanceof ConsoleCommandSender)
+ {
+ consoleLog.info("[rscp] Update is available! Enter /rscp update to update plugin now.");
+ consoleLog.info("[rscp] Please be noted that after restart updated file will have name including old version.");
+ } else if(sender.hasPermission("rscp.admin")) {
+ Message(sender, "Update is available: {_LB}" + updater.getLatestName() + "{GOLD}!");
+ Message(sender, "Enter {_LG}/rscp update{GOLD} to update plugin now.");
+ Message(sender, "Please be noted that after restart updated file will have name including old version.");
+ }
+ }
+ }
+ @Override
+ public void onDisable()
+ {
+ getServer().getServicesManager().unregisterAll(this);
+ RegionFinderThreadStop();
+ StopRecalcThread();
+ cache.clear();
+ connectionList.Disconnect();
+ connectionList = null;
+ regionListProvider.deintegrate();
+ metrics = null;
+ consoleLog.info("[rscp] rscPermissions has been disabled.");
+ }
+ public String doUpdate(CommandSender sender)
+ {
+ if(updater != null)
+ {
+ updater = new Updater(this, projectNumberInDBO, this.getFile(), Updater.UpdateType.DEFAULT, true);
+ updater = null;
+ return "Plugin will be updated automatically after restart.";
+ }
+ return "No updates available / waiting for server restart.";
+ }
+ private Thread hThreadRegionFinder = null;
+ private void RegionFinderThreadStart()
+ {
+ RegionFinderThreadStop();
+ hThreadRegionFinder = new Thread()
+ {
+ @Override
+ public void run()
+ {
+ this.setName("rscp:RegionFinder");
+ this.setPriority(MIN_PRIORITY);
+ long granularity = settings.getRegionFinderGranularity();
+ if(granularity < 20)
+ granularity = 20;
+ if(granularity > 10000)
+ granularity = 10000;
+ try
+ {
+ for(; !Thread.interrupted(); Thread.sleep(granularity))
+ for(Player player : movedPlayers.getMovedPlayersAsync())
+ if(regionListProvider.IsRegionListChanged(player))
+ cache.calculatePlayerPermissions(player);
+ } catch(InterruptedException ex) {
+ }
+ }
+ };
+ hThreadRegionFinder.start();
+ }
+ public void RegionFinderThreadStop()
+ {
+ if(hThreadRegionFinder == null)
+ return;
+ try
+ {
+ hThreadRegionFinder.interrupt();
+ hThreadRegionFinder.join();
+ hThreadRegionFinder = null;
+ } catch(InterruptedException ex) {
+ consoleLog.log(Level.SEVERE, "[rscp] Exception in RegionFinderThread(): {0}", ex.getLocalizedMessage());
+ }
+ }
+ public void StopRecalcThread()
+ {
+ if(threadPermissions != null)
+ try
+ {
+ threadPermissions.interrupt();
+ threadPermissions.join();
+ threadPermissions = null;
+ } catch(InterruptedException ex) {
+ consoleLog.log(Level.WARNING, "[rscp] Exception in StopRecalcThread: {0}", ex.getLocalizedMessage());
+ }
+ }
+ public void StartRecalcThread()
+ {
+ StopRecalcThread();
+ final MainPluginClass plugin = this;
+ threadPermissions = new Thread()
+ {
+ @Override
+ public void run()
+ {
+ setName("rscp:PermCalculator");
+ setPriority(Thread.MIN_PRIORITY);
+ try
+ {
+ AsyncPlayerInfo p2rc;
+ while((p2rc = recalculatingPlayers.take()) != null)
+ {
+ // Build inheritance tree and calculate permissions
+ final HashMap permissions = cache.treeToPermissions(p2rc);
+ // Schedule attachment update
+ final Player player = p2rc.player;
+ getServer().getScheduler().runTask(plugin, new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ PermissionAttachment attachment = attachments.get(player);
+ if(attachment != null)
+ attachment.remove();
+ attachment = player.addAttachment(plugin);
+ attachments.put(player, attachment);
+ for(String permission : permissions.keySet())
+ attachment.setPermission(permission, permissions.get(permission));
+ if(settings.isAsteriskOP())
+ {
+ final Boolean asteriskValue = permissions.get("*");
+ player.setOp(asteriskValue != null ? asteriskValue : false);
+ }
+ }
+ });
+ }
+ } catch(InterruptedException ex) {
+ }
+ recalculatingPlayers.clear();
+ }
+ };
+ threadPermissions.start();
+ }
+ public void recalculateOnlinePlayers()
+ {
+ for(Player player : Bukkit.getServer().getOnlinePlayers())
+ if(player != null)
+ cache.calculatePlayerPermissions(player);
+ rescheduleAutoUpdate();
+ }
+ private int nAutoUpdaterTaskId = -1;
+ private void rescheduleAutoUpdate()
+ {
+ final BukkitScheduler scheduler = getServer().getScheduler();
+ if(nAutoUpdaterTaskId != -1)
+ scheduler.cancelTask(nAutoUpdaterTaskId);
+ final int delay = settings.getAutoReloadDelayTicks();
+ nAutoUpdaterTaskId = scheduler.scheduleSyncDelayedTask(this, new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ connectionList.threadFetchTablesData();
+ }
+ }, delay);
+ }
+ @Override
+ public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args)
+ {
+ try
+ {
+ commandExecutor.onCommand(sender, cmd, label, args);
+ } catch(CommandHelperAnswerException ex) {
+ for(String answer : ex.getMessageArray())
+ sender.sendMessage(LanguageUtility.processStringStatic(chatPrefix + answer));
+ } catch(NullPointerException ex) {
+ // These will never occur! I hope...
+ }
+ return true;
+ }
+ @org.bukkit.event.EventHandler(priority = EventPriority.LOWEST)
+ public void onPlayerLogin(PlayerLoginEvent event)
+ {
+ final Player player = event.getPlayer();
+ String name = event.getPlayer().getName();
+ final HashMap pending = cache.mapPermissions.get(name);
+ if(pending != null)
+ {
+ final PermissionAttachment attachment = player.addAttachment(this);
+ for(String permission : pending.keySet())
+ attachment.setPermission(permission, pending.get(permission));
+ attachments.put(player, attachment);
+ }
+ cache.calculatePlayerPermissions(player);
+ }
+ @org.bukkit.event.EventHandler
+ public void onPlayerJoin(PlayerJoinEvent event)
+ {
+ infoAboutUpdate(event.getPlayer());
+ // Show list of player's rewards
+ if(settings.isRewardsEnabled())
+ commandExecutor.rewardHelper.executeReward(event.getPlayer(), null);
+ }
+ @org.bukkit.event.EventHandler
+ public void onPlayerExp(PlayerLevelChangeEvent event)
+ {
+ cache.calculatePlayerPermissions(event.getPlayer());
+ }
+ @org.bukkit.event.EventHandler
+ public void onPlayerLevel(PlayerExpChangeEvent event)
+ {
+ cache.calculatePlayerPermissions(event.getPlayer());
+ }
+ @org.bukkit.event.EventHandler
+ public void onPlayerKick(PlayerQuitEvent event)
+ {
+ attachments.remove(event.getPlayer());
+ regionListProvider.removePlayer(event.getPlayer());
+ }
+ @org.bukkit.event.EventHandler
+ public void onPlayerQuit(PlayerQuitEvent event)
+ {
+ attachments.remove(event.getPlayer());
+ regionListProvider.removePlayer(event.getPlayer());
+ }
+ public void Message(CommandSender sender, String message)
+ {
+ if(message == null || "".equals(message))
+ return;
+ message = LanguageUtility.processStringStatic(chatPrefix + message);
+ sender.sendMessage(message);
+ }
+ @SuppressWarnings({"DeadBranch", "UnusedAssignment"})
+ public static void main(String args[])
+ {
+ System.out.println("rscPermissions - Bukkit superperms plugin © SimSonic");
+ System.out.println("http://dev.bukkit.org/bukkit-plugins/rscpermissions/");
+ // TEST SECTION STARTS BELOW
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/simsonic/rscPermissions/MaintenanceMode.java b/src/main/java/ru/simsonic/rscPermissions/MaintenanceMode.java
new file mode 100644
index 0000000..ef4edfa
--- /dev/null
+++ b/src/main/java/ru/simsonic/rscPermissions/MaintenanceMode.java
@@ -0,0 +1,95 @@
+package ru.simsonic.rscPermissions;
+import java.util.HashMap;
+import java.util.UUID;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
+import org.bukkit.event.server.ServerListPingEvent;
+import ru.simsonic.utilities.LanguageUtility;
+
+public class MaintenanceMode implements Listener
+{
+ private final MainPluginClass plugin;
+ public MaintenanceMode(MainPluginClass rscp)
+ {
+ this.plugin = rscp;
+ }
+ @org.bukkit.event.EventHandler
+ public void onServerPing(ServerListPingEvent event)
+ {
+ if(plugin.settings.isInMaintenance())
+ {
+ String motd = "Server is under maintenance";
+ motd = plugin.getConfig().getString("language.mModes.locked.default.motd", motd);
+ motd = plugin.getConfig().getString("language.mModes.locked." + plugin.settings.getMaintenanceMode() + ".motd", motd);
+ motd = LanguageUtility.processStringStatic(motd);
+ if(!"".equals(motd))
+ event.setMotd(motd);
+ }
+ }
+ @org.bukkit.event.EventHandler
+ public void onAsyncPlayerPreLogin(AsyncPlayerPreLoginEvent event)
+ {
+ if("".equals(plugin.settings.getMaintenanceMode()))
+ {
+ event.allow();
+ return;
+ }
+ final String permissionAll = "rscp.maintenance.*";
+ final String permission_mm = "rscp.maintenance." + (plugin.settings.getMaintenanceMode());
+ final HashMap permissions = new HashMap<>();
+ try
+ {
+ final String name = event.getName();
+ plugin.cache.calculateBasePermissions(name);
+ permissions.putAll(plugin.cache.mapPermissions.get(name));
+ } catch(RuntimeException ex) {
+ }
+ try
+ {
+ final UUID uuid = event.getUniqueId();
+ final String userFriendlyUniqueId = uuid.toString().replace("-", "").toLowerCase();
+ plugin.cache.calculateBasePermissions(uuid);
+ permissions.putAll(plugin.cache.mapPermissions.get(userFriendlyUniqueId));
+ } catch(RuntimeException | NoSuchMethodError ex) {
+ }
+ for(String permission : permissions.keySet())
+ if(permission.equalsIgnoreCase(permission_mm) || permission.equalsIgnoreCase(permissionAll))
+ {
+ event.allow();
+ return;
+ }
+ String kickMsg = "{_YL}Server is in maintenance mode\nPlease try to connect later...";
+ kickMsg = plugin.getConfig().getString("language.mmodes.locked.default.motd", kickMsg);
+ kickMsg = plugin.getConfig().getString("language.mmodes.locked." + plugin.settings.getMaintenanceMode() + ".motd", kickMsg);
+ kickMsg = LanguageUtility.processStringStatic(kickMsg);
+ event.disallow(AsyncPlayerPreLoginEvent.Result.KICK_WHITELIST, kickMsg);
+ }
+ public void setMaintenanceMode(String mMode)
+ {
+ plugin.settings.setMaintenanceMode(mMode);
+ if(!plugin.settings.isInMaintenance())
+ return;
+ plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ for(Player player : Bukkit.getServer().getOnlinePlayers())
+ if(player != null)
+ {
+ if(player.hasPermission("rscp.maintenance.*"))
+ continue;
+ if(player.hasPermission("rscp.maintenance." + plugin.settings.getMaintenanceMode()))
+ continue;
+ String kick = "{_YL}Server is going into maintenance mode";
+ kick = plugin.getConfig().getString("language.mModes.locked.default.kick", kick);
+ kick = plugin.getConfig().getString("language.mModes.locked." + plugin.settings.getMaintenanceMode() + ".kick", kick);
+ kick = LanguageUtility.processStringStatic(kick);
+ player.kickPlayer(kick);
+ }
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/simsonic/rscPermissions/RegionListProviders.java b/src/main/java/ru/simsonic/rscPermissions/RegionListProviders.java
new file mode 100644
index 0000000..06873bb
--- /dev/null
+++ b/src/main/java/ru/simsonic/rscPermissions/RegionListProviders.java
@@ -0,0 +1,124 @@
+package ru.simsonic.rscPermissions;
+import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
+import com.sk89q.worldguard.protection.ApplicableRegionSet;
+import com.sk89q.worldguard.protection.managers.RegionManager;
+import com.sk89q.worldguard.protection.regions.ProtectedRegion;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import net.t00thpick1.residence.Residence;
+import net.t00thpick1.residence.api.ResidenceAPI;
+import net.t00thpick1.residence.api.ResidenceManager;
+import net.t00thpick1.residence.api.areas.ResidenceArea;
+import org.bukkit.Location;
+import org.bukkit.World;
+import org.bukkit.entity.Player;
+import org.bukkit.plugin.Plugin;
+
+public final class RegionListProviders
+{
+ private final MainPluginClass plugin;
+ private Plugin worldguard;
+ private Plugin residence;
+ private final Map> regionsByPlayer = new HashMap<>();
+ private final Map playerRegionHashes = new HashMap<>();
+ private final Map playerLastWorld = new HashMap<>();
+ public RegionListProviders(MainPluginClass rscp)
+ {
+ this.plugin = rscp;
+ }
+ public synchronized void integrate()
+ {
+ // WorldGuard
+ if(plugin.settings.isUseWorldGuard())
+ {
+ final Plugin pluginWG = plugin.getServer().getPluginManager().getPlugin("WorldGuard");
+ if((pluginWG != null) && (pluginWG instanceof WorldGuardPlugin))
+ {
+ this.worldguard = pluginWG;
+ MainPluginClass.consoleLog.info("[rscp] WorldGuard was found and integrated.");
+ } else {
+ this.worldguard = null;
+ MainPluginClass.consoleLog.info("[rscp] WorldGuard was not found.");
+ }
+ } else
+ this.worldguard = null;
+ // Residence
+ if(plugin.settings.isUseResidence())
+ {
+ final Plugin pluginR = plugin.getServer().getPluginManager().getPlugin("Residence");
+ if((pluginR != null) && (pluginR instanceof Residence))
+ {
+ this.residence = pluginR;
+ MainPluginClass.consoleLog.info("[rscp] Residence was found and integrated.");
+ } else {
+ this.residence = null;
+ MainPluginClass.consoleLog.info("[rscp] Residence was not found.");
+ }
+ } else
+ this.residence = null;
+ }
+ public synchronized void deintegrate()
+ {
+ this.worldguard = null;
+ this.residence = null;
+ }
+ public synchronized boolean IsRegionListChanged(Player player)
+ {
+ final Location location = player.getLocation();
+ final World world = location.getWorld();
+ final Set playerRegions = new HashSet<>();
+ // WorldGuard
+ if(worldguard != null && worldguard.isEnabled())
+ try
+ {
+ final WorldGuardPlugin pluginWG = (WorldGuardPlugin)worldguard;
+ final RegionManager rman = pluginWG.getRegionManager(world);
+ if(rman == null)
+ return false;
+ // Get list
+ final ApplicableRegionSet appregs = rman.getApplicableRegions(location);
+ for(ProtectedRegion region : appregs)
+ playerRegions.add(region.getId());
+ } catch(RuntimeException ex) {
+ }
+ // Residence
+ if(residence != null && residence.isEnabled())
+ try
+ {
+ // Get list
+ final ResidenceManager residenceManager = ResidenceAPI.getResidenceManager();
+ if(residenceManager != null)
+ {
+ final ResidenceArea residenceArea = residenceManager.getByLocation(location);
+ if(residenceArea != null)
+ playerRegions.add(residenceArea.getFullName());
+ }
+ } catch(RuntimeException ex) {
+ }
+ // Is it changed?
+ int hashcode = playerRegions.hashCode();
+ if(playerLastWorld.containsKey(player))
+ if(playerLastWorld.get(player).equals(world))
+ if(hashcode == playerRegionHashes.get(player))
+ return false;
+ // Update
+ playerRegionHashes.put(player, hashcode);
+ regionsByPlayer.put(player, playerRegions);
+ playerLastWorld.put(player, world);
+ return true;
+ }
+ public synchronized Set GetRegionList(Player player)
+ {
+ Set result = regionsByPlayer.get(player);
+ return (result != null) ? result : Collections.emptySet();
+ }
+ public synchronized void removePlayer(Player player)
+ {
+ playerRegionHashes.remove(player);
+ regionsByPlayer.remove(player);
+ playerLastWorld.remove(player);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/simsonic/rscPermissions/Rewards.java b/src/main/java/ru/simsonic/rscPermissions/Rewards.java
new file mode 100644
index 0000000..af83421
--- /dev/null
+++ b/src/main/java/ru/simsonic/rscPermissions/Rewards.java
@@ -0,0 +1,180 @@
+package ru.simsonic.rscPermissions;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.logging.Level;
+import org.bukkit.entity.Player;
+import org.bukkit.permissions.PermissionAttachment;
+import ru.simsonic.rscPermissions.DataTypes.RowReward;
+import ru.simsonic.utilities.TimeIntervalParser;
+
+public class Rewards
+{
+ private static final String strCommandExecutorPlayer = "player:";
+ private static final String strCommandExecutorConsole = "console:";
+ private final MainPluginClass plugin;
+ public Rewards(MainPluginClass rscp)
+ {
+ this.plugin = rscp;
+ }
+ protected final HashMap> rewards = new HashMap<>();
+ public synchronized int ImportRewards(RowReward[] rows)
+ {
+ rewards.clear();
+ if(rows == null)
+ return 0;
+ int total_rewards = 0;
+ for(RowReward row : rows)
+ {
+ ArrayList userRewards = rewards.get(row.user.toLowerCase());
+ if(userRewards == null)
+ {
+ userRewards = new ArrayList<>();
+ rewards.put(row.user.toLowerCase(), userRewards);
+ }
+ userRewards.add(row);
+ total_rewards += 1;
+ }
+ return total_rewards;
+ }
+ public synchronized HashMap getAvailableRewards(String user)
+ {
+ final HashMap result = new HashMap<>();
+ ArrayList user_rewards = rewards.get(user.toLowerCase());
+ if(user_rewards != null)
+ for(RowReward reward : user_rewards)
+ result.put(reward.code, result.get(reward.code) + 1);
+ return result;
+ }
+ public synchronized RowReward getRewardDetails(String user, String code)
+ {
+ if((user == null) || (code == null))
+ return null;
+ if("".equals(user) || "".equals(code))
+ return null;
+ ArrayList user_rewards = rewards.get(user.toLowerCase());
+ for(RowReward row : user_rewards)
+ if(row.code.equalsIgnoreCase(code))
+ return row;
+ return null;
+ }
+ public void executeReward(final Player player, String reward)
+ {
+ if(plugin.settings.isRewardsEnabled() == false)
+ {
+ plugin.Message(player, "Rewards support has been disabled by administrator.");
+ return;
+ }
+ final HashMap reward_list = getAvailableRewards(player.getName());
+ if(reward == null)
+ {
+ if(reward_list.isEmpty() == false)
+ {
+ String text = "";
+ for(String code : reward_list.keySet())
+ {
+ Integer count = reward_list.get(code);
+ text += ((count > 1) ? code + " (" + count.toString() + "), " : code + ", ");
+ }
+ plugin.Message(player, "Available rewards: {_R}" + text.substring(0, text.length() - 2) + ".");
+ }
+ else
+ plugin.Message(player, "No rewards available.");
+ return;
+ }
+ for(String code : reward_list.keySet())
+ if(code.equalsIgnoreCase(reward))
+ reward = code;
+ final String correctFinalReward = reward;
+ Integer count = reward_list.get(reward);
+ if(count != null)
+ {
+ Thread rewardExecutor = new Thread()
+ {
+ @Override
+ public void run()
+ {
+ Thread.currentThread().setName("rscp:Rewarder");
+ Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
+ try
+ {
+ plugin.connectionList.threadFetchTablesData().join();
+ final RowReward details = getRewardDetails(player.getName(), correctFinalReward);
+ threadApplyReward(player, details);
+ plugin.connectionList.threadFetchTablesData().join();
+ } catch(InterruptedException ex) {
+ }
+ plugin.Message(player, "You have received reward \"" + correctFinalReward + "\"!");
+ }
+ };
+ rewardExecutor.start();
+ }
+ else
+ plugin.Message(player, "No such reward available for you.");
+ }
+ public void threadApplyReward(final Player player, final RowReward reward)
+ {
+ if((reward == null) || (player == null))
+ return;
+ if(reward.add_group != null)
+ {
+ if(reward.add_group_destination != null)
+ {
+ }
+ if(reward.add_group_expirience != 0)
+ {
+ }
+ if(reward.add_group_interval != null)
+ {
+ int seconds = TimeIntervalParser.parseTimeInterval(reward.add_group_interval);
+ }
+ }
+ if(reward.execute_commands != null)
+ {
+ final String[] commands = reward.execute_commands.split("[\r\n]+");
+ plugin.getServer().getScheduler().runTask(plugin, new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ PermissionAttachment temporary = player.addAttachment(plugin);
+ if(reward.command_permissions != null)
+ {
+ String[] permissions = reward.command_permissions.split("[\r\n]+");
+ for(String permission : permissions)
+ {
+ MainPluginClass.consoleLog.log(Level.INFO, "[rscp] + temp perm: \"{0}\"", permission);
+ temporary.setPermission(permission, true);
+ }
+ }
+ player.recalculatePermissions();
+ for(String command : commands)
+ {
+ boolean bConsole = false;
+ if(command.toLowerCase().startsWith(strCommandExecutorConsole))
+ {
+ command = command.substring(strCommandExecutorConsole.length());
+ bConsole = true;
+ }
+ if(command.toLowerCase().startsWith(strCommandExecutorPlayer))
+ {
+ command = command.substring(strCommandExecutorPlayer.length());
+ bConsole = false;
+ }
+ MainPluginClass.consoleLog.log(Level.INFO,
+ "[rscp] Reward \"{0}\" for user \"{1}\" executes command:\n{2} /{3}", new Object[]
+ {
+ reward.code,
+ player.getName(),
+ bConsole ? strCommandExecutorConsole : strCommandExecutorPlayer,
+ command,
+ });
+ plugin.getServer().dispatchCommand(bConsole ? plugin.getServer().getConsoleSender() : player, command);
+ player.sendMessage("You've received reward \"" + reward.code + "\".");
+ }
+ player.removeAttachment(temporary);
+ player.recalculatePermissions();
+ }
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/simsonic/rscPermissions/Settings.java b/src/main/java/ru/simsonic/rscPermissions/Settings.java
new file mode 100644
index 0000000..a70cecd
--- /dev/null
+++ b/src/main/java/ru/simsonic/rscPermissions/Settings.java
@@ -0,0 +1,162 @@
+package ru.simsonic.rscPermissions;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import org.bukkit.configuration.file.FileConfiguration;
+
+public class Settings
+{
+ private final MainPluginClass plugin;
+ private String strDefaultGroup = "Default";
+ private String strMaintenanceMode = "";
+ private boolean bAlwaysInheritDefault = false;
+ private boolean bTreatAsteriskAsOP = true;
+ private boolean bRewardsEnabled = false;
+ private boolean bUseMetrics = true;
+ private boolean bUseUpdater = true;
+ private boolean bUseWorldGuard = true;
+ private boolean bUseResidence = true;
+ private int nAutoReloadDelayTicks = 20 * 900;
+ private int nRegionFinderGranularity = 1000;
+ public final int CurrentVersion = 2;
+ public static final String separator = ".";
+ public static final String separatorRegExp = "\\.";
+ public static final String instantiator = "?";
+ public static final String instantiatorRegExp = "\\?";
+ public Settings(final MainPluginClass plugin)
+ {
+ this.plugin = plugin;
+ }
+ public void onLoad()
+ {
+ plugin.saveDefaultConfig();
+ final FileConfiguration config = plugin.getConfig();
+ switch(plugin.getConfig().getInt("internal.version", CurrentVersion))
+ {
+ case 1:
+ update_v1_to_v2(config);
+ MainPluginClass.consoleLog.info("[rscp] Configuration updated from v1 to v2.");
+ case CurrentVersion: // Current version
+ plugin.saveConfig();
+ break;
+ }
+ }
+ private void update_v1_to_v2(FileConfiguration config)
+ {
+ config.set("settings.enable-bans", null);
+ config.set("settings.integration.worldguard", true);
+ config.set("settings.integration.residence", true);
+ config.set("internal.version", 2);
+ }
+ public void readSettings()
+ {
+ plugin.reloadConfig();
+ final FileConfiguration config = plugin.getConfig();
+ strDefaultGroup = config.getString("settings.default-group", "Default");
+ strMaintenanceMode = config.getString("settings.maintenance-mode", "");
+ bAlwaysInheritDefault = config.getBoolean("always-inherit-default-group", false);
+ bTreatAsteriskAsOP = config.getBoolean("settings.treat-asterisk-as-op", true);
+ bRewardsEnabled = config.getBoolean("settings.enable-rewards", false);
+ bUseMetrics = config.getBoolean("settings.use-metrics", true);
+ bUseUpdater = config.getBoolean("settings.auto-update", true);
+ bUseWorldGuard = config.getBoolean("settings.integration.worldguard", true);
+ bUseResidence = config.getBoolean("settings.integration.residence", true);
+ nAutoReloadDelayTicks = 20 * config.getInt("settings.auto-reload-delay-sec", 900);
+ nRegionFinderGranularity = config.getInt("settings.region-finder-thread-granularity-msec", 1000);
+ }
+ public String getDefaultGroup()
+ {
+ return strDefaultGroup;
+ }
+ public boolean isInMaintenance()
+ {
+ return !"".equals(strMaintenanceMode);
+ }
+ public String getMaintenanceMode()
+ {
+ return strMaintenanceMode;
+ }
+ public void setMaintenanceMode(String mMode)
+ {
+ strMaintenanceMode = (mMode == null) ? "" : mMode;
+ plugin.getConfig().set("settings.maintenance-mode", strMaintenanceMode);
+ plugin.saveConfig();
+ }
+ public boolean isDefaultForever()
+ {
+ return bAlwaysInheritDefault;
+ }
+ public boolean isAsteriskOP()
+ {
+ return bTreatAsteriskAsOP;
+ }
+ public boolean isRewardsEnabled()
+ {
+ return bRewardsEnabled;
+ }
+ public boolean isUseMetrics()
+ {
+ return bUseMetrics;
+ }
+ public boolean isUpdatable()
+ {
+ return bUseUpdater;
+ }
+ public boolean isUseWorldGuard()
+ {
+ return bUseWorldGuard;
+ }
+ public boolean isUseResidence()
+ {
+ return bUseResidence;
+ }
+ public int getAutoReloadDelayTicks()
+ {
+ return nAutoReloadDelayTicks;
+ }
+ public long getRegionFinderGranularity()
+ {
+ return nRegionFinderGranularity;
+ }
+ public ConnectionHelper getConnectionChain()
+ {
+ List