PageRenderTime 822ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 0ms

/src/PluginLoader.java

http://github.com/traitor/Minecraft-Server-Mod
Java | 771 lines | 450 code | 41 blank | 280 comment | 71 complexity | d72856258cf1a1241daa8268a2f77383 MD5 | raw file
  1. import java.io.File;
  2. import java.net.MalformedURLException;
  3. import java.net.URL;
  4. import java.net.URLClassLoader;
  5. import java.util.ArrayList;
  6. import java.util.HashMap;
  7. import java.util.Iterator;
  8. import java.util.List;
  9. import java.util.logging.Level;
  10. import java.util.logging.Logger;
  11. import net.minecraft.server.MinecraftServer;
  12. /**
  13. * PluginLoader.java - Used to load plugins, toggle them, etc.
  14. *
  15. * @author James
  16. */
  17. public class PluginLoader {
  18. /**
  19. * Hook - Used for adding a listener to listen on specific hooks
  20. */
  21. public enum Hook {
  22. /**
  23. * Calls onLoginChecks
  24. */
  25. LOGINCHECK,
  26. /**
  27. * Calls onLogin
  28. */
  29. LOGIN,
  30. /**
  31. * Calls onChat
  32. */
  33. CHAT,
  34. /**
  35. * Calls onCommand
  36. */
  37. COMMAND,
  38. /**
  39. * Calls onConsoleCommand
  40. */
  41. SERVERCOMMAND,
  42. /**
  43. * Calls onBan
  44. */
  45. BAN,
  46. /**
  47. * Calls onIpBan
  48. */
  49. IPBAN,
  50. /**
  51. * Calls onKick
  52. */
  53. KICK,
  54. /**
  55. * Calls onBlockCreate
  56. */
  57. BLOCK_CREATED,
  58. /**
  59. * Calls onBlockDestroy
  60. */
  61. BLOCK_DESTROYED,
  62. /**
  63. * Calls onDisconnect
  64. */
  65. DISCONNECT,
  66. /**
  67. * Calls onPlayerMove
  68. */
  69. PLAYER_MOVE,
  70. /**
  71. * Calls onArmSwing
  72. */
  73. ARM_SWING,
  74. /**
  75. * Calls onItemDrop
  76. */
  77. ITEM_DROP,
  78. /**
  79. * Calls onItemPickUp
  80. */
  81. ITEM_PICK_UP,
  82. /**
  83. * Calls onTeleport
  84. */
  85. TELEPORT,
  86. /**
  87. * Calls onBlockBreak
  88. */
  89. BLOCK_BROKEN,
  90. /**
  91. * Calls onIgnite
  92. */
  93. IGNITE,
  94. /**
  95. * Calls onFlow
  96. */
  97. FLOW,
  98. /**
  99. * Calls onExplode
  100. */
  101. EXPLODE,
  102. /**
  103. * Calls onMobSpawn
  104. */
  105. MOB_SPAWN,
  106. /**
  107. * Calls onDamage
  108. */
  109. DAMAGE,
  110. /**
  111. * Calls onHealthChange
  112. */
  113. HEALTH_CHANGE,
  114. /**
  115. * Calls onRedstoneChange
  116. */
  117. REDSTONE_CHANGE,
  118. /**
  119. * Calls onBlockPhysics
  120. */
  121. BLOCK_PHYSICS,
  122. /**
  123. * Calls onVehicleCreate
  124. */
  125. VEHICLE_CREATE,
  126. /**
  127. * Calls onVehicleUpdate
  128. */
  129. VEHICLE_UPDATE,
  130. /**
  131. * Calls onVehicleDamage
  132. */
  133. VEHICLE_DAMAGE,
  134. /**
  135. * Calls onVehicleCollision
  136. */
  137. VEHICLE_COLLISION,
  138. /**
  139. * Calls onVehicleDestroyed
  140. */
  141. VEHICLE_DESTROYED,
  142. /**
  143. * Calls onVehicleEntered
  144. */
  145. VEHICLE_ENTERED,
  146. /**
  147. * Calls onVehiclePositionChange
  148. */
  149. VEHICLE_POSITIONCHANGE,
  150. /**
  151. * Calls onItemUse
  152. */
  153. ITEM_USE,
  154. /**
  155. * Calls onBlockPlace
  156. */
  157. BLOCK_PLACE,
  158. /**
  159. * Calls onBlockRightClicked
  160. */
  161. BLOCK_RIGHTCLICKED,
  162. /**
  163. * Calls onLiquidDestroy
  164. */
  165. LIQUID_DESTROY,
  166. /**
  167. * Calls onAttack
  168. */
  169. ATTACK,
  170. /**
  171. * Calls onOpenInventory
  172. */
  173. OPEN_INVENTORY,
  174. /**
  175. * Calls onSignShow
  176. */
  177. SIGN_SHOW,
  178. /**
  179. * Calls onSignChange
  180. */
  181. SIGN_CHANGE,
  182. /**
  183. * Calls onLeafDecay
  184. */
  185. LEAF_DECAY,
  186. /**
  187. * Unused.
  188. */
  189. NUM_HOOKS
  190. }
  191. /**
  192. * HookResult - Used where returning a boolean isn't enough.
  193. */
  194. public enum HookResult {
  195. /**
  196. * Prevent the action
  197. */
  198. PREVENT_ACTION,
  199. /**
  200. * Allow the action
  201. */
  202. ALLOW_ACTION,
  203. /**
  204. * Do whatever it would normally do, continue processing
  205. */
  206. DEFAULT_ACTION
  207. }
  208. public enum DamageType {
  209. /**
  210. * Creeper explosion
  211. */
  212. CREEPER_EXPLOSION,
  213. /**
  214. * Damage dealt by another entity
  215. */
  216. ENTITY,
  217. /**
  218. * Damage caused by explosion
  219. */
  220. EXPLOSION,
  221. /**
  222. * Damage caused from falling (fall distance - 3.0)
  223. */
  224. FALL,
  225. /**
  226. * Damage caused by fire (1)
  227. */
  228. FIRE,
  229. /**
  230. * Low periodic damage caused by burning (1)
  231. */
  232. FIRE_TICK,
  233. /**
  234. * Damage caused from lava (4)
  235. */
  236. LAVA,
  237. /**
  238. * Damage caused from drowning (2)
  239. */
  240. WATER,
  241. /**
  242. * Damage caused by cactus (1)
  243. */
  244. CACTUS,
  245. /**
  246. * Damage caused by suffocating(1)
  247. */
  248. SUFFOCATION
  249. }
  250. private static final Logger log = Logger.getLogger("Minecraft");
  251. private static final Object lock = new Object();
  252. private List<Plugin> plugins = new ArrayList<Plugin>();
  253. private List<List<PluginRegisteredListener>> listeners = new ArrayList<List<PluginRegisteredListener>>();
  254. private HashMap<String, PluginInterface> customListeners = new HashMap<String, PluginInterface>();
  255. private Server server;
  256. private PropertiesFile properties;
  257. private volatile boolean loaded = false;
  258. /**
  259. * Creates a plugin loader
  260. *
  261. * @param server
  262. * server to use
  263. */
  264. public PluginLoader(MinecraftServer server) {
  265. properties = new PropertiesFile("server.properties");
  266. this.server = new Server(server);
  267. for (int h = 0; h < Hook.NUM_HOOKS.ordinal(); ++h)
  268. listeners.add(new ArrayList<PluginRegisteredListener>());
  269. }
  270. /**
  271. * Loads all plugins.
  272. */
  273. public void loadPlugins() {
  274. if (loaded)
  275. return;
  276. log.info("hMod: Loading plugins...");
  277. String[] classes = properties.getString("plugins", "").split(",");
  278. for (String sclass : classes) {
  279. if (sclass.equals(""))
  280. continue;
  281. loadPlugin(sclass.trim());
  282. }
  283. log.info("hMod: Loaded " + plugins.size() + " plugins.");
  284. loaded = true;
  285. }
  286. /**
  287. * Loads the specified plugin
  288. *
  289. * @param fileName
  290. * file name of plugin to load
  291. * @return if the operation was successful
  292. */
  293. public Boolean loadPlugin(String fileName) {
  294. if (getPlugin(fileName) != null)
  295. return false; // Already exists.
  296. return load(fileName);
  297. }
  298. /**
  299. * Reloads the specified plugin
  300. *
  301. * @param fileName
  302. * file name of plugin to reload
  303. * @return if the operation was successful
  304. */
  305. public Boolean reloadPlugin(String fileName) {
  306. /* Not sure exactly how much of this is necessary */
  307. Plugin toNull = getPlugin(fileName);
  308. if (toNull != null)
  309. if (toNull.isEnabled())
  310. toNull.disable();
  311. synchronized (lock) {
  312. plugins.remove(toNull);
  313. for (List<PluginRegisteredListener> regListeners : listeners) {
  314. Iterator<PluginRegisteredListener> iter = regListeners.iterator();
  315. while (iter.hasNext())
  316. if (iter.next().getPlugin() == toNull)
  317. iter.remove();
  318. }
  319. }
  320. toNull = null;
  321. return load(fileName);
  322. }
  323. private Boolean load(String fileName) {
  324. try {
  325. File file = new File("plugins/" + fileName + ".jar");
  326. if (!file.exists()) {
  327. log.log(Level.SEVERE, "Failed to find plugin file: plugins/" + fileName + ".jar. Please ensure the file exists");
  328. return false;
  329. }
  330. URLClassLoader child = null;
  331. try {
  332. child = new MyClassLoader(new URL[] { file.toURI().toURL() }, Thread.currentThread().getContextClassLoader());
  333. } catch (MalformedURLException ex) {
  334. log.log(Level.SEVERE, "Exception while loading class", ex);
  335. return false;
  336. }
  337. Class<?> c = child.loadClass(fileName);
  338. Plugin plugin = (Plugin) c.newInstance();
  339. plugin.setName(fileName);
  340. plugin.enable();
  341. synchronized (lock) {
  342. plugins.add(plugin);
  343. plugin.initialize();
  344. }
  345. } catch (Throwable ex) {
  346. log.log(Level.SEVERE, "Exception while loading plugin", ex);
  347. return false;
  348. }
  349. return true;
  350. }
  351. /**
  352. * Returns the specified plugin
  353. *
  354. * @param name
  355. * name of plugin
  356. * @return plugin
  357. */
  358. public Plugin getPlugin(String name) {
  359. synchronized (lock) {
  360. for (Plugin plugin : plugins)
  361. if (plugin.getName().equalsIgnoreCase(name))
  362. return plugin;
  363. }
  364. return null;
  365. }
  366. /**
  367. * Returns a string list of plugins
  368. *
  369. * @return String of plugins
  370. */
  371. public String getPluginList() {
  372. StringBuilder sb = new StringBuilder();
  373. synchronized (lock) {
  374. for (Plugin plugin : plugins) {
  375. sb.append(plugin.getName());
  376. sb.append(" ");
  377. sb.append(plugin.isEnabled() ? "(E)" : "(D)");
  378. sb.append(",");
  379. }
  380. }
  381. String str = sb.toString();
  382. if (str.length() > 1)
  383. return str.substring(0, str.length() - 1);
  384. else
  385. return "Empty";
  386. }
  387. /**
  388. * Enables the specified plugin (Or adds and enables it)
  389. *
  390. * @param name
  391. * name of plugin to enable
  392. * @return whether or not this plugin was enabled
  393. */
  394. public boolean enablePlugin(String name) {
  395. Plugin plugin = getPlugin(name);
  396. if (plugin != null) {
  397. if (!plugin.isEnabled()) {
  398. plugin.toggleEnabled();
  399. plugin.enable();
  400. }
  401. } else { // New plugin, perhaps?
  402. File file = new File("plugins/" + name + ".jar");
  403. if (file.exists())
  404. return loadPlugin(name);
  405. else
  406. return false;
  407. }
  408. return true;
  409. }
  410. /**
  411. * Disables specified plugin
  412. *
  413. * @param name
  414. * name of the plugin to disable
  415. */
  416. public void disablePlugin(String name) {
  417. Plugin plugin = getPlugin(name);
  418. if (plugin != null)
  419. if (plugin.isEnabled()) {
  420. plugin.toggleEnabled();
  421. plugin.disable();
  422. }
  423. }
  424. /**
  425. * Returns the server
  426. *
  427. * @return server
  428. */
  429. public Server getServer() {
  430. return server;
  431. }
  432. /**
  433. * Calls a plugin hook.
  434. *
  435. * @param h
  436. * Hook to call
  437. * @param parameters
  438. * Parameters of call
  439. * @return Object returned by call
  440. */
  441. @SuppressWarnings("deprecation")
  442. public Object callHook(Hook h, Object... parameters) {
  443. Object toRet;
  444. switch (h) {
  445. case REDSTONE_CHANGE:
  446. toRet = parameters[2];
  447. break;
  448. case LIQUID_DESTROY:
  449. toRet = HookResult.DEFAULT_ACTION;
  450. break;
  451. default:
  452. toRet = false;
  453. break;
  454. }
  455. if (!loaded)
  456. return toRet;
  457. synchronized (lock) {
  458. PluginListener listener = null;
  459. try {
  460. List<PluginRegisteredListener> registeredListeners = listeners.get(h.ordinal());
  461. for (PluginRegisteredListener regListener : registeredListeners) {
  462. if (!regListener.getPlugin().isEnabled())
  463. continue;
  464. listener = regListener.getListener();
  465. try {
  466. switch (h) {
  467. case LOGINCHECK:
  468. String result = listener.onLoginChecks((String) parameters[0]);
  469. if (result != null)
  470. toRet = result;
  471. break;
  472. case LOGIN:
  473. listener.onLogin((Player) parameters[0]);
  474. break;
  475. case DISCONNECT:
  476. listener.onDisconnect((Player) parameters[0]);
  477. break;
  478. case CHAT:
  479. if (listener.onChat((Player) parameters[0], (String) parameters[1]))
  480. toRet = true;
  481. break;
  482. case COMMAND:
  483. if (listener.onCommand((Player) parameters[0], (String[]) parameters[1]))
  484. toRet = true;
  485. break;
  486. case SERVERCOMMAND:
  487. if (listener.onConsoleCommand((String[]) parameters[0]))
  488. toRet = true;
  489. break;
  490. case BAN:
  491. listener.onBan((Player) parameters[0], (Player) parameters[1], (String) parameters[2]);
  492. break;
  493. case IPBAN:
  494. listener.onIpBan((Player) parameters[0], (Player) parameters[1], (String) parameters[2]);
  495. break;
  496. case KICK:
  497. listener.onKick((Player) parameters[0], (Player) parameters[1], (String) parameters[2]);
  498. break;
  499. case BLOCK_CREATED:
  500. if (listener.onBlockCreate((Player) parameters[0], (Block) parameters[1], (Block) parameters[2], (Integer) parameters[3]))
  501. toRet = true;
  502. break;
  503. case BLOCK_DESTROYED:
  504. if (listener.onBlockDestroy((Player) parameters[0], (Block) parameters[1]))
  505. toRet = true;
  506. break;
  507. case PLAYER_MOVE:
  508. listener.onPlayerMove((Player) parameters[0], (Location) parameters[1], (Location) parameters[2]);
  509. break;
  510. case ARM_SWING:
  511. listener.onArmSwing((Player) parameters[0]);
  512. break;
  513. case ITEM_DROP:
  514. if (listener.onItemDrop((Player) parameters[0], (Item) parameters[1]))
  515. toRet = true;
  516. break;
  517. case ITEM_PICK_UP:
  518. if (listener.onItemPickUp((Player) parameters[0], (Item) parameters[1]))
  519. toRet = true;
  520. break;
  521. case TELEPORT:
  522. if (listener.onTeleport((Player) parameters[0], (Location) parameters[1], (Location) parameters[2]))
  523. toRet = true;
  524. break;
  525. case BLOCK_BROKEN:
  526. if (listener.onBlockBreak((Player) parameters[0], (Block) parameters[1]))
  527. toRet = true;
  528. break;
  529. case FLOW:
  530. if (listener.onFlow((Block) parameters[0], (Block) parameters[1]))
  531. toRet = true;
  532. break;
  533. case IGNITE:
  534. if (listener.onIgnite((Block) parameters[0], (parameters[1] == null ? null : (Player) parameters[1])))
  535. toRet = true;
  536. break;
  537. case EXPLODE:
  538. if (listener.onExplode((Block) parameters[0]))
  539. toRet = true;
  540. break;
  541. case MOB_SPAWN:
  542. if (listener.onMobSpawn((Mob) parameters[0]))
  543. toRet = true;
  544. break;
  545. case DAMAGE:
  546. if (listener.onDamage((DamageType) parameters[0], (BaseEntity) parameters[1], (BaseEntity) parameters[2], (Integer) parameters[3]))
  547. toRet = true;
  548. break;
  549. case HEALTH_CHANGE:
  550. if (listener.onHealthChange((Player) parameters[0], (Integer) parameters[1], (Integer) parameters[2]))
  551. toRet = true;
  552. break;
  553. case REDSTONE_CHANGE:
  554. toRet = listener.onRedstoneChange((Block) parameters[0], (Integer) parameters[1], (Integer) toRet);
  555. break;
  556. case BLOCK_PHYSICS:
  557. if (listener.onBlockPhysics((Block) parameters[0], (Boolean) parameters[1]))
  558. toRet = true;
  559. break;
  560. case VEHICLE_CREATE:
  561. listener.onVehicleCreate((BaseVehicle) parameters[0]);
  562. break;
  563. case VEHICLE_UPDATE:
  564. listener.onVehicleUpdate((BaseVehicle) parameters[0]);
  565. break;
  566. case VEHICLE_DAMAGE:
  567. if (listener.onVehicleDamage((BaseVehicle) parameters[0], (BaseEntity) parameters[1], (Integer) parameters[2]))
  568. toRet = true;
  569. break;
  570. case VEHICLE_COLLISION:
  571. if (listener.onVehicleCollision((BaseVehicle) parameters[0], (BaseEntity) parameters[1]))
  572. toRet = true;
  573. break;
  574. case VEHICLE_DESTROYED:
  575. listener.onVehicleDestroyed((BaseVehicle) parameters[0]);
  576. break;
  577. case VEHICLE_ENTERED:
  578. listener.onVehicleEnter((BaseVehicle) parameters[0], (HumanEntity) parameters[1]);
  579. break;
  580. case VEHICLE_POSITIONCHANGE:
  581. listener.onVehiclePositionChange((BaseVehicle) parameters[0], (Integer) parameters[1], (Integer) parameters[2], (Integer) parameters[3]);
  582. break;
  583. case ITEM_USE:
  584. if (listener.onItemUse((Player) parameters[0], (Block) parameters[1], (Block) parameters[2], (Item) parameters[3]))
  585. toRet = true;
  586. break;
  587. case BLOCK_RIGHTCLICKED:
  588. listener.onBlockRightClicked((Player) parameters[0], (Block) parameters[1], (Item) parameters[2]);
  589. break;
  590. case BLOCK_PLACE:
  591. if (listener.onBlockPlace((Player) parameters[0], (Block) parameters[1], (Block) parameters[2], (Item) parameters[3]))
  592. toRet = true;
  593. break;
  594. case LIQUID_DESTROY:
  595. HookResult ret = listener.onLiquidDestroy((HookResult) toRet, (Integer) parameters[0], (Block) parameters[1]);
  596. if (ret != HookResult.DEFAULT_ACTION && (HookResult) toRet == HookResult.DEFAULT_ACTION)
  597. toRet = ret;
  598. break;
  599. case ATTACK:
  600. if (listener.onAttack((LivingEntity) parameters[0], (LivingEntity) parameters[1], (Integer) parameters[2]))
  601. toRet = true;
  602. break;
  603. case OPEN_INVENTORY:
  604. if (listener.onOpenInventory((Player) parameters[0], (Inventory) parameters[1]))
  605. toRet = true;
  606. break;
  607. case SIGN_SHOW:
  608. listener.onSignShow((Player) parameters[0], (Sign) parameters[1]);
  609. break;
  610. case SIGN_CHANGE:
  611. if (listener.onSignChange((Player) parameters[0], (Sign) parameters[1]))
  612. toRet = true;
  613. break;
  614. case LEAF_DECAY:
  615. if (listener.onLeafDecay((Block) parameters[0]))
  616. toRet = true;
  617. break;
  618. }
  619. } catch (UnsupportedOperationException ex) {
  620. }
  621. }
  622. } catch (Exception ex) {
  623. String listenerString = listener == null ? "null(unknown listener)" : listener.getClass().toString();
  624. log.log(Level.SEVERE, "Exception while calling plugin function in '" + listenerString + "' while calling hook: '" + h.toString() + "'.", ex);
  625. } catch (Throwable ex) { // The 'exception' thrown is so severe it's
  626. // not even an exception!
  627. log.log(Level.SEVERE, "Throwable while calling plugin (Outdated?)", ex);
  628. }
  629. }
  630. return toRet;
  631. }
  632. /**
  633. * Calls a custom hook
  634. *
  635. * @param name
  636. * name of hook
  637. * @param parameters
  638. * parameters for the hook
  639. * @return object returned by call
  640. */
  641. public Object callCustomHook(String name, Object[] parameters) {
  642. Object toRet = false;
  643. synchronized (lock) {
  644. try {
  645. PluginInterface listener = customListeners.get(name);
  646. if (listener == null) {
  647. log.log(Level.SEVERE, "Cannot find custom hook: " + name);
  648. return false;
  649. }
  650. String msg = listener.checkParameters(parameters);
  651. if (msg != null) {
  652. log.log(Level.SEVERE, msg);
  653. return false;
  654. }
  655. toRet = listener.run(parameters);
  656. } catch (Exception ex) {
  657. log.log(Level.SEVERE, "Exception while calling custom plugin function", ex);
  658. }
  659. }
  660. return toRet;
  661. }
  662. /**
  663. * Calls a plugin hook.
  664. *
  665. * @param hook
  666. * The hook to call on
  667. * @param listener
  668. * The listener to use when calling
  669. * @param plugin
  670. * The plugin of this listener
  671. * @param priorityEnum
  672. * The priority of this listener
  673. * @return PluginRegisteredListener
  674. */
  675. public PluginRegisteredListener addListener(Hook hook, PluginListener listener, Plugin plugin, PluginListener.Priority priorityEnum) {
  676. int priority = priorityEnum.ordinal();
  677. PluginRegisteredListener reg = new PluginRegisteredListener(hook, listener, plugin, priority);
  678. synchronized (lock) {
  679. List<PluginRegisteredListener> regListeners = listeners.get(hook.ordinal());
  680. int pos = 0;
  681. for (PluginRegisteredListener other : regListeners) {
  682. if (other.getPriority() < priority)
  683. break;
  684. ++pos;
  685. }
  686. regListeners.add(pos, reg);
  687. }
  688. return reg;
  689. }
  690. /**
  691. * Adds a custom listener
  692. *
  693. * @param listener
  694. * listener to add
  695. */
  696. public void addCustomListener(PluginInterface listener) {
  697. synchronized (lock) {
  698. if (customListeners.get(listener.getName()) != null)
  699. log.log(Level.SEVERE, "Replacing existing listener: " + listener.getName());
  700. customListeners.put(listener.getName(), listener);
  701. log.info("Registered custom hook: " + listener.getName());
  702. }
  703. }
  704. /**
  705. * Removes the specified listener from the list of listeners
  706. *
  707. * @param reg
  708. * listener to remove
  709. */
  710. public void removeListener(PluginRegisteredListener reg) {
  711. List<PluginRegisteredListener> regListeners = listeners.get(reg.getHook().ordinal());
  712. synchronized (lock) {
  713. regListeners.remove(reg);
  714. }
  715. }
  716. /**
  717. * Removes a custom listener
  718. *
  719. * @param name
  720. * name of listener
  721. */
  722. public void removeCustomListener(String name) {
  723. synchronized (lock) {
  724. customListeners.remove(name);
  725. }
  726. }
  727. public boolean isLoaded() {
  728. return loaded;
  729. }
  730. }