diff --git a/src/main/java/org/ultramine/commands/CommandContext.java b/src/main/java/org/ultramine/commands/CommandContext.java index bee11e4..8a4a741 100644 --- a/src/main/java/org/ultramine/commands/CommandContext.java +++ b/src/main/java/org/ultramine/commands/CommandContext.java @@ -9,6 +9,7 @@ import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; import net.minecraft.server.MinecraftServer; import net.minecraft.util.ChatComponentText; import net.minecraft.util.ChatComponentTranslation; @@ -20,6 +21,7 @@ import org.ultramine.server.PermissionHandler; import org.ultramine.server.data.ServerDataLoader; import org.ultramine.server.data.player.PlayerData; +import org.ultramine.server.util.BasicTypeParser; import java.util.HashMap; import java.util.List; @@ -337,6 +339,11 @@ { return CommandBase.getBlockByText(sender, value); } + + public ItemStack asItemStack() + { + return BasicTypeParser.parseItemStack(asString()); + } } public static class Builder diff --git a/src/main/java/org/ultramine/commands/basic/BasicCommands.java b/src/main/java/org/ultramine/commands/basic/BasicCommands.java index 02d1440..7666184 100644 --- a/src/main/java/org/ultramine/commands/basic/BasicCommands.java +++ b/src/main/java/org/ultramine/commands/basic/BasicCommands.java @@ -4,6 +4,9 @@ import static net.minecraft.util.EnumChatFormatting.*; import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.ChatStyle; import net.minecraft.util.MathHelper; import net.minecraft.world.storage.WorldInfo; @@ -11,6 +14,7 @@ import org.ultramine.commands.CommandContext; import org.ultramine.server.Teleporter; import org.ultramine.server.data.player.PlayerData; +import org.ultramine.server.util.InventoryUtil; import org.ultramine.server.util.WarpLocation; public class BasicCommands @@ -243,4 +247,70 @@ WorldInfo wi = player.worldObj.getWorldInfo(); Teleporter.tpNow(player, wi.getSpawnX(), wi.getSpawnY(), wi.getSpawnZ()); } + + @Command( + name = "heal", + group = "admin", + permissions = {"command.heal", "command.heal.other"}, + syntax = {"", ""} + ) + public static void heal(CommandContext ctx) + { + ctx.checkPermissionIfArg("player", "command.heal.other", "command.heal.noperm.other"); + EntityPlayerMP player = ctx.contains("player") ? ctx.get("player").asPlayer() : ctx.getSenderAsPlayer(); + player.setHealth(player.getMaxHealth()); + player.getFoodStats().addStats(20, 0.5F); + player.addChatComponentMessage(new ChatComponentTranslation("command.heal.success").setChatStyle(new ChatStyle().setColor(GOLD))); + if(ctx.contains("player")) + ctx.sendMessage("command.heal.success.other", player.getCommandSenderName()); + } + + @Command( + name = "dropall", + group = "admin", + permissions = {"command.dropall"}, + syntax = {"", ""} + ) + public static void dropall(CommandContext ctx) + { + ctx.checkPermissionIfArg("player", "command.dropall.other", "command.dropall.noperm.other"); + EntityPlayerMP player = ctx.contains("player") ? ctx.get("player").asPlayer() : ctx.getSenderAsPlayer(); + player.inventory.dropAllItems(); + } + + @Command( + name = "item", + group = "admin", + aliases = {"i"}, + permissions = {"command.item"}, + syntax = { + "", + " ", + " ..." + } + ) + public static void item(CommandContext ctx) + { + ItemStack is = ctx.get("item").asItemStack(); + EntityPlayerMP player = ctx.contains("player") ? ctx.get("player").asPlayer() : ctx.getSenderAsPlayer(); + if(ctx.contains("size")) + is.stackSize = ctx.get("size").asInt(); + InventoryUtil.addItem(player.inventory, is); + } + + @Command( + name = "dupe", + group = "admin", + permissions = {"command.dupe"}, + syntax = {"", "<%count>"} + ) + public static void dupe(CommandContext ctx) + { + ItemStack is = ctx.getSenderAsPlayer().inventory.getCurrentItem(); + ctx.check(is != null, "command.dupe.fail"); + is = is.copy(); + if(ctx.contains("count")) + is.stackSize *= ctx.get("count").asInt(); + InventoryUtil.addItem(ctx.getSenderAsPlayer().inventory, is); + } } diff --git a/src/main/java/org/ultramine/commands/syntax/DefaultCompleters.java b/src/main/java/org/ultramine/commands/syntax/DefaultCompleters.java index 935e5a1..3224a81 100644 --- a/src/main/java/org/ultramine/commands/syntax/DefaultCompleters.java +++ b/src/main/java/org/ultramine/commands/syntax/DefaultCompleters.java @@ -5,8 +5,13 @@ import net.minecraft.entity.EntityList; import net.minecraft.item.Item; import net.minecraft.server.MinecraftServer; + import org.ultramine.server.util.BasicTypeParser; +import com.google.common.collect.Iterables; + +import cpw.mods.fml.common.registry.FMLControlledNamespacedRegistry; + import java.util.ArrayList; import java.util.List; @@ -18,18 +23,25 @@ return filterArray(val, MinecraftServer.getServer().getAllUsernames()); } + @SuppressWarnings({ "unchecked", "rawtypes" }) @ArgumentCompleter("item") - public static List item(String val, String[] args) + public static List item(String val, String[] args) { - return filterCollection(val, Item.itemRegistry.getKeys()); + Iterable it = Iterables.concat(Item.itemRegistry.getKeys(), ((FMLControlledNamespacedRegistry)Item.itemRegistry).getAliases().keySet()); + List ret = filterCollection(val, it); + if(val.indexOf(':') == -1) + ret.addAll(filterCollection("minecraft:".concat(val), it)); + return ret; } + @SuppressWarnings({ "unchecked", "rawtypes" }) @ArgumentCompleter("block") public static List block(String val, String[] args) { - return filterCollection(val, Block.blockRegistry.getKeys()); + return filterCollection(val, Iterables.concat(Block.blockRegistry.getKeys(), ((FMLControlledNamespacedRegistry)Block.blockRegistry).getAliases().keySet())); } + @SuppressWarnings({ "unchecked" }) @ArgumentCompleter("entity") public static List entity(String val, String[] args) { @@ -54,6 +66,12 @@ return filterCollection(val, MinecraftServer.getServer().getConfigurationManager().getDataLoader().getWarps().keySet()); } + @ArgumentValidator("int") + public static boolean int_validator(String val, String[] args) + { + return BasicTypeParser.isInt(val); + } + @ArgumentValidator("world") public static boolean world_validator(String val, String[] args) { diff --git a/src/main/java/org/ultramine/server/util/BasicTypeParser.java b/src/main/java/org/ultramine/server/util/BasicTypeParser.java index 10eed9f..89b139c 100644 --- a/src/main/java/org/ultramine/server/util/BasicTypeParser.java +++ b/src/main/java/org/ultramine/server/util/BasicTypeParser.java @@ -1,5 +1,13 @@ package org.ultramine.server.util; +import net.minecraft.command.CommandBase; +import net.minecraft.command.CommandException; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.JsonToNBT; +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTException; +import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.EnumChatFormatting; import net.minecraft.world.EnumDifficulty; @@ -94,4 +102,71 @@ return null; } + + public static ItemStack parseItemStack(String str) + { + int len = str.length(); + int ind1 = str.indexOf(':'); + int ind4 = str.indexOf(':', ind1+1); + int ind2 = str.indexOf('*'); + int ind3 = str.indexOf('{'); + if(ind3 != -1 && ind1 > ind3) ind1 = -1; + if(ind3 != -1 && ind2 > ind3) ind2 = -1; + if(ind4 != -1 && ind4 != -1 && (ind4 < ind3 || ind3 == -1)) ind1 = ind4; + boolean hasData = ind1 != -1; + boolean hasSize = ind2 != -1; + boolean hasNBT = ind3 != -1; + int idEndInd = hasData ? ind1 : hasSize ? ind2 : hasNBT ? ind3 : len; + int dataEndInd = hasSize ? ind2 : hasNBT ? ind3 : len; + int sizeEndInd = hasNBT ? ind3 : len; + int data = 0; + int size = -1; + NBTTagCompound nbt = null; + int endInd; + Item item = CommandBase.getItemByText(null, str.substring(0, endInd = idEndInd)); + if(hasData) + { + String dataS = str.substring(endInd+1, endInd = dataEndInd); + try + { + data = Integer.parseInt(dataS); + } + catch(NumberFormatException e) + { + throw new CommandException("commands.generic.itemstack.data", dataS); + } + } + if(hasSize) + { + String sizeS = str.substring(endInd+1, endInd = sizeEndInd); + try + { + size = Integer.parseInt(sizeS); + } + catch(NumberFormatException e) + { + throw new CommandException("commands.generic.itemstack.size", sizeS); + } + } + if(hasNBT) + { + try + { + NBTBase nbtbase = JsonToNBT.func_150315_a(str.substring(endInd, len)); + if(nbtbase instanceof NBTTagCompound) + nbt = (NBTTagCompound)nbtbase; + } + catch(NBTException e) + { + throw new CommandException("commands.setblock.tagError", e.getMessage()); + } + } + + ItemStack is = new ItemStack(item, 1, data); + if(nbt != null) + is.setTagCompound(nbt); + is.stackSize = size != -1 ? size : is.getMaxStackSize(); + + return is; + } } diff --git a/src/main/java/org/ultramine/server/util/InventoryUtil.java b/src/main/java/org/ultramine/server/util/InventoryUtil.java new file mode 100644 index 0000000..7c08537 --- /dev/null +++ b/src/main/java/org/ultramine/server/util/InventoryUtil.java @@ -0,0 +1,236 @@ +package org.ultramine.server.util; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +import net.minecraft.entity.item.EntityItem; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; + +public class InventoryUtil +{ + public static boolean isStacksEquals(ItemStack is1, ItemStack is2) + { + return + is1 == null && is2 == null || is1 != null && is1.isItemEqual(is2) && + (is1.stackTagCompound == null && is2.stackTagCompound != null ? false : is1.stackTagCompound == null || is1.stackTagCompound.equals(is2.stackTagCompound)); + } + + public static boolean contains(IInventory inv, ItemStack item) + { + for(int i = 0; i < inv.getSizeInventory(); i++) + { + ItemStack is = inv.getStackInSlot(i); + if(isStacksEquals(is, item)) return true; + } + + return false; + } + + public static int first(IInventory inv, ItemStack item) + { + for(int i = 0; i < inv.getSizeInventory(); i++) + { + ItemStack is = inv.getStackInSlot(i); + if(isStacksEquals(is, item)) return i; + } + + return -1; + } + + public static int firstEmpty(IInventory inv) + { + for(int i = 0; i < inv.getSizeInventory(); i++) + { + if(inv.getStackInSlot(i) == null) return i; + } + + return -1; + } + + private static int firstPartial(IInventory inv, ItemStack filteredItem) + { + if(filteredItem == null) + { + return -1; + } + for(int i = 0; i < inv.getSizeInventory(); i++) + { + ItemStack cItem = inv.getStackInSlot(i); + if(cItem != null && cItem.stackSize < cItem.getMaxStackSize() && isStacksEquals(cItem, filteredItem)) + { + return i; + } + } + return -1; + } + + public static List removeItem(IInventory inv, ItemStack... items) + { + List ret = new LinkedList(); + for(ItemStack item : items) + { + ret.addAll(removeItem(inv, item)); + } + return ret; + } + + public static List removeItem(IInventory inv, ItemStack item) + { + List leftover = null; + + // TODO: optimization + + int toDelete = item.stackSize; + + while(true) + { + int first = first(inv, item); + + // Drat! we don't have this type in the inventory + if(first == -1) + { + item.stackSize = toDelete; + if(leftover == null) + leftover = new LinkedList(); + leftover.add(item); + break; + } + else + { + ItemStack itemStack = inv.getStackInSlot(first); + int amount = itemStack.stackSize; + + if(amount <= toDelete) + { + toDelete -= amount; + // clear the slot, all used up + inv.setInventorySlotContents(first, null); + } + else + { + // split the stack and store + itemStack.stackSize = amount - toDelete; + inv.setInventorySlotContents(first, itemStack); + toDelete = 0; + } + } + + // Bail when done + if(toDelete <= 0) + { + break; + } + } + + if(leftover == null) + leftover = Collections.emptyList(); + return leftover; + } + + public static List addItem(IInventory inv, ItemStack... items) + { + List ret = new LinkedList(); + for(ItemStack item : items) + { + ret.addAll(addItem(inv, item)); + } + return ret; + } + + public static List addItem(IInventory inv, ItemStack item) + { + List leftover = null; + + int maxSize = Math.min(inv.getInventoryStackLimit(), item.getMaxStackSize()); + + while(true) + { + // Do we already have a stack of it? + int firstPartial = firstPartial(inv, item); + + // Drat! no partial stack + if(firstPartial == -1) + { + // Find a free spot! + int firstFree = firstEmpty(inv); + + if(firstFree == -1) + { + // No space at all! + if(leftover == null) + leftover = new LinkedList(); + leftover.add(item); + break; + } + else + { + // More than a single stack! + if(item.stackSize > maxSize) + { + ItemStack stack = item.splitStack(maxSize); + inv.setInventorySlotContents(firstFree, stack); + } + else + { + // Just store it + inv.setInventorySlotContents(firstFree, item); + break; + } + } + } + else + { + // So, apparently it might only partially fit, well lets do just that + ItemStack partialItem = inv.getStackInSlot(firstPartial); + + int amount = item.stackSize; + int partialAmount = partialItem.stackSize; + int maxAmount = partialItem.getMaxStackSize(); + + // Check if it fully fits + if(amount + partialAmount <= maxAmount) + { + partialItem.stackSize = (amount + partialAmount); + break; + } + + // It fits partially + partialItem.stackSize = maxAmount; + item.stackSize = (amount + partialAmount - maxAmount); + } + } + + if(leftover == null) + leftover = Collections.emptyList(); + return leftover; + } + + public static void dropItem(World world, double x, double y, double z, ItemStack item) + { + if(item == null) + return; + double var7 = world.rand.nextDouble() * 0.8D + 0.1D; + double var9 = world.rand.nextDouble() * 0.8D + 0.1D; + double var11 = world.rand.nextDouble() * 0.8D + 0.1D; + EntityItem var14 = new EntityItem(world, x + var7, y + var9, z + var11, item); + double var15 = 0.05D; + var14.motionX = world.rand.nextGaussian() * var15; + var14.motionY = world.rand.nextGaussian() * var15 + 0.2D; + var14.motionZ = world.rand.nextGaussian() * var15; + world.spawnEntityInWorld(var14); + } + + public static void dropItemFixed(World world, double x, double y, double z, ItemStack item) + { + if(item == null) + return; + double var7 = world.rand.nextDouble() * 0.8D + 0.1D; + double var9 = world.rand.nextDouble() * 0.8D + 0.1D; + double var11 = world.rand.nextDouble() * 0.8D + 0.1D; + EntityItem var14 = new EntityItem(world, x + var7, y + var9, z + var11, item); + world.spawnEntityInWorld(var14); + } +} diff --git a/src/main/resources/assets/ultramine/lang/en_US.lang b/src/main/resources/assets/ultramine/lang/en_US.lang index 32d0b63..f536fbb 100644 --- a/src/main/resources/assets/ultramine/lang/en_US.lang +++ b/src/main/resources/assets/ultramine/lang/en_US.lang @@ -5,6 +5,8 @@ #Command generic commands.generic.world.invalid=Can't find world '%s' +commands.generic.itemstack.data=Failed to parse item data: %s +commands.generic.itemstack.size=Failed to parse stack size: %s #Vanilla replacements command.tp.usage=/tp [target player] OR /tp [target player] [world] @@ -119,6 +121,23 @@ command.localspawn.usage=/localspawn command.localspawn.description=Teleport to local spawn of current world (vanilla logic) +command.heal.usage=/heal [player] +command.heal.description=Restores health and hunger +command.heal.noperm.other=Yout don't have permissions to heal other player +command.heal.success=You have been healed +command.heal.success.other=Player %s has been healed + +command.dropall.usage=/dropall [player] +command.dropall.description=Drops all items in inventory +command.dropall.noperm.other=Yout don't have permissions to dropall other player + +command.item.usage=/item [count] OR /item +command.item.description=Get specified item stack + +command.dupe.usage=/dupe [count] +command.dupe.description=Dupe the item in hand +command.dupe.fail=Get item for dupe to the hand first + #Technical commands command.id.usage=/id command.id.description=Displays information about specified item id