PageRenderTime 780ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java

https://github.com/talmor/worldguard
Java | 817 lines | 441 code | 108 blank | 268 comment | 97 complexity | f88f12a879a316d00dbc58035372b8eb MD5 | raw file
  1. // $Id$
  2. /*
  3. * WorldGuard
  4. * Copyright (C) 2010 sk89q <http://www.sk89q.com>
  5. *
  6. * This program is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. package com.sk89q.worldguard.bukkit;
  20. import java.io.File;
  21. import java.io.FileNotFoundException;
  22. import java.io.FileOutputStream;
  23. import java.io.IOException;
  24. import java.io.InputStream;
  25. import java.util.ArrayList;
  26. import java.util.Arrays;
  27. import java.util.Iterator;
  28. import java.util.List;
  29. import java.util.jar.JarFile;
  30. import java.util.logging.Filter;
  31. import java.util.logging.Logger;
  32. import java.util.zip.ZipEntry;
  33. import org.bukkit.ChatColor;
  34. import org.bukkit.Location;
  35. import org.bukkit.World;
  36. import org.bukkit.World.Environment;
  37. import org.bukkit.block.Block;
  38. import org.bukkit.command.Command;
  39. import org.bukkit.command.CommandSender;
  40. import org.bukkit.entity.Player;
  41. import org.bukkit.plugin.Plugin;
  42. import org.bukkit.plugin.java.JavaPlugin;
  43. import com.sk89q.bukkit.migration.PermissionsResolverManager;
  44. import com.sk89q.minecraft.util.commands.CommandException;
  45. import com.sk89q.minecraft.util.commands.CommandPermissionsException;
  46. import com.sk89q.minecraft.util.commands.CommandUsageException;
  47. import com.sk89q.minecraft.util.commands.CommandsManager;
  48. import com.sk89q.minecraft.util.commands.MissingNestedCommandException;
  49. import com.sk89q.minecraft.util.commands.WrappedCommandException;
  50. import com.sk89q.worldedit.bukkit.WorldEditPlugin;
  51. import com.sk89q.worldguard.LocalPlayer;
  52. import com.sk89q.worldguard.TickSyncDelayLoggerFilter;
  53. import com.sk89q.worldguard.bukkit.commands.GeneralCommands;
  54. import com.sk89q.worldguard.bukkit.commands.ProtectionCommands;
  55. import com.sk89q.worldguard.bukkit.commands.ToggleCommands;
  56. import com.sk89q.worldguard.protection.GlobalRegionManager;
  57. import com.sk89q.worldguard.protection.managers.RegionManager;
  58. /**
  59. * The main class for WorldGuard as a Bukkit plugin.
  60. *
  61. * @author sk89q
  62. */
  63. public class WorldGuardPlugin extends JavaPlugin {
  64. /**
  65. * Logger for messages.
  66. */
  67. static final Logger logger = Logger.getLogger("Minecraft.WorldGuard");
  68. /**
  69. * Manager for commands. This automatically handles nested commands,
  70. * permissions checking, and a number of other fancy command things.
  71. * We just set it up and register commands against it.
  72. */
  73. private final CommandsManager<CommandSender> commands;
  74. /**
  75. * Handles the region databases for all worlds.
  76. */
  77. private final GlobalRegionManager globalRegionManager;
  78. /**
  79. * Handles all configuration.
  80. */
  81. private final ConfigurationManager configuration;
  82. /**
  83. * Processes queries for permissions information. The permissions manager
  84. * is from WorldEdit and it automatically handles looking up permissions
  85. * systems and picking the right one. WorldGuard just needs to call
  86. * the permission methods.
  87. */
  88. private PermissionsResolverManager perms;
  89. /**
  90. * Used for scheduling flags.
  91. */
  92. private FlagStateManager flagStateManager;
  93. /**
  94. * Construct objects. Actual loading occurs when the plugin is enabled, so
  95. * this merely instantiates the objects.
  96. */
  97. public WorldGuardPlugin() {
  98. configuration = new ConfigurationManager(this);
  99. globalRegionManager = new GlobalRegionManager(this);
  100. final WorldGuardPlugin plugin = this;
  101. commands = new CommandsManager<CommandSender>() {
  102. @Override
  103. public boolean hasPermission(CommandSender player, String perm) {
  104. return plugin.hasPermission(player, perm);
  105. }
  106. };
  107. // Register command classes
  108. commands.register(ToggleCommands.class);
  109. commands.register(ProtectionCommands.class);
  110. commands.register(GeneralCommands.class);
  111. }
  112. /**
  113. * Called on plugin enable.
  114. */
  115. public void onEnable() {
  116. // Need to create the plugins/WorldGuard folder
  117. getDataFolder().mkdirs();
  118. // Set up permissions
  119. perms = new PermissionsResolverManager(this, getDescription().getName(), logger);
  120. perms.load();
  121. // This must be done before configuration is laoded
  122. LegacyWorldGuardMigration.migrateBlacklist(this);
  123. // Load the configuration
  124. configuration.load();
  125. globalRegionManager.preload();
  126. // Migrate regions after the regions were loaded because
  127. // the migration code reuses the loaded region managers
  128. LegacyWorldGuardMigration.migrateRegions(this);
  129. flagStateManager = new FlagStateManager(this);
  130. if (configuration.useRegionsScheduler) {
  131. getServer().getScheduler().scheduleAsyncRepeatingTask(this, flagStateManager, FlagStateManager.RUN_DELAY, FlagStateManager.RUN_DELAY);
  132. }
  133. if (configuration.suppressTickSyncWarnings) {
  134. Logger.getLogger("Minecraft").setFilter(
  135. new TickSyncDelayLoggerFilter());
  136. } else {
  137. Filter filter = Logger.getLogger("Minecraft").getFilter();
  138. if (filter != null && filter instanceof TickSyncDelayLoggerFilter) {
  139. Logger.getLogger("Minecraft").setFilter(null);
  140. }
  141. }
  142. // Register events
  143. (new WorldGuardPlayerListener(this)).registerEvents();
  144. (new WorldGuardBlockListener(this)).registerEvents();
  145. (new WorldGuardEntityListener(this)).registerEvents();
  146. (new WorldGuardWeatherListener(this)).registerEvents();
  147. (new WorldGuardVehicleListener(this)).registerEvents();
  148. // handle worlds separately to initialize already loaded worlds
  149. WorldGuardWorldListener worldListener = (new WorldGuardWorldListener(this));
  150. for (World world : getServer().getWorlds()) {
  151. worldListener.initWorld(world);
  152. }
  153. // Check god mode for existing players, if any
  154. for (Player player : getServer().getOnlinePlayers()) {
  155. if (inGroup(player, "wg-invincible") ||
  156. (configuration.autoGodMode && hasPermission(player, "worldguard.auto-invincible"))) {
  157. configuration.enableGodMode(player);
  158. }
  159. }
  160. logger.info("WorldGuard " + this.getDescription().getVersion() + " enabled.");
  161. }
  162. /**
  163. * Called on plugin disable.
  164. */
  165. public void onDisable() {
  166. globalRegionManager.unload();
  167. configuration.unload();
  168. this.getServer().getScheduler().cancelTasks(this);
  169. logger.info("WorldGuard " + getDescription().getVersion() + " disabled.");
  170. }
  171. /**
  172. * Handle a command.
  173. */
  174. @Override
  175. public boolean onCommand(CommandSender sender, Command cmd, String label,
  176. String[] args) {
  177. try {
  178. commands.execute(cmd.getName(), args, sender, this, sender);
  179. } catch (CommandPermissionsException e) {
  180. sender.sendMessage(ChatColor.RED + "You don't have permission.");
  181. } catch (MissingNestedCommandException e) {
  182. sender.sendMessage(ChatColor.RED + e.getUsage());
  183. } catch (CommandUsageException e) {
  184. sender.sendMessage(ChatColor.RED + e.getMessage());
  185. sender.sendMessage(ChatColor.RED + e.getUsage());
  186. } catch (WrappedCommandException e) {
  187. if (e.getCause() instanceof NumberFormatException) {
  188. sender.sendMessage(ChatColor.RED + "Number expected, string received instead.");
  189. } else {
  190. sender.sendMessage(ChatColor.RED + "An error has occurred. See console.");
  191. e.printStackTrace();
  192. }
  193. } catch (CommandException e) {
  194. sender.sendMessage(ChatColor.RED + e.getMessage());
  195. }
  196. return true;
  197. }
  198. /**
  199. * Get the GlobalRegionManager.
  200. *
  201. * @return
  202. */
  203. public GlobalRegionManager getGlobalRegionManager() {
  204. return globalRegionManager;
  205. }
  206. /**
  207. * Get the WorldGuardConfiguraton.
  208. *
  209. * @return
  210. * @deprecated Use {@link #getGlobalStateManager()} instead
  211. */
  212. @Deprecated
  213. public ConfigurationManager getGlobalConfiguration() {
  214. return getGlobalStateManager();
  215. }
  216. /**
  217. * Gets the flag state manager.
  218. *
  219. * @return
  220. */
  221. public FlagStateManager getFlagStateManager() {
  222. return flagStateManager;
  223. }
  224. /**
  225. * Get the WorldGuardConfiguraton.
  226. *
  227. * @return
  228. */
  229. public ConfigurationManager getGlobalStateManager() {
  230. return configuration;
  231. }
  232. /**
  233. * Check whether a player is in a group.
  234. *
  235. * @param player
  236. * @param group
  237. * @return
  238. */
  239. public boolean inGroup(Player player, String group) {
  240. try {
  241. return perms.inGroup(player.getName(), group);
  242. } catch (Throwable t) {
  243. t.printStackTrace();
  244. return false;
  245. }
  246. }
  247. /**
  248. * Get the groups of a player.
  249. *
  250. * @param player
  251. * @return
  252. */
  253. public String[] getGroups(Player player) {
  254. try {
  255. return perms.getGroups(player.getName());
  256. } catch (Throwable t) {
  257. t.printStackTrace();
  258. return new String[0];
  259. }
  260. }
  261. /**
  262. * Gets the name of a command sender. This is a unique name and this
  263. * method should never return a "display name".
  264. *
  265. * @param sender
  266. * @return
  267. */
  268. public String toUniqueName(CommandSender sender) {
  269. if (sender instanceof Player) {
  270. return ((Player) sender).getName();
  271. } else {
  272. return "*Console*";
  273. }
  274. }
  275. /**
  276. * Gets the name of a command sender. This play be a display name.
  277. *
  278. * @param sender
  279. * @return
  280. */
  281. public String toName(CommandSender sender) {
  282. if (sender instanceof Player) {
  283. return ((Player) sender).getName();
  284. } else {
  285. return "*Console*";
  286. }
  287. }
  288. /**
  289. * Checks permissions.
  290. *
  291. * @param sender
  292. * @param perm
  293. * @return
  294. */
  295. public boolean hasPermission(CommandSender sender, String perm) {
  296. if (sender.isOp()) {
  297. if (sender instanceof Player) {
  298. if (this.getGlobalStateManager().get(((Player) sender).
  299. getWorld()).opPermissions) {
  300. return true;
  301. }
  302. } else {
  303. return true;
  304. }
  305. }
  306. // Invoke the permissions resolver
  307. if (sender instanceof Player) {
  308. Player player = (Player) sender;
  309. return perms.hasPermission(player.getWorld().getName(), player.getName(), perm);
  310. }
  311. return false;
  312. }
  313. /**
  314. * Checks permissions and throws an exception if permission is not met.
  315. *
  316. * @param sender
  317. * @param perm
  318. * @throws CommandPermissionsException
  319. */
  320. public void checkPermission(CommandSender sender, String perm)
  321. throws CommandPermissionsException {
  322. if (!hasPermission(sender, perm)) {
  323. throw new CommandPermissionsException();
  324. }
  325. }
  326. /**
  327. * Checks to see if the sender is a player, otherwise throw an exception.
  328. *
  329. * @param sender
  330. * @return
  331. * @throws CommandException
  332. */
  333. public Player checkPlayer(CommandSender sender)
  334. throws CommandException {
  335. if (sender instanceof Player) {
  336. return (Player) sender;
  337. } else {
  338. throw new CommandException("A player is expected.");
  339. }
  340. }
  341. /**
  342. * Match player names.
  343. *
  344. * @param filter
  345. * @return
  346. */
  347. public List<Player> matchPlayerNames(String filter) {
  348. Player[] players = getServer().getOnlinePlayers();
  349. filter = filter.toLowerCase();
  350. // Allow exact name matching
  351. if (filter.charAt(0) == '@' && filter.length() >= 2) {
  352. filter = filter.substring(1);
  353. for (Player player : players) {
  354. if (player.getName().equalsIgnoreCase(filter)) {
  355. List<Player> list = new ArrayList<Player>();
  356. list.add(player);
  357. return list;
  358. }
  359. }
  360. return new ArrayList<Player>();
  361. // Allow partial name matching
  362. } else if (filter.charAt(0) == '*' && filter.length() >= 2) {
  363. filter = filter.substring(1);
  364. List<Player> list = new ArrayList<Player>();
  365. for (Player player : players) {
  366. if (player.getName().toLowerCase().contains(filter)) {
  367. list.add(player);
  368. }
  369. }
  370. return list;
  371. // Start with name matching
  372. } else {
  373. List<Player> list = new ArrayList<Player>();
  374. for (Player player : players) {
  375. if (player.getName().toLowerCase().startsWith(filter)) {
  376. list.add(player);
  377. }
  378. }
  379. return list;
  380. }
  381. }
  382. /**
  383. * Checks if the given list of players is greater than size 0, otherwise
  384. * throw an exception.
  385. *
  386. * @param players
  387. * @return
  388. * @throws CommandException
  389. */
  390. protected Iterable<Player> checkPlayerMatch(List<Player> players)
  391. throws CommandException {
  392. // Check to see if there were any matches
  393. if (players.size() == 0) {
  394. throw new CommandException("No players matched query.");
  395. }
  396. return players;
  397. }
  398. /**
  399. * Checks permissions and throws an exception if permission is not met.
  400. *
  401. * @param source
  402. * @param filter
  403. * @return iterator for players
  404. * @throws CommandException no matches found
  405. */
  406. public Iterable<Player> matchPlayers(CommandSender source, String filter)
  407. throws CommandException {
  408. if (getServer().getOnlinePlayers().length == 0) {
  409. throw new CommandException("No players matched query.");
  410. }
  411. if (filter.equals("*")) {
  412. return checkPlayerMatch(Arrays.asList(getServer().getOnlinePlayers()));
  413. }
  414. // Handle special hash tag groups
  415. if (filter.charAt(0) == '#') {
  416. // Handle #world, which matches player of the same world as the
  417. // calling source
  418. if (filter.equalsIgnoreCase("#world")) {
  419. List<Player> players = new ArrayList<Player>();
  420. Player sourcePlayer = checkPlayer(source);
  421. World sourceWorld = sourcePlayer.getWorld();
  422. for (Player player : getServer().getOnlinePlayers()) {
  423. if (player.getWorld().equals(sourceWorld)) {
  424. players.add(player);
  425. }
  426. }
  427. return checkPlayerMatch(players);
  428. // Handle #near, which is for nearby players.
  429. } else if (filter.equalsIgnoreCase("#near")) {
  430. List<Player> players = new ArrayList<Player>();
  431. Player sourcePlayer = checkPlayer(source);
  432. World sourceWorld = sourcePlayer.getWorld();
  433. org.bukkit.util.Vector sourceVector
  434. = sourcePlayer.getLocation().toVector();
  435. for (Player player : getServer().getOnlinePlayers()) {
  436. if (player.getWorld().equals(sourceWorld)
  437. && player.getLocation().toVector().distanceSquared(
  438. sourceVector) < 900) {
  439. players.add(player);
  440. }
  441. }
  442. return checkPlayerMatch(players);
  443. } else {
  444. throw new CommandException("Invalid group '" + filter + "'.");
  445. }
  446. }
  447. List<Player> players = matchPlayerNames(filter);
  448. return checkPlayerMatch(players);
  449. }
  450. /**
  451. * Match only a single player.
  452. *
  453. * @param sender
  454. * @param filter
  455. * @return
  456. * @throws CommandException
  457. */
  458. public Player matchSinglePlayer(CommandSender sender, String filter)
  459. throws CommandException {
  460. // This will throw an exception if there are no matches
  461. Iterator<Player> players = matchPlayers(sender, filter).iterator();
  462. Player match = players.next();
  463. // We don't want to match the wrong person, so fail if if multiple
  464. // players were found (we don't want to just pick off the first one,
  465. // as that may be the wrong player)
  466. if (players.hasNext()) {
  467. throw new CommandException("More than one player found! " +
  468. "Use @<name> for exact matching.");
  469. }
  470. return match;
  471. }
  472. /**
  473. * Match only a single player or console.
  474. *
  475. * @param sender
  476. * @param filter
  477. * @return
  478. * @throws CommandException
  479. */
  480. public CommandSender matchPlayerOrConsole(CommandSender sender, String filter)
  481. throws CommandException {
  482. // Let's see if console is wanted
  483. if (filter.equalsIgnoreCase("#console")
  484. || filter.equalsIgnoreCase("*console*")
  485. || filter.equalsIgnoreCase("!")) {
  486. try {
  487. return getServer().getConsoleSender();
  488. } catch (Throwable t) {
  489. // Legacy support
  490. return new LegacyConsoleSender(getServer());
  491. }
  492. }
  493. return matchSinglePlayer(sender, filter);
  494. }
  495. /**
  496. * Get a single player as an iterator for players.
  497. *
  498. * @param player
  499. * @return iterator for players
  500. */
  501. public Iterable<Player> matchPlayers(Player player) {
  502. return Arrays.asList(new Player[] {player});
  503. }
  504. /**
  505. * Match a world.
  506. * @param sender
  507. *
  508. * @param filter
  509. * @return
  510. * @throws CommandException
  511. */
  512. public World matchWorld(CommandSender sender, String filter) throws CommandException {
  513. List<World> worlds = getServer().getWorlds();
  514. // Handle special hash tag groups
  515. if (filter.charAt(0) == '#') {
  516. // #main for the main world
  517. if (filter.equalsIgnoreCase("#main")) {
  518. return worlds.get(0);
  519. // #normal for the first normal world
  520. } else if (filter.equalsIgnoreCase("#normal")) {
  521. for (World world : worlds) {
  522. if (world.getEnvironment() == Environment.NORMAL) {
  523. return world;
  524. }
  525. }
  526. throw new CommandException("No normal world found.");
  527. // #nether for the first nether world
  528. } else if (filter.equalsIgnoreCase("#nether")) {
  529. for (World world : worlds) {
  530. if (world.getEnvironment() == Environment.NETHER) {
  531. return world;
  532. }
  533. }
  534. throw new CommandException("No nether world found.");
  535. // Handle getting a world from a player
  536. } else if (filter.matches("^#player$")) {
  537. String parts[] = filter.split(":", 2);
  538. // They didn't specify an argument for the player!
  539. if (parts.length == 1) {
  540. throw new CommandException("Argument expected for #player.");
  541. }
  542. return matchPlayers(sender, parts[1]).iterator().next().getWorld();
  543. } else {
  544. throw new CommandException("Invalid identifier '" + filter + "'.");
  545. }
  546. }
  547. for (World world : worlds) {
  548. if (world.getName().equals(filter)) {
  549. return world;
  550. }
  551. }
  552. throw new CommandException("No world by that exact name found.");
  553. }
  554. /**
  555. * Gets a copy of the WorldEdit plugin.
  556. *
  557. * @return
  558. * @throws CommandException
  559. */
  560. public WorldEditPlugin getWorldEdit() throws CommandException {
  561. Plugin worldEdit = getServer().getPluginManager().getPlugin("WorldEdit");
  562. if (worldEdit == null) {
  563. throw new CommandException("WorldEdit does not appear to be installed.");
  564. }
  565. if (worldEdit instanceof WorldEditPlugin) {
  566. return (WorldEditPlugin) worldEdit;
  567. } else {
  568. throw new CommandException("WorldEdit detection failed (report error).");
  569. }
  570. }
  571. /**
  572. * Wrap a player as a LocalPlayer.
  573. *
  574. * @param player
  575. * @return
  576. */
  577. public LocalPlayer wrapPlayer(Player player) {
  578. return new BukkitPlayer(this, player);
  579. }
  580. /**
  581. * Create a default configuration file from the .jar.
  582. *
  583. * @param actual
  584. * @param defaultName
  585. */
  586. public void createDefaultConfiguration(File actual,
  587. String defaultName) {
  588. // Make parent directories
  589. File parent = actual.getParentFile();
  590. if (!parent.exists()) {
  591. parent.mkdirs();
  592. }
  593. if (actual.exists()) {
  594. return;
  595. }
  596. InputStream input =
  597. null;
  598. try {
  599. JarFile file = new JarFile(getFile());
  600. ZipEntry copy = file.getEntry("defaults/" + defaultName);
  601. if (copy == null) throw new FileNotFoundException();
  602. input = file.getInputStream(copy);
  603. } catch (IOException e) {
  604. logger.severe(getDescription().getName() + ": Unable to read default configuration: " + defaultName);
  605. }
  606. if (input != null) {
  607. FileOutputStream output = null;
  608. try {
  609. output = new FileOutputStream(actual);
  610. byte[] buf = new byte[8192];
  611. int length = 0;
  612. while ((length = input.read(buf)) > 0) {
  613. output.write(buf, 0, length);
  614. }
  615. logger.info("WorldGuard: Default configuration file written: "
  616. + actual.getAbsolutePath());
  617. } catch (IOException e) {
  618. e.printStackTrace();
  619. } finally {
  620. try {
  621. if (input != null) {
  622. input.close();
  623. }
  624. } catch (IOException e) {
  625. }
  626. try {
  627. if (output != null) {
  628. output.close();
  629. }
  630. } catch (IOException e) {
  631. }
  632. }
  633. }
  634. }
  635. /**
  636. * Notifies all with the notify permission.
  637. *
  638. * @param msg
  639. */
  640. public void broadcastNotification(String msg) {
  641. for (Player player : getServer().getOnlinePlayers()) {
  642. if (hasPermission(player, "worldguard.notify")) {
  643. player.sendMessage(msg);
  644. }
  645. }
  646. }
  647. /**
  648. * Forgets a player.
  649. *
  650. * @param player
  651. */
  652. public void forgetPlayer(Player player) {
  653. flagStateManager.forget(player);
  654. }
  655. /**
  656. * Checks to see if a player can build at a location. This will return
  657. * true if region protection is disabled.
  658. *
  659. * @param player
  660. * @param loc
  661. * @return
  662. */
  663. public boolean canBuild(Player player, Location loc) {
  664. return getGlobalRegionManager().canBuild(player, loc);
  665. }
  666. /**
  667. * Checks to see if a player can build at a location. This will return
  668. * true if region protection is disabled.
  669. *
  670. * @param player
  671. * @param block
  672. * @return
  673. */
  674. public boolean canBuild(Player player, Block block) {
  675. return getGlobalRegionManager().canBuild(player, block);
  676. }
  677. /**
  678. * Gets the region manager for a world.
  679. *
  680. * @param world world to get the region manager for
  681. * @return the region manager or null if regions are not enabled
  682. */
  683. public RegionManager getRegionManager(World world) {
  684. if (!getGlobalStateManager().get(world).useRegions) {
  685. return null;
  686. }
  687. return getGlobalRegionManager().get(world);
  688. }
  689. /**
  690. * Replace macros in the text.
  691. *
  692. * @param sender
  693. * @param message
  694. * @return
  695. */
  696. public String replaceMacros(CommandSender sender, String message) {
  697. Player[] online = getServer().getOnlinePlayers();
  698. message = message.replace("%name%", toName(sender));
  699. message = message.replace("%id%", toUniqueName(sender));
  700. message = message.replace("%online%", String.valueOf(online.length));
  701. if (sender instanceof Player) {
  702. Player player = (Player) sender;
  703. World world = player.getWorld();
  704. message = message.replace("%world%", world.getName());
  705. message = message.replace("%health%", String.valueOf(player.getHealth()));
  706. }
  707. return message;
  708. }
  709. }