Add Things API.

With this commit, the Things API is introduced to the code base, but it is not yet used. It introduces the building blocks for an extensible architecture that supports custom parsers and custom "things". This should allow other developers simple, yet powerful hooks into the way MobArena handles class "equipment" and rewards. The basic skeleton includes parsers for ItemStacks and economy money, so it should be interchangeable with the current inner workings of the plugin.

The commit also adds an overload to the ItemParser that allows for a the method to fail silently. This is necessary to avoid false negatives in the log in case the ItemStackThingParser fails but a different parser succeeds.
This commit is contained in:
Andreas Troelsen 2017-06-27 23:03:30 +02:00
parent 8d61810dae
commit aecdac7b39
9 changed files with 274 additions and 1 deletions

View File

@ -5,6 +5,7 @@ import com.garbagemule.MobArena.framework.Arena;
import com.garbagemule.MobArena.framework.ArenaMaster;
import com.garbagemule.MobArena.listeners.MAGlobalListener;
import com.garbagemule.MobArena.listeners.MagicSpellsListener;
import com.garbagemule.MobArena.things.ThingManager;
import com.garbagemule.MobArena.util.VersionChecker;
import com.garbagemule.MobArena.util.config.ConfigUtils;
import com.garbagemule.MobArena.util.inventory.InventoryManager;
@ -58,6 +59,12 @@ public class MobArena extends JavaPlugin
public static Random random = new Random();
private Messenger messenger;
private ThingManager thingman;
@Override
public void onLoad() {
thingman = new ThingManager(this);
}
public void onEnable() {
// Initialize config-file
@ -316,6 +323,14 @@ public class MobArena extends JavaPlugin
return false;
}
public boolean giveMoney(Player p, double amount) {
if (economy != null) {
EconomyResponse result = economy.depositPlayer(p, amount);
return (result.type == ResponseType.SUCCESS);
}
return false;
}
public boolean takeMoney(Player p, ItemStack item) {
return takeMoney(p, getAmount(item));
}
@ -353,4 +368,8 @@ public class MobArena extends JavaPlugin
public Messenger getGlobalMessenger() {
return messenger;
}
public ThingManager getThingManager() {
return thingman;
}
}

View File

@ -0,0 +1,40 @@
package com.garbagemule.MobArena.things;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
public class ItemStackThing implements Thing {
private ItemStack stack;
public ItemStackThing(ItemStack stack) {
this.stack = stack;
}
@Override
public boolean giveTo(Player player) {
return player.getInventory().addItem(stack).isEmpty();
}
@Override
public boolean takeFrom(Player player) {
return player.getInventory().removeItem(stack).isEmpty();
}
@Override
public boolean heldBy(Player player) {
return player.getInventory().containsAtLeast(stack, stack.getAmount());
}
@Override
public String toString() {
String item = stack.getType()
.name()
.replace("_", " ")
.toLowerCase();
if (stack.getAmount() > 1) {
return stack.getAmount() + "x " + item;
}
return item;
}
}

View File

@ -0,0 +1,15 @@
package com.garbagemule.MobArena.things;
import com.garbagemule.MobArena.util.ItemParser;
import org.bukkit.inventory.ItemStack;
class ItemStackThingParser implements ThingParser {
@Override
public ItemStackThing parse(String s) {
ItemStack stack = ItemParser.parseItem(s, false);
if (stack == null) {
return null;
}
return new ItemStackThing(stack);
}
}

View File

@ -0,0 +1,34 @@
package com.garbagemule.MobArena.things;
import com.garbagemule.MobArena.MobArena;
import org.bukkit.entity.Player;
public class MoneyThing implements Thing {
private MobArena plugin;
private double amount;
public MoneyThing(MobArena plugin, double amount) {
this.plugin = plugin;
this.amount = amount;
}
@Override
public boolean giveTo(Player player) {
return plugin.giveMoney(player, amount);
}
@Override
public boolean takeFrom(Player player) {
return plugin.takeMoney(player, amount);
}
@Override
public boolean heldBy(Player player) {
return plugin.hasEnough(player, amount);
}
@Override
public String toString() {
return plugin.economyFormat(amount);
}
}

View File

@ -0,0 +1,48 @@
package com.garbagemule.MobArena.things;
import com.garbagemule.MobArena.MobArena;
class MoneyThingParser implements ThingParser {
private static final String PREFIX_LONG = "money:";
private static final String PREFIX_SHORT = "$";
private MobArena plugin;
MoneyThingParser(MobArena plugin) {
this.plugin = plugin;
}
@Override
public MoneyThing parse(String s) {
String money = trimPrefix(s);
if (money == null) {
return null;
}
Double value = valueOf(money);
if (value == null) {
return null;
}
return new MoneyThing(plugin, value);
}
private String trimPrefix(String s) {
if (s.startsWith(PREFIX_SHORT)) {
return s.substring(PREFIX_SHORT.length());
}
if (s.startsWith(PREFIX_LONG)) {
return s.substring(PREFIX_LONG.length());
}
return null;
}
private Double valueOf(String money) {
try {
return Double.parseDouble(money);
} catch (NumberFormatException e) {
plugin.getLogger().warning("Invalid economy value: " + money);
return null;
}
}
}

View File

@ -0,0 +1,40 @@
package com.garbagemule.MobArena.things;
import org.bukkit.entity.Player;
/**
* A thing is something that can be given to a player, taken from a player,
* or something that a player can have or be in possession of. Things may have
* any number of these three properties, which means that just because a thing
* can be given to a player, it doesn't mean that it can also be taken away,
* or that the player is in possession of it.
* <p>
* The interface exposes three methods that are all optional operations. An
* operation returns false if it fails or if it isn't applicable to the given
* thing (which is the same as failing).
*/
public interface Thing {
/**
* Give this thing to the given player.
*
* @param player a player, non-null
* @return true, if this thing was given to the player, false otherwise
*/
boolean giveTo(Player player);
/**
* Take this thing from the given player.
*
* @param player a player, non-null
* @return true, if this thing was taken from the player, false otherwise
*/
boolean takeFrom(Player player);
/**
* Check if the given player has this thing.
*
* @param player a player, non-null
* @return true, if the player has this thing, false otherwise
*/
boolean heldBy(Player player);
}

View File

@ -0,0 +1,55 @@
package com.garbagemule.MobArena.things;
import com.garbagemule.MobArena.MobArena;
import java.util.ArrayList;
import java.util.List;
public class ThingManager implements ThingParser {
private List<ThingParser> parsers;
public ThingManager(MobArena plugin) {
parsers = new ArrayList<>();
parsers.add(new MoneyThingParser(plugin));
parsers.add(new ItemStackThingParser());
}
/**
* Register a new thing parser in the manager.
*
* @param parser a thing parser, non-null
* @param beforeCoreParsers if true, the parser will be invoked before the
* core parsers, effectively allowing it to override the defaults. Only do
* this if you know what you're doing.
*/
public void register(ThingParser parser, boolean beforeCoreParsers) {
if (beforeCoreParsers) {
parsers.add(0, parser);
} else {
parsers.add(parser);
}
}
/**
* Register a new thing parser in the manager.
* <p>
* This is a convenience method for {@link #register(ThingParser, boolean)}
* that registers the parser <i>after</i> the core parsers.
*
* @param parser a thing parser, non-null
*/
public void register(ThingParser parser) {
register(parser, false);
}
@Override
public Thing parse(String s) {
for (ThingParser parser : parsers) {
Thing thing = parser.parse(s);
if (thing != null) {
return thing;
}
}
return null;
}
}

View File

@ -0,0 +1,16 @@
package com.garbagemule.MobArena.things;
/**
* A thing parser takes a string as input and returns either an instance of
* {@link Thing} or null.
*/
public interface ThingParser {
/**
* Parse the given string, returning a {@link Thing} instance on success,
* otherwise null.
*
* @param s a string to parse
* @return an instance of {@link Thing}, or null
*/
Thing parse(String s);
}

View File

@ -143,6 +143,10 @@ public class ItemParser
}
public static ItemStack parseItem(String item) {
return parseItem(item, true);
}
public static ItemStack parseItem(String item, boolean logFailure) {
if (item == null || item.equals(""))
return null;
@ -164,7 +168,9 @@ public class ItemParser
break;
}
if (result == null || result.getTypeId() == 0) {
Bukkit.getLogger().warning("[MobArena] Failed to parse item: " + item);
if (logFailure) {
Bukkit.getLogger().warning("[MobArena] Failed to parse item: " + item);
}
return null;
}