Skip to content

Commit

Permalink
Merge pull request #190 from Microsoft/simplifyInventorySwaps
Browse files Browse the repository at this point in the history
Simplify inventory swaps, add combine command.
  • Loading branch information
DaveyBiggers authored Jul 14, 2016
2 parents 03deaeb + c11dfa2 commit 4c6dd8c
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 28 deletions.
2 changes: 2 additions & 0 deletions Minecraft/src/main/java/com/microsoft/Malmo/MalmoMod.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@

import com.microsoft.Malmo.Client.MalmoModClient;
import com.microsoft.Malmo.MissionHandlers.AbsoluteMovementCommandsImplementation;
import com.microsoft.Malmo.MissionHandlers.InventoryCommandsImplementation;
import com.microsoft.Malmo.MissionHandlers.ObservationFromFullStatsImplementation;
import com.microsoft.Malmo.MissionHandlers.ObservationFromGridImplementation;
import com.microsoft.Malmo.MissionHandlers.SimpleCraftCommandsImplementation;
Expand Down Expand Up @@ -116,6 +117,7 @@ public void preInit(FMLPreInitializationEvent event)
network.registerMessage(SimpleCraftCommandsImplementation.CraftMessageHandler.class, SimpleCraftCommandsImplementation.CraftMessage.class, 4, Side.SERVER);
network.registerMessage(AbsoluteMovementCommandsImplementation.TeleportMessageHandler.class, AbsoluteMovementCommandsImplementation.TeleportMessage.class, 5, Side.SERVER);
network.registerMessage(MalmoMessageHandler.class, MalmoMessage.class, 6, Side.SERVER); // Malmo messages from client to server
network.registerMessage(InventoryCommandsImplementation.InventoryMessageHandler.class, InventoryCommandsImplementation.InventoryMessage.class, 7, Side.SERVER);
}

public Configuration getModSessionConfigFile() { return this.sessionConfig; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,20 @@

package com.microsoft.Malmo.MissionHandlers;

import io.netty.buffer.ByteBuf;

import java.util.ArrayList;
import java.util.List;

import net.minecraft.client.Minecraft;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.item.ItemStack;
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler;
import net.minecraftforge.fml.common.network.simpleimpl.MessageContext;

import com.microsoft.Malmo.MalmoMod;
import com.microsoft.Malmo.Schemas.InventoryCommand;
import com.microsoft.Malmo.Schemas.InventoryCommands;
import com.microsoft.Malmo.Schemas.MissionInit;
Expand All @@ -34,7 +44,53 @@
*/
public class InventoryCommandsImplementation extends CommandGroup
{
private int sourceSlotIndex = 0;
public static class InventoryMessage implements IMessage
{
int slotA;
int slotB;
boolean combine;

public InventoryMessage()
{
}

public InventoryMessage(int a, int b, boolean combine)
{
this.slotA = a;
this.slotB = b;
this.combine = combine;
}

@Override
public void fromBytes(ByteBuf buf)
{
this.slotA = buf.readInt();
this.slotB = buf.readInt();
this.combine = buf.readBoolean();
}

@Override
public void toBytes(ByteBuf buf)
{
buf.writeInt(this.slotA);
buf.writeInt(this.slotB);
buf.writeBoolean(this.combine);
}
}

public static class InventoryMessageHandler implements IMessageHandler<InventoryMessage, IMessage>
{
@Override
public IMessage onMessage(InventoryMessage message, MessageContext ctx)
{
EntityPlayerMP player = ctx.getServerHandler().playerEntity;
if (message.combine)
combineSlots(player, message.slotA, message.slotB);
else
swapSlots(player, message.slotA, message.slotB);
return null;
}
}

InventoryCommandsImplementation()
{
Expand All @@ -48,46 +104,137 @@ public boolean parseParameters(Object params)
super.parseParameters(params);

if (params == null || !(params instanceof InventoryCommands))
return false;
InventoryCommands iparams = (InventoryCommands)params;
setUpAllowAndDenyLists(iparams.getModifierList());
return true;
return false;

InventoryCommands iparams = (InventoryCommands) params;
setUpAllowAndDenyLists(iparams.getModifierList());
return true;
}


static void combineSlots(EntityPlayerMP player, int dst, int add)
{
InventoryPlayer inv = player.inventory;
ItemStack dstStack = inv.getStackInSlot(dst);
ItemStack addStack = inv.getStackInSlot(add);

if (addStack == null)
return; // Combination is a no-op.

if (dstStack == null) // Do a straight move - nothing to combine with.
{
inv.setInventorySlotContents(dst, addStack);
inv.setInventorySlotContents(add, null);
return;
}

// Check we can combine. This logic comes from InventoryPlayer.storeItemStack():
boolean itemsMatch = dstStack.getItem() == addStack.getItem();
boolean dstCanStack = dstStack.isStackable() && dstStack.stackSize < dstStack.getMaxStackSize() && dstStack.stackSize < inv.getInventoryStackLimit();
boolean subTypesMatch = !dstStack.getHasSubtypes() || dstStack.getMetadata() == addStack.getMetadata();
boolean tagsMatch = ItemStack.areItemStackTagsEqual(dstStack, addStack);
if (itemsMatch && dstCanStack && subTypesMatch && tagsMatch)
{
// We can combine, so figure out how much we have room for:
int limit = Math.min(dstStack.getMaxStackSize(), inv.getInventoryStackLimit());
int room = limit - dstStack.stackSize;
if (addStack.stackSize > room)
{
// Not room for all of it, so shift across as much as possible.
addStack.stackSize -= room;
dstStack.stackSize += room;
}
else
{
// Room for the whole lot, so empty out the add slot.
dstStack.stackSize += addStack.stackSize;
inv.setInventorySlotContents(add, null);
}
}
}

static void swapSlots(EntityPlayerMP player, int lhs, int rhs)
{
InventoryPlayer inv = player.inventory;
ItemStack srcStack = inv.getStackInSlot(lhs);
ItemStack dstStack = inv.getStackInSlot(rhs);
inv.setInventorySlotContents(lhs, dstStack);
inv.setInventorySlotContents(rhs, srcStack);
}

@Override
protected boolean onExecute(String verb, String parameter, MissionInit missionInit)
{
if (verb.equalsIgnoreCase(InventoryCommand.SELECT_INVENTORY_ITEM.value()))
if (verb.equalsIgnoreCase(InventoryCommand.SWAP_INVENTORY_ITEMS.value()))
{
if (parameter != null && parameter.length() != 0)
{
this.sourceSlotIndex = Integer.valueOf(parameter);
return true;
List<Integer> params = new ArrayList<Integer>();
if (getParameters(parameter, params))
{
// All okay, so create a swap message for the server:
MalmoMod.network.sendToServer(new InventoryMessage(params.get(0), params.get(1), false));
return true;
}
else
return false; // Duff parameters.
}
}
else if (verb.equalsIgnoreCase(InventoryCommand.DROP_INVENTORY_ITEM.value()))
else if (verb.equalsIgnoreCase(InventoryCommand.COMBINE_INVENTORY_ITEMS.value()))
{
if (parameter != null && parameter.length() != 0)
{
int slot = Integer.valueOf(parameter);
if (slot == this.sourceSlotIndex)
List<Integer> params = new ArrayList<Integer>();
if (getParameters(parameter, params))
{
return true; // No-op.
// All okay, so create a combine message for the server:
MalmoMod.network.sendToServer(new InventoryMessage(params.get(0), params.get(1), true));
return true;
}
InventoryPlayer inv = Minecraft.getMinecraft().thePlayer.inventory;
ItemStack srcStack = inv.getStackInSlot(this.sourceSlotIndex);
ItemStack dstStack = inv.getStackInSlot(slot);
inv.setInventorySlotContents(this.sourceSlotIndex, dstStack);
inv.setInventorySlotContents(slot, srcStack);
return true;
else
return false; // Duff parameters.
}
}
else if (verb.equalsIgnoreCase(InventoryCommand.DISCARD_CURRENT_ITEM.value()))
{
// This we can do on the client side:
Minecraft.getMinecraft().thePlayer.dropOneItem(false); // false means just drop one item - true means drop everything in the current stack.
return true;
}
return super.onExecute(verb, parameter, missionInit);
}

private boolean getParameters(String parameter, List<Integer> parsedParams)
{
String[] params = parameter.split(" ");
if (params.length != 2)
{
System.out.println("Malformed parameter string (" + parameter + ") - expected <x> <y>");
return false; // Error - incorrect number of parameters.
}
Integer lhs, rhs;
try
{
lhs = Integer.valueOf(params[0]);
rhs = Integer.valueOf(params[1]);
}
catch (NumberFormatException e)
{
System.out.println("Malformed parameter string (" + parameter + ") - " + e.getMessage());
return false;
}
if (lhs == null || rhs == null)
{
System.out.println("Malformed parameter string (" + parameter + ")");
return false; // Error - incorrect parameters.
}
InventoryPlayer inv = Minecraft.getMinecraft().thePlayer.inventory;
if (lhs < 0 || lhs >= inv.getSizeInventory() || rhs < 0 || rhs >= inv.getSizeInventory())
{
System.out.println("Inventory swap parameters out of bounds - must be between 0 and " + (inv.getSizeInventory() - 1));
return false; // Out of bounds.
}
parsedParams.add(lhs);
parsedParams.add(rhs);
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ public static Item ParseItemType( String s )
if (s == null)
return null;
Item item = (Item)Item.itemRegistry.getObject(new ResourceLocation(s)); // Minecraft returns null when it doesn't recognise the string
if (item == null)
{
// Maybe this is a request for a block item?
IBlockState block = MinecraftTypeHelper.ParseBlockType(s);
item = (block != null && block.getBlock() != null) ? Item.getItemFromBlock(block.getBlock()) : null;
}
return item;
}
}
25 changes: 17 additions & 8 deletions Schemas/MissionHandlers.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -809,16 +809,25 @@
<xs:documentation>
Commands for changing the contents of the inventory and hotbar.

To move items around in the inventory you can use {{{selectInventoryItem}}} and {{{dropInventoryItem}}}. For example, to swap
the contents of slots 13 and 14, issue these two commands in this order:
To move items around in the inventory you can use {{{swapInventoryItems}}}. For example, to swap
the contents of slots 13 and 14, issue this command:

"{{{selectInventoryItem 13}}}"
"{{{swapInventoryItems 13 14}}}"

"{{{dropInventoryItem 14}}}"
Note that inventory slots are numbered from 0 to 39.
0-8 are the hotbar slots (which correspond to the hotbar commands hotbar.1-hotbar.9 - _note the offset_)
9-35 are the rest of the inventory (visible when you press 'E' in the game)
36-39 are the armour slots.

So to move an item out of the hotbar, say:

"{{{swapInventoryItems 3 30}}}"

Other commands:

"{{{discardCurrentItem 15}}}" - discards the item that is in slot 15 in the agent's inventory.
"{{{combineInventoryItems x y}}}" - will attempt to combine the stacks in slots x and y, and leave the results in slot x. Eg if there are ten blocks of granite in slot 4, and 57 blocks of granite in slot 12, then {{{combineInventoryItems 4 12}}} will result in 64 (the max) blocks of granite in slot 4, and the remainder in slot 12. If the slots can't be combined (they are different types, or the first slot is full) then nothing will happen.

"{{{discardCurrentItem}}}" - discards the currently held item.

To select a hotbar slot:

Expand All @@ -830,8 +839,8 @@
</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:string">
<xs:enumeration value="selectInventoryItem" />
<xs:enumeration value="dropInventoryItem" />
<xs:enumeration value="swapInventoryItems" />
<xs:enumeration value="combineInventoryItems" />
<xs:enumeration value="discardCurrentItem" />
<xs:enumeration value="hotbar.1" />
<xs:enumeration value="hotbar.2" />
Expand Down Expand Up @@ -1126,7 +1135,7 @@
When present, the Mod will return observations that say what is in the player's inventory.

Two values are returned for each slot, if not empty: e.g. {{{Inventory_0_size}}} and {{{Inventory_0_item}}} containing the number and
type of the item(s) in the slot, respectively. Inventory slots are numbered 1 to 27 inclusive.
type of the item(s) in the slot, respectively. Inventory slots are numbered 0 to 39 inclusive.
</xs:documentation>
</xs:annotation>
<xs:complexType/>
Expand Down

0 comments on commit 4c6dd8c

Please sign in to comment.