PageRenderTime 220ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java

https://gitlab.com/Skull3x/WorldEdit
Java | 466 lines | 285 code | 69 blank | 112 comment | 80 complexity | 1455cb648a263e425cc0ccce7b5db38e MD5 | raw file
  1. /*
  2. * WorldEdit, a Minecraft world manipulation toolkit
  3. * Copyright (C) sk89q <http://www.sk89q.com>
  4. * Copyright (C) WorldEdit team and contributors
  5. *
  6. * This program is free software: you can redistribute it and/or modify it
  7. * under the terms of the GNU Lesser General Public License as published by the
  8. * 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, but WITHOUT
  12. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  14. * for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. package com.sk89q.worldedit.extension.platform;
  20. import com.sk89q.worldedit.LocalConfiguration;
  21. import com.sk89q.worldedit.LocalSession;
  22. import com.sk89q.worldedit.ServerInterface;
  23. import com.sk89q.worldedit.Vector;
  24. import com.sk89q.worldedit.WorldEdit;
  25. import com.sk89q.worldedit.WorldVector;
  26. import com.sk89q.worldedit.command.tool.BlockTool;
  27. import com.sk89q.worldedit.command.tool.DoubleActionBlockTool;
  28. import com.sk89q.worldedit.command.tool.DoubleActionTraceTool;
  29. import com.sk89q.worldedit.command.tool.Tool;
  30. import com.sk89q.worldedit.command.tool.TraceTool;
  31. import com.sk89q.worldedit.entity.Player;
  32. import com.sk89q.worldedit.event.platform.BlockInteractEvent;
  33. import com.sk89q.worldedit.event.platform.ConfigurationLoadEvent;
  34. import com.sk89q.worldedit.event.platform.Interaction;
  35. import com.sk89q.worldedit.event.platform.PlatformInitializeEvent;
  36. import com.sk89q.worldedit.event.platform.PlatformReadyEvent;
  37. import com.sk89q.worldedit.event.platform.PlayerInputEvent;
  38. import com.sk89q.worldedit.extension.platform.permission.ActorSelectorLimits;
  39. import com.sk89q.worldedit.internal.ServerInterfaceAdapter;
  40. import com.sk89q.worldedit.regions.RegionSelector;
  41. import com.sk89q.worldedit.util.Location;
  42. import com.sk89q.worldedit.util.eventbus.Subscribe;
  43. import com.sk89q.worldedit.world.World;
  44. import javax.annotation.Nullable;
  45. import java.util.ArrayList;
  46. import java.util.EnumMap;
  47. import java.util.Iterator;
  48. import java.util.List;
  49. import java.util.Map;
  50. import java.util.Map.Entry;
  51. import java.util.concurrent.atomic.AtomicBoolean;
  52. import java.util.logging.Level;
  53. import java.util.logging.Logger;
  54. import static com.google.common.base.Preconditions.checkNotNull;
  55. /**
  56. * Manages registered {@link Platform}s for WorldEdit. Platforms are
  57. * implementations of WorldEdit.
  58. *
  59. * <p>This class is thread-safe.</p>
  60. */
  61. public class PlatformManager {
  62. private static final Logger logger = Logger.getLogger(PlatformManager.class.getCanonicalName());
  63. private final WorldEdit worldEdit;
  64. private final CommandManager commandManager;
  65. private final List<Platform> platforms = new ArrayList<Platform>();
  66. private final Map<Capability, Platform> preferences = new EnumMap<Capability, Platform>(Capability.class);
  67. private @Nullable String firstSeenVersion;
  68. private final AtomicBoolean initialized = new AtomicBoolean();
  69. private final AtomicBoolean configured = new AtomicBoolean();
  70. /**
  71. * Create a new platform manager.
  72. *
  73. * @param worldEdit the WorldEdit instance
  74. */
  75. public PlatformManager(WorldEdit worldEdit) {
  76. checkNotNull(worldEdit);
  77. this.worldEdit = worldEdit;
  78. this.commandManager = new CommandManager(worldEdit, this);
  79. // Register this instance for events
  80. worldEdit.getEventBus().register(this);
  81. }
  82. /**
  83. * Register a platform with WorldEdit.
  84. *
  85. * @param platform the platform
  86. */
  87. public synchronized void register(Platform platform) {
  88. checkNotNull(platform);
  89. logger.log(Level.FINE, "Got request to register " + platform.getClass() + " with WorldEdit [" + super.toString() + "]");
  90. // Just add the platform to the list of platforms: we'll pick favorites
  91. // once all the platforms have been loaded
  92. platforms.add(platform);
  93. // Make sure that versions are in sync
  94. if (firstSeenVersion != null) {
  95. if (!firstSeenVersion.equals(platform.getVersion())) {
  96. logger.log(Level.WARNING, "Multiple ports of WorldEdit are installed but they report different versions ({0} and {1}). " +
  97. "If these two versions are truly different, then you may run into unexpected crashes and errors.",
  98. new Object[]{ firstSeenVersion, platform.getVersion() });
  99. }
  100. } else {
  101. firstSeenVersion = platform.getVersion();
  102. }
  103. }
  104. /**
  105. * Unregister a platform from WorldEdit.
  106. *
  107. * <p>If the platform has been chosen for any capabilities, then a new
  108. * platform will be found.</p>
  109. *
  110. * @param platform the platform
  111. */
  112. public synchronized boolean unregister(Platform platform) {
  113. checkNotNull(platform);
  114. boolean removed = platforms.remove(platform);
  115. if (removed) {
  116. logger.log(Level.FINE, "Unregistering " + platform.getClass().getCanonicalName() + " from WorldEdit");
  117. boolean choosePreferred = false;
  118. // Check whether this platform was chosen to be the preferred one
  119. // for any capability and be sure to remove it
  120. Iterator<Entry<Capability, Platform>> it = preferences.entrySet().iterator();
  121. while (it.hasNext()) {
  122. Entry<Capability, Platform> entry = it.next();
  123. if (entry.getValue().equals(platform)) {
  124. entry.getKey().unload(this, entry.getValue());
  125. it.remove();
  126. choosePreferred = true; // Have to choose new favorites
  127. }
  128. }
  129. if (choosePreferred) {
  130. choosePreferred();
  131. }
  132. }
  133. return removed;
  134. }
  135. /**
  136. * Get the preferred platform for handling a certain capability. Returns
  137. * null if none is available.
  138. *
  139. * @param capability the capability
  140. * @return the platform
  141. * @throws NoCapablePlatformException thrown if no platform is capable
  142. */
  143. public synchronized Platform queryCapability(Capability capability) throws NoCapablePlatformException {
  144. Platform platform = preferences.get(checkNotNull(capability));
  145. if (platform != null) {
  146. return platform;
  147. } else {
  148. throw new NoCapablePlatformException("No platform was found supporting " + capability.name());
  149. }
  150. }
  151. /**
  152. * Choose preferred platforms and perform necessary initialization.
  153. */
  154. private synchronized void choosePreferred() {
  155. for (Capability capability : Capability.values()) {
  156. Platform preferred = findMostPreferred(capability);
  157. if (preferred != null) {
  158. preferences.put(capability, preferred);
  159. capability.initialize(this, preferred);
  160. }
  161. }
  162. // Fire configuration event
  163. if (preferences.containsKey(Capability.CONFIGURATION) && configured.compareAndSet(false, true)) {
  164. worldEdit.getEventBus().post(new ConfigurationLoadEvent(queryCapability(Capability.CONFIGURATION).getConfiguration()));
  165. }
  166. }
  167. /**
  168. * Find the most preferred platform for a given capability from the list of
  169. * platforms. This does not use the map of preferred platforms.
  170. *
  171. * @param capability the capability
  172. * @return the most preferred platform, or null if no platform was found
  173. */
  174. private synchronized @Nullable Platform findMostPreferred(Capability capability) {
  175. Platform preferred = null;
  176. Preference highest = null;
  177. for (Platform platform : platforms) {
  178. Preference preference = platform.getCapabilities().get(capability);
  179. if (preference != null && (highest == null || preference.isPreferredOver(highest))) {
  180. preferred = platform;
  181. highest = preference;
  182. }
  183. }
  184. return preferred;
  185. }
  186. /**
  187. * Get a list of loaded platforms.
  188. *
  189. * <p>The returned list is a copy of the original and is mutable.</p>
  190. *
  191. * @return a list of platforms
  192. */
  193. public synchronized List<Platform> getPlatforms() {
  194. return new ArrayList<Platform>(platforms);
  195. }
  196. /**
  197. * Given a world, possibly return the same world but using a different
  198. * platform preferred for world editing operations.
  199. *
  200. * @param base the world to match
  201. * @return the preferred world, if one was found, otherwise the given world
  202. */
  203. public World getWorldForEditing(World base) {
  204. checkNotNull(base);
  205. World match = queryCapability(Capability.WORLD_EDITING).matchWorld(base);
  206. return match != null ? match : base;
  207. }
  208. /**
  209. * Given an actor, return a new one that may use a different platform
  210. * for permissions and world editing.
  211. *
  212. * @param base the base actor to match
  213. * @return a new delegate actor
  214. */
  215. @SuppressWarnings("unchecked")
  216. public <T extends Actor> T createProxyActor(T base) {
  217. checkNotNull(base);
  218. if (base instanceof Player) {
  219. Player player = (Player) base;
  220. Player permActor = queryCapability(Capability.PERMISSIONS).matchPlayer(player);
  221. if (permActor == null) {
  222. permActor = player;
  223. }
  224. Player cuiActor = queryCapability(Capability.WORLDEDIT_CUI).matchPlayer(player);
  225. if (cuiActor == null) {
  226. cuiActor = player;
  227. }
  228. return (T) new PlayerProxy(player, permActor, cuiActor, getWorldForEditing(player.getWorld()));
  229. } else {
  230. return base;
  231. }
  232. }
  233. /**
  234. * Get the command manager.
  235. *
  236. * @return the command manager
  237. */
  238. public CommandManager getCommandManager() {
  239. return commandManager;
  240. }
  241. /**
  242. * Get the current configuration.
  243. *
  244. * <p>If no platform has been registered yet, then a default configuration
  245. * will be returned.</p>
  246. *
  247. * @return the configuration
  248. */
  249. public LocalConfiguration getConfiguration() {
  250. return queryCapability(Capability.CONFIGURATION).getConfiguration();
  251. }
  252. /**
  253. * Return a legacy {@link ServerInterface}.
  254. *
  255. * @return a {@link ServerInterface}
  256. * @throws IllegalStateException if no platform has been registered
  257. */
  258. @SuppressWarnings("deprecation")
  259. public ServerInterface getServerInterface() throws IllegalStateException {
  260. return ServerInterfaceAdapter.adapt(queryCapability(Capability.USER_COMMANDS));
  261. }
  262. @Subscribe
  263. public void handlePlatformReady(PlatformReadyEvent event) {
  264. choosePreferred();
  265. if (initialized.compareAndSet(false, true)) {
  266. worldEdit.getEventBus().post(new PlatformInitializeEvent());
  267. }
  268. }
  269. @SuppressWarnings("deprecation")
  270. @Subscribe
  271. public void handleBlockInteract(BlockInteractEvent event) {
  272. // Create a proxy actor with a potentially different world for
  273. // making changes to the world
  274. Actor actor = createProxyActor(event.getCause());
  275. Location location = event.getLocation();
  276. Vector vector = location.toVector();
  277. // At this time, only handle interaction from players
  278. if (actor instanceof Player) {
  279. Player player = (Player) actor;
  280. LocalSession session = worldEdit.getSessionManager().get(actor);
  281. if (event.getType() == Interaction.HIT) {
  282. if (player.getItemInHand() == getConfiguration().wandItem) {
  283. if (!session.isToolControlEnabled()) {
  284. return;
  285. }
  286. if (!actor.hasPermission("worldedit.selection.pos")) {
  287. return;
  288. }
  289. RegionSelector selector = session.getRegionSelector(player.getWorld());
  290. if (selector.selectPrimary(location.toVector(), ActorSelectorLimits.forActor(player))) {
  291. selector.explainPrimarySelection(actor, session, vector);
  292. }
  293. event.setCancelled(true);
  294. return;
  295. }
  296. if (player.isHoldingPickAxe() && session.hasSuperPickAxe()) {
  297. final BlockTool superPickaxe = session.getSuperPickaxe();
  298. if (superPickaxe != null && superPickaxe.canUse(player)) {
  299. event.setCancelled(superPickaxe.actPrimary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session, location));
  300. return;
  301. }
  302. }
  303. Tool tool = session.getTool(player.getItemInHand());
  304. if (tool != null && tool instanceof DoubleActionBlockTool) {
  305. if (tool.canUse(player)) {
  306. ((DoubleActionBlockTool) tool).actSecondary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session, location);
  307. event.setCancelled(true);
  308. }
  309. }
  310. } else if (event.getType() == Interaction.OPEN) {
  311. if (player.getItemInHand() == getConfiguration().wandItem) {
  312. if (!session.isToolControlEnabled()) {
  313. return;
  314. }
  315. if (!actor.hasPermission("worldedit.selection.pos")) {
  316. return;
  317. }
  318. RegionSelector selector = session.getRegionSelector(player.getWorld());
  319. if (selector.selectSecondary(vector, ActorSelectorLimits.forActor(player))) {
  320. selector.explainSecondarySelection(actor, session, vector);
  321. }
  322. event.setCancelled(true);
  323. return;
  324. }
  325. Tool tool = session.getTool(player.getItemInHand());
  326. if (tool != null && tool instanceof BlockTool) {
  327. if (tool.canUse(player)) {
  328. ((BlockTool) tool).actPrimary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session, location);
  329. event.setCancelled(true);
  330. }
  331. }
  332. }
  333. }
  334. }
  335. @SuppressWarnings("deprecation")
  336. @Subscribe
  337. public void handlePlayerInput(PlayerInputEvent event) {
  338. // Create a proxy actor with a potentially different world for
  339. // making changes to the world
  340. Player player = createProxyActor(event.getPlayer());
  341. switch (event.getInputType()) {
  342. case PRIMARY: {
  343. if (player.getItemInHand() == getConfiguration().navigationWand) {
  344. if (getConfiguration().navigationWandMaxDistance <= 0) {
  345. return;
  346. }
  347. if (!player.hasPermission("worldedit.navigation.jumpto.tool")) {
  348. return;
  349. }
  350. WorldVector pos = player.getSolidBlockTrace(getConfiguration().navigationWandMaxDistance);
  351. if (pos != null) {
  352. player.findFreePosition(pos);
  353. } else {
  354. player.printError("No block in sight (or too far)!");
  355. }
  356. event.setCancelled(true);
  357. return;
  358. }
  359. LocalSession session = worldEdit.getSessionManager().get(player);
  360. Tool tool = session.getTool(player.getItemInHand());
  361. if (tool != null && tool instanceof DoubleActionTraceTool) {
  362. if (tool.canUse(player)) {
  363. ((DoubleActionTraceTool) tool).actSecondary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session);
  364. event.setCancelled(true);
  365. return;
  366. }
  367. }
  368. break;
  369. }
  370. case SECONDARY: {
  371. if (player.getItemInHand() == getConfiguration().navigationWand) {
  372. if (getConfiguration().navigationWandMaxDistance <= 0) {
  373. return;
  374. }
  375. if (!player.hasPermission("worldedit.navigation.thru.tool")) {
  376. return;
  377. }
  378. if (!player.passThroughForwardWall(40)) {
  379. player.printError("Nothing to pass through!");
  380. }
  381. event.setCancelled(true);
  382. return;
  383. }
  384. LocalSession session = worldEdit.getSessionManager().get(player);
  385. Tool tool = session.getTool(player.getItemInHand());
  386. if (tool != null && tool instanceof TraceTool) {
  387. if (tool.canUse(player)) {
  388. ((TraceTool) tool).actPrimary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session);
  389. event.setCancelled(true);
  390. return;
  391. }
  392. }
  393. break;
  394. }
  395. }
  396. }
  397. }