API

ScrollingMenuSign exposes an API to allow other plugins to query and manipulate its menus.

Here are some examples of using the API:

Building with Maven

You can use the following repository and dependency information:

<repository>
  <id>DH Dropbox</id>
  <url>http://dl.dropboxusercontent.com/u/12467600/maven-repo</url>
</repository>

<dependency>
  <groupId>me.desht</groupId>
  <artifactId>scrollingmenusign</artifactId>
  <version>RELEASE</version>
  <scope>provided</scope>
 </dependency>

Set up the handler object

High level operations like menu creation etc. are handled via the SMSHandler interface:

import me.desht.scrollingmenusign.SMSHandler;
import me.desht.scrollingmenusign.SMSMenu;
import me.desht.scrollingmenusign.SMSException;
import me.desht.scrollingmenusign.ScrollingMenuSign;

SMSHandler smsHandler;

// you would call something like this from your onEnable() method
static void setupSMS() {
  if (smsHandler == null) {
    Plugin p = Bukkit.getServer().getPluginManager().getPlugin("ScrollingMenuSign");
    if (p != null && p instanceof ScrollingMenuSign) {
      ScrollingMenuSign sms = (ScrollingMenuSign) p;
      smsHandler = sms.getHandler();
      log("ScrollingMenuSign integration is enabled");
    } else {
      log("ScrollingMenuSign integration is not enabled");
    }
  }
}

Create a new menu

Managing individual menus is done with the SMSMenu class...

SMSMenu menu = smsHandler.createMenu("mymenu", "&1My Title", "owner-name");
if (menu != null) {
  menu.addItem("Day", "/time day", "It's day time!");
  menu.addItem("Night", "/time night", "It's night time!");
  menu.addItem("Compass", "/compass", "");
} else {
  // should never get here
}

Delete a menu

try {
  smsHandler.deleteMenu("mymenu");
} catch (SMSException e) {
  // e.getMessage() contains an error message
}

Check if a menu exists

if (smsHandler.checkMenu("mymenu")) {
  // ....
}

Retrieve a menu by name

try {
  SMSMenu menu = smsHandler.getMenu("mymenu");
  // ...
} catch (SMSException e) {
  // e.getMessage() contains an error message - probably no such menu
}

Iterate through all known menus

for (SMSMenu menu : smsHandler.listMenus()) {
  // ...
}

Get the name of the menu which owns the sign the player is looking at

String menuName = smsHandler.getTargetedMenuSign(player, false);
if (menuName != null) {
  // ...
}

Creating and Using Views

To interact with menu views (sign, map, spout, redstone), you will use SMSView or one of its subclasses. Sign/multisign, map & spout views are all scrollable views, and inherit from SMSScrollableView (which inherits from SMSView). (Sign/Multisign views actually inherit from SMSGlobalScrollableView which in turn inherits from SMSScrollableView. Globally scrollable views don't have a per-player scroll position). Redstone views, not having any visual appearance, are not scrollable and inherit from SMSView directly. Inventory views do have a visual appearance, but are not scrollable, so also inherit from SMSView directly. Spout and inventory views both implement the SMSPoppableView interface, which defines methods for popping view windows up and down for players, and checking if a view has a currently-popped up window for a player.

Note that all views need to have an owner - this should either be a valid player name (views created by the SMS sync command use the name of the player who issued the command), or ScrollingMenuSign.CONSOLE_OWNER.

// get the view manager object
ViewManager mgr = ScrollingMenuSign.getInstance().getViewManager();

// create a sign view at <loc> on the given menu (exception thrown if no sign there)
Location loc = new Location(world, x, y, z);
SMSSignView view = mgr.addSignToMenu(menu, loc, player.getName());

// create a map view with the map <item> and give it a custom font
short mapID = item.getDurability();
SMSMapView view = mgr.addMapToMenu(menu, mapID, player.getName());
view.setAttribute(SMSMapView.FONT, "lucidaconsole");
view.setAttribute(SMSMapView.FONTSIZE, "9");

// create a Spout view and set its hotkey to "p"
SMSSpoutView view = mgr.addSpoutViewToMenu(menu, player.getName());
view.setAttribute(SMSSpoutView.SPOUTKEYS, "p");

// create an Inventory view
SMSInventoryView view = mgr.addInventoryViewToMenu(menu, player.getName());
// other attributes are AUTO_POPDOWN (boolean) and SPACING (integer)
view.setAttribute(SMSInventoryView.WIDTH, "5");
// to show/hide the inventory view via code:
view.toggleGUI(player);

// scroll the view at <loc> up or down
// right now, the only kind of scrollable view which has a location in the
// world is a sign/multisign view, but we'll keep the code generic...
SMSView view = mgr.getViewForLocation(loc);
if (view != null && view instanceof SMSScrollableView) {
  SMSScrollableView sView = (SMSScrollableView) view;
  sView.scrollUp();
  sView.scrollDown();
}

// execute the player's currently selected item via SMSUserAction
// permission & view ownership checks for the player will be carried out
// SMSException will be thrown if there was a problem
SMSUserAction.EXECUTE.execute(player, view);

Some other useful operations

Individual menu items are represented by objects of the SMSMenuItem class.

SMSMenu menu = smsHandler.getMenu("mymenu");

// add an item
menu.addItem("Day", "/time day", "It's day time");

// remove an item
menu.removeItem(1);     // by numeric index
menu.removeItem("Day");   // by label

// sort the menu
menu.sortItems();
// set the menu to autosort
menu.setAutosort(true);

// get the specified item
SMSMenuItem item = menu.getItem(1);   // by numeric index
SMSMenuItem item = menu.getItem("day"); // by label

// executing the item directly
// permission & view ownership is bypassed if you do this
// player can be null, in which case the command will be run as the console
item.execute(player);

/*
 * A repaint is always needed after any operation which changes the
 * menu's contents - repaints are not done automatically.  This forces
 * all the menu's views to update their appearance.
 */
menu.notifyObservers();

Hooking into the Command Parser

You can use SMS's command parser from other plugins too:

  setupSMS();

  // ...

  ParsedCommand cmd = smsHandler.runCommand(player, "$E,50 /command arg1 arg2 ..");
  if (cmd.getStatus() != ReturnStatus.CMD_OK) {
    log.warn("Command failed: " + cmd.getLastError());
  }

Checking for maps that SMS is using

This may be useful if your plugin also uses maps and you want to co-exist peacefully with SMS:

ViewManager mgr = ScrollingMenuSign.getInstance().getViewManager();
ItemStack item = player.getItemInHand();
if (item.getType == Material.MAP && mgr.checkForMapId(item.getDurability())) {
  // this map is being used by ScrollingMenuSign
}

Real-world Examples

The ChessCraft plugin (v0.4+) uses the ScrollingMenuSign API to create menus of the boards and games in the world. Players could use these menus to quickly see what boards are available, what games are in progress, etc. See in particular https://github.com/desht/ChessCraft/blob/master/src/main/java/me/desht/chesscraft/SMSIntegration.java

Heroes (v1.4.4+) uses ScrollingMenuSign to create class-specific spell scrolls using SMSMapView.


Comments

Posts Quoted:
Reply
Clear All Quotes