use of eelfloat.replcraft.exceptions.ApiError in project replcraft by LeeFlemingRepl.
the class WebsocketServer method onMessage.
private void onMessage(WsMessageContext ctx) {
String nonce = null;
try {
JSONObject request = new JSONObject(ctx.message());
nonce = request.getString("nonce");
JSONObject response = new JSONObject();
response.put("ok", true);
response.put("nonce", nonce);
Client client = clients.get(ctx);
String action = request.getString("action");
WebsocketActionHandler handler = this.apis.get(action);
if (handler == null)
throw new ApiError("bad request", "Unknown action");
if (handler.authenticated() && client.getStructure() == null) {
String error = client.isInvalidated() ? "Structure was invalidated" : "Connection isn't authenticated yet";
throw new ApiError("unauthenticated", error);
}
if (client.getStructure() != null && !client.getStructure().material.apis.contains(action)) {
throw new ApiError("bad request", "This structure type doesn't support this API call.");
}
String permission = handler.permission();
if (permission != null) {
OfflinePlayer player = client.getStructure().getPlayer();
World world = client.getStructure().getWorld();
if (!ReplCraft.plugin.permissionProvider.hasPermission(player, world, permission))
throw new ApiError("bad request", "You lack the permission to make this API call.");
}
// --- Threading boundary! ---
// Any code inside runTask() is run on the main server thread.
// Any code above this should be non-mutative, functional-only code that should never touch the world.
String finalNonce = nonce;
Bukkit.getScheduler().runTask(ReplCraft.plugin, () -> {
try {
if (client.getStructure() != null) {
double fuelCost = handler.cost().toDouble() * client.getStructure().material.fuelMultiplier;
boolean freeFuel = (client.getStructure() != null && ReplCraft.plugin.world_guard && ApiUtil.checkFlagOnStructure(client.getStructure(), ReplCraft.plugin.worldGuard.replcraft_infinite_fuel));
if (!freeFuel && !client.useFuel(fuelCost)) {
String message = String.format("out of fuel (cost: %s). available strategies: provide %s of %s.", fuelCost, ReplCraft.plugin.consume_from_all ? "ALL" : "ANY", client.getFuelSources());
throw new ApiError("out of fuel", message);
}
}
handler.execute(client, ctx, request, response);
ctx.send(response.toString());
} catch (Exception ex) {
ctx.send(toClientError(ex, finalNonce).toString());
}
});
} catch (Exception ex) {
ctx.send(toClientError(ex, nonce).toString());
}
}
use of eelfloat.replcraft.exceptions.ApiError in project replcraft by LeeFlemingRepl.
the class WebsocketServer method toClientError.
private JSONObject toClientError(Exception ex, String nonce) {
JSONObject json = new JSONObject();
json.put("ok", false);
json.put("nonce", nonce);
if (ex instanceof JSONException) {
json.put("error", "bad request");
json.put("message", ex.getMessage());
} else if (ex instanceof InvalidStructure) {
json.put("error", "invalid structure");
json.put("message", ex.getMessage());
} else if (ex instanceof ApiError) {
json.put("error", ((ApiError) ex).type);
json.put("message", ((ApiError) ex).message);
} else {
json.put("error", "internal error");
json.put("message", "an internal server error occurred (this is a bug)");
ex.printStackTrace();
}
return json;
}
use of eelfloat.replcraft.exceptions.ApiError in project replcraft by LeeFlemingRepl.
the class Craft method execute.
@Override
public void execute(Client client, WsMessageContext ctx, JSONObject request, JSONObject response) throws ApiError {
JSONArray ingredients = request.getJSONArray("ingredients");
Inventory output = ApiUtil.getContainer(ApiUtil.getBlock(client, request), "output container");
ApiUtil.checkProtectionPlugins(client.getStructure().minecraft_uuid, output.getLocation());
class CraftingHelper {
final ItemStack item;
final Location location;
final int index;
/**
* The number of items that have been consumed from this helper
*/
int timesUsed = 0;
/**
* The number of times this helper was included in the client's provided recipe
*/
int timesReferenced = 0;
CraftingHelper(ItemStack item, Location location, int index) {
this.item = item;
this.location = location;
this.index = index;
}
}
// A list of crafting helpers. Crafting helpers pointing at the same location are duplicated
// in the list. Some slots are null to account for spaces.
ArrayList<CraftingHelper> items = new ArrayList<>();
for (int i = 0; i < ingredients.length(); i++) {
if (ingredients.isNull(i)) {
items.add(null);
continue;
}
JSONObject reference = ingredients.getJSONObject(i);
Block block = ApiUtil.getBlock(client, reference);
int index = reference.getInt("index");
ItemStack item = ApiUtil.getItem(block, String.format("ingredient %d block", i), index);
Location location = block.getLocation();
ApiUtil.checkProtectionPlugins(client.getStructure().minecraft_uuid, location);
CraftingHelper new_or_existing = items.stream().filter(helper -> helper != null && helper.location.equals(location) && helper.index == index).findFirst().orElseGet(() -> new CraftingHelper(item, location, index));
new_or_existing.timesReferenced += 1;
items.add(new_or_existing);
}
boolean crafted = false;
Iterator<Recipe> iter = Bukkit.recipeIterator();
recipes: while (!crafted && iter.hasNext()) {
Recipe next = iter.next();
if (next instanceof ShapedRecipe) {
Map<Character, ItemStack> ingredientMap = ((ShapedRecipe) next).getIngredientMap();
String[] rows = ((ShapedRecipe) next).getShape();
for (int row = 0; row < rows.length; row++) {
for (int col = 0; col < rows[row].length(); col++) {
ItemStack stack = ingredientMap.get(rows[row].charAt(col));
int i = row * rows.length + col;
if (i >= items.size())
continue recipes;
CraftingHelper ingredient = items.get(i);
if (stack == null && ingredient != null) {
// Reset used items and try next recipe
for (CraftingHelper item : items) if (item != null)
item.timesUsed = 0;
continue recipes;
}
if (stack == null) {
// No item to check here and no item provided, move on to next slot
continue;
}
if (ingredient == null || stack.getType() != ingredient.item.getType()) {
// Reset used items and try next recipe
for (CraftingHelper item : items) if (item != null)
item.timesUsed = 0;
continue recipes;
}
ingredient.timesUsed += 1;
}
}
} else if (next instanceof ShapelessRecipe) {
List<ItemStack> recipe_ingredients = ((ShapelessRecipe) next).getIngredientList();
for (ItemStack required_item : recipe_ingredients) {
boolean matched = items.stream().anyMatch(helper -> {
if (helper == null)
return false;
if (helper.item.getType() != required_item.getType())
return false;
if (helper.timesUsed >= helper.item.getAmount())
return false;
helper.timesUsed++;
return true;
});
if (!matched) {
for (CraftingHelper item : items) if (item != null)
item.timesUsed = 0;
continue recipes;
}
}
crafted = true;
} else {
continue recipes;
}
// Ensure all items have actually been used as many times as they claim
if (!items.stream().allMatch(helper -> helper == null || helper.timesUsed == helper.timesReferenced)) {
for (CraftingHelper item : items) if (item != null)
item.timesUsed = 0;
continue recipes;
}
// Ensure no item underflowed
for (CraftingHelper ingredient : items) {
if (ingredient == null)
continue;
if (ingredient.timesUsed > ingredient.item.getAmount()) {
throw new ApiError("invalid operation", String.format("attempted to use more %s than available", ingredient.item.getType()));
}
}
// Ensure there's somewhere to put the resulting item
if (!output.addItem(next.getResult()).isEmpty()) {
throw new ApiError("invalid operation", "no space to store result");
}
if (ReplCraft.plugin.core_protect) {
String player = client.getStructure().getPlayer().getName();
ReplCraft.plugin.coreProtect.logContainerTransaction(player + " [API]", output.getLocation());
}
for (CraftingHelper ingredient : items) {
if (ingredient == null)
continue;
ingredient.item.setAmount(ingredient.item.getAmount() - ingredient.timesUsed);
if (ReplCraft.plugin.core_protect) {
String player = client.getStructure().getPlayer().getName();
ReplCraft.plugin.coreProtect.logContainerTransaction(player + " [API]", ingredient.location);
}
}
crafted = true;
}
if (!crafted)
throw new ApiError("invalid operation", "no matching recipe");
}
use of eelfloat.replcraft.exceptions.ApiError in project replcraft by LeeFlemingRepl.
the class GetInventory method execute.
@Override
public void execute(Client client, WsMessageContext ctx, JSONObject request, JSONObject response) throws ApiError {
JSONArray items = new JSONArray();
BlockState state = getBlock(client, request).getState();
if (!(state instanceof Container)) {
throw new ApiError("invalid operation", "block isn't a container");
}
ItemStack[] contents = ((Container) state).getInventory().getContents();
for (int i = 0; i < contents.length; i++) {
ItemStack item = contents[i];
if (item == null)
continue;
JSONObject jsonitem = new JSONObject();
jsonitem.put("index", i);
jsonitem.put("type", item.getType().getKey());
jsonitem.put("amount", item.getAmount());
jsonitem.put("enchantments", item.getEnchantments().entrySet().stream().map(entry -> {
JSONObject enchantment = new JSONObject();
enchantment.put("id", entry.getKey().getKey());
enchantment.put("lvl", entry.getValue().intValue());
return enchantment;
}).collect(Collectors.toList()));
ItemMeta itemMeta = item.getItemMeta();
if (itemMeta != null) {
jsonitem.put("meta", itemMeta.serialize());
if (itemMeta instanceof Damageable) {
short maxDurability = item.getType().getMaxDurability();
int damage = ((Damageable) itemMeta).getDamage();
jsonitem.put("maxDurability", maxDurability);
jsonitem.put("durability", maxDurability - damage);
}
if (itemMeta instanceof BookMeta) {
jsonitem.put("pages", ((BookMeta) itemMeta).getPages());
jsonitem.put("author", ((BookMeta) itemMeta).getAuthor());
jsonitem.put("title", ((BookMeta) itemMeta).getTitle());
}
}
items.put(jsonitem);
}
response.put("items", items);
}
use of eelfloat.replcraft.exceptions.ApiError in project replcraft by LeeFlemingRepl.
the class GetSignText method execute.
@Override
public void execute(Client client, WsMessageContext ctx, JSONObject request, JSONObject response) throws ApiError {
BlockState state = getBlock(client, request).getState();
if (!(state instanceof Sign)) {
throw new ApiError("invalid operation", "block is not a sign");
}
response.put("lines", ((Sign) state).getLines());
}
Aggregations