/** @author andune */ @Singleton public class EssentialsModule implements com.andune.minecraft.hsp.integration.Essentials, Initializable { private static final Logger log = LoggerFactory.getLogger(EssentialsModule.class); private Plugin essentialsPlugin; private Map<String, List<PluginCommand>> altcommands; private final Plugin plugin; private final BukkitCommandRegister bukkitCommandRegister; private final Scheduler scheduler; @Inject public EssentialsModule( Plugin bukkitPlugin, BukkitCommandRegister bukkitCommandRegister, Scheduler scheduler) { this.plugin = bukkitPlugin; this.bukkitCommandRegister = bukkitCommandRegister; this.scheduler = scheduler; } /** * Called to register HSP's commands with Essentials, which then enables Essentials "respectful" * command usurp behavior which will let HSP own commands like "/home" and "/spawn". */ private void registerCommands() { log.debug("entering registerCommands()"); essentialsPlugin = plugin.getServer().getPluginManager().getPlugin("Essentials"); if (essentialsPlugin == null) { log.debug("Essentials plugin not found, registerComamnds() doing nothing"); return; } try { grabInternalAltCommands(); mapHSPCommands(); } catch (Exception e) { log.error("Caught exception when trying to register commands with Essentials", e); } log.debug("exiting registerCommands()"); } /** * Using Essentials own internal alternate commands map, assign HSP commands into the map. This is * a replication of the internal algorithm that Essentials uses in AlternativeCommandsHandler as * of Essentials 2.10.1. */ private void mapHSPCommands() { Map<String, PluginCommand> hspCommands = bukkitCommandRegister.getLoadedCommands(); Collection<PluginCommand> commands = hspCommands.values(); // final String pluginName = plugin.getDescription().getName().toLowerCase(); log.debug("commands.size() = {}", commands == null ? null : commands.size()); for (Command command : commands) { final PluginCommand pc = (PluginCommand) command; final List<String> labels = new ArrayList<String>(pc.getAliases()); labels.add(pc.getName()); log.debug("registering command {}", pc.getName()); // PluginCommand reg = plugin.getServer().getPluginCommand(pluginName + ":" + // pc.getName().toLowerCase()); // if (reg == null) // { // reg = plugin.getServer().getPluginCommand(pc.getName().toLowerCase()); // } // if (reg == null || !reg.getPlugin().equals(plugin)) // { // continue; // } // log.debug("reg = {}", reg); for (String label : labels) { log.debug("registering label {}", label); List<PluginCommand> plugincommands = altcommands.get(label.toLowerCase()); if (plugincommands == null) { plugincommands = new ArrayList<PluginCommand>(); altcommands.put(label.toLowerCase(), plugincommands); } boolean found = false; for (PluginCommand pc2 : plugincommands) { if (pc2.getPlugin().equals(plugin)) { found = true; } } if (!found) { plugincommands.add(pc); } } } } /** * Method to grab Essentials internal altCommands hash. This is ugly code but Essentials offers no * external APIs to make this any cleaner. * * @throws NoSuchFieldException * @throws SecurityException * @throws IllegalAccessException * @throws IllegalArgumentException */ @SuppressWarnings("unchecked") private void grabInternalAltCommands() throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { Essentials pluginObject = (Essentials) essentialsPlugin; Field commandHandler = pluginObject.getClass().getDeclaredField("alternativeCommandsHandler"); commandHandler.setAccessible(true); AlternativeCommandsHandler ach = (AlternativeCommandsHandler) commandHandler.get(pluginObject); Field altCommandsField = ach.getClass().getDeclaredField("altcommands"); altCommandsField.setAccessible(true); altcommands = (HashMap<String, List<PluginCommand>>) altCommandsField.get(ach); log.debug("altcommands = {}", altcommands); } @Override public void init() throws Exception { // we register commands a few ticks after the server has started // up. This gives Essentials time to load (if present). scheduler.scheduleSyncDelayedTask( new Runnable() { public void run() { registerCommands(); } }, 3); } @Override public void shutdown() throws Exception {} @Override public int getInitPriority() { return 9; } @Override public boolean isEnabled() { return essentialsPlugin != null; } @Override public String getVersion() { if (essentialsPlugin != null) return essentialsPlugin.getDescription().getVersion(); else return null; } }
/** * Implementation of ChunkStorage that uses the file system. * * <p>This is for TESTING ONLY. It is expected that on a production server, thousands of chunk files * will cause I/O delays, the same as the original MC server did when it had one-file-per-chunk. A * solution is to group files into regions like MC does now, so if someone wants to contribute such * an implementation, it could be used on production servers. * * @author andune */ public class FileChunkStorage implements ChunkStorage { private final Logger log = LoggerFactory.getLogger(FileChunkStorage.class); private final File worldContainer; public FileChunkStorage(Plugin plugin) { this.worldContainer = plugin.getServer().getWorldContainer(); } /** * Return the region directory and create it if it doesn't exist. * * @param worldName * @param chunkX * @param chunkZ * @return */ private File getRegionDirectory(String worldName, int chunkX, int chunkZ) { int regionX = chunkX >> 4; int regionZ = chunkZ >> 4; File regionDirectory = new File(worldContainer, worldName + "/blockOwner/" + regionX + "_" + regionZ); if (!regionDirectory.exists()) { regionDirectory.mkdirs(); } return regionDirectory; } @Override public void save(Chunk chunk) throws IOException { File regionDirectory = getRegionDirectory(chunk.world, chunk.x, chunk.z); File f = new File(regionDirectory, chunk.x + "_" + chunk.z); DataOutputStream os = null; try { os = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(f))); os.writeInt(chunk.map.assigned); final int[] keys = chunk.map.keys; final short[] values = chunk.map.values; final boolean[] states = chunk.map.allocated; for (int i = 0; i < states.length; i++) { if (states[i]) { os.writeInt(keys[i]); os.writeShort(values[i]); } } os.flush(); } finally { if (os != null) os.close(); } } @Override public void load(Chunk chunk) throws IOException { File regionDirectory = getRegionDirectory(chunk.world, chunk.x, chunk.z); File f = new File(regionDirectory, chunk.x + "_" + chunk.z); // if no file exists, just initialize a small-capacity map if (!f.exists()) { log.debug("load on chunk {}, no file exists, initializing empty dataset", chunk); chunk.map = new IntShortOpenHashMap(100); return; } DataInputStream is = null; try { is = new DataInputStream(new BufferedInputStream(new FileInputStream(f))); // first 32 bits represent the size of the map int available = is.readInt(); // we round up by 30% to account for .75 load factor in hash. // this initializes the map at less than the load factor with // some extra room for growth before needing a clone & grow chunk.map = new IntShortOpenHashMap((int) (available * 1.3)); while (is.available() > 0) { int key = is.readInt(); short value = is.readShort(); chunk.map.put(key, value); if (log.isDebugEnabled()) { Formatter format = new Formatter(); format.format("loaded chunk{%d,%d} owner %d for key %x", chunk.x, chunk.z, value, key); log.debug(format.toString()); format.close(); } } } finally { if (is != null) is.close(); } } }
/** @author andune */ public class HomeInviteDAOEBean implements HomeInviteDAO { private static final Logger log = LoggerFactory.getLogger(HomeInviteDAOEBean.class); protected static final String TABLE = "hsp_homeinvite"; private EbeanServer ebean; private final Storage storage; private final ConfigCore configCore; private final EbeanStorageUtil util; public HomeInviteDAOEBean( final EbeanServer ebean, final Storage storage, ConfigCore configCore, final EbeanStorageUtil util) { setEbeanServer(ebean); this.storage = storage; this.configCore = configCore; this.util = util; } public void setEbeanServer(final EbeanServer ebean) { this.ebean = ebean; } @Override public HomeInvite findHomeInviteById(int id) { String q = "find homeInvite where id = :id"; Query<HomeInvite> query = ebean.createQuery(HomeInvite.class, q); query.setParameter("id", id); return query.findUnique(); } @Override public HomeInvite findInviteByHomeAndInvitee(Home home, String invitee) { String q; if (configCore.useEbeanSearchLower()) q = "find homeInvite where home = :home and lower(invitedPlayer) = lower(:invitee)"; else q = "find homeInvite where home = :home and invitedPlayer = :invitee"; Query<HomeInvite> query = ebean.createQuery(HomeInvite.class, q); query.setParameter("home", home.getId()); query.setParameter("invitee", invitee); return query.findUnique(); } @Override public Set<HomeInvite> findInvitesByHome(Home home) { String q = "find homeInvite where home = :home"; Query<HomeInvite> query = ebean.createQuery(HomeInvite.class, q); query.setParameter("home", home.getId()); return query.findSet(); } @Override public Set<HomeInvite> findAllAvailableInvites(String invitee) { String q; if (configCore.useEbeanSearchLower()) q = "find homeInvite where lower(invitedPlayer) = lower(:invitee)"; else q = "find homeInvite where invitedPlayer = :invitee"; Query<HomeInvite> query = ebean.createQuery(HomeInvite.class, q); query.setParameter("invitee", invitee); return query.findSet(); } @Override public Set<HomeInvite> findAllPublicInvites() { return findAllAvailableInvites(HomeInvite.PUBLIC_HOME); } @Override public Set<HomeInvite> findAllOpenInvites(String player) { Set<HomeInvite> invites = new HashSet<HomeInvite>(5); // first find all homes for this player Set<? extends Home> homes = storage.getHomeDAO().findHomesByPlayer(player); if (homes == null || homes.size() == 0) return invites; // then find all HomeInvites related to any of those homes for (Home home : homes) { Set<HomeInvite> homeInvites = findInvitesByHome(home); if (homeInvites != null) invites.addAll(homeInvites); } return invites; } @Override public void saveHomeInvite(HomeInvite homeInvite) { Transaction tx = ebean.beginTransaction(); ebean.save(homeInvite, tx); tx.commit(); } @Override public void deleteHomeInvite(HomeInvite homeInvite) throws StorageException { Transaction tx = ebean.beginTransaction(); tx.setPersistCascade(false); ebean.delete(homeInvite, tx); tx.commit(); } @Override public Set<HomeInvite> findAllHomeInvites() { return ebean.find(HomeInvite.class).findSet(); } @Override public int purgePlayerData(long purgeTime) { return util.purgePlayers(this, purgeTime); } @Override public int purgeWorldData(final String world) { int rowsPurged = 0; // delete any invites whose home is on the purged world Set<HomeInvite> set = findAllHomeInvites(); for (HomeInvite hi : set) { if (world.equals(hi.getHome().getWorld())) { try { deleteHomeInvite(hi); rowsPurged++; } catch (Exception e) { log.error("Caught exception while purging homeInvite", e); } } } return rowsPurged; } @Override public int purgePlayer(String playerName) { // first delete any homeInvites the player was invited too int rowsPurged = util.deleteRows(TABLE, "invited_player", playerName); // then delete any HomeInvites the player sent out to others Set<HomeInvite> set = findAllOpenInvites(playerName); for (HomeInvite hi : set) { try { deleteHomeInvite(hi); rowsPurged++; } catch (Exception e) { log.error("Caught exception while purging homeInvite", e); } } return rowsPurged; } @Override public Set<String> getAllPlayerNames() { Set<HomeInvite> set = findAllHomeInvites(); Set<String> playerNames = new HashSet<String>(set.size() * 3 / 2); for (HomeInvite hi : set) { playerNames.add(hi.getInvitedPlayer()); playerNames.add(hi.getHome().getPlayerName()); } return playerNames; } }