diff --git a/Malmo/samples/Python_examples/inventory_test.py b/Malmo/samples/Python_examples/inventory_test.py index 3658b6449..064255480 100755 --- a/Malmo/samples/Python_examples/inventory_test.py +++ b/Malmo/samples/Python_examples/inventory_test.py @@ -187,7 +187,10 @@ def getContainerXML(): while world_state.is_mission_running: world_state = agent_host.getWorldState() if world_state.number_of_rewards_since_last_state > 0: - total_reward += world_state.rewards[-1].getValue() + reward = world_state.rewards[-1].getValue() + if reward != 0: + print("Got reward of " + str(reward)) + total_reward += reward if world_state.number_of_observations_since_last_state > 0: obs = json.loads(world_state.observations[-1].text) @@ -306,7 +309,10 @@ def getContainerXML(): # Mission has ended. # Get final reward: if world_state.number_of_rewards_since_last_state > 0: - total_reward += world_state.rewards[-1].getValue() + reward = world_state.rewards[-1].getValue() + if reward != 0: + print("Got final reward of " + str(reward)) + total_reward += reward test_passed = True if False in list(completed_boxes.values()): diff --git a/Malmo/samples/Python_examples/sample_missions_loader.py b/Malmo/samples/Python_examples/sample_missions_loader.py index ca8d541a5..f1a8baa41 100644 --- a/Malmo/samples/Python_examples/sample_missions_loader.py +++ b/Malmo/samples/Python_examples/sample_missions_loader.py @@ -34,7 +34,9 @@ # -- set up the mission -- # #mission_file_no_ext = '../Sample_missions/default_world_1' # Survive and find gold, diamond or redstone! #mission_file_no_ext = '../Sample_missions/default_flat_1' # Move to a wooden hut in a snow tempest! -mission_file_no_ext = '../Sample_missions/tricky_arena_1' # Mind your step to the redstone! +# This is the default mission to run: +mission_file_no_ext = '../Sample_missions/tricky_arena_1' # Mind your step to the redstone! +#mission_file_no_ext = '../Sample_missions/inventory_handlers' # Tour of crafting, smelting, collecting! #mission_file_no_ext = '../Sample_missions/eating_1' # Eat a healthy diet! #mission_file_no_ext = '../Sample_missions/cliff_walking_1' # Burning lava! diff --git a/Malmo/src/MissionSpec.cpp b/Malmo/src/MissionSpec.cpp index 679bec5cf..6ed92ab75 100755 --- a/Malmo/src/MissionSpec.cpp +++ b/Malmo/src/MissionSpec.cpp @@ -39,6 +39,8 @@ namespace malmo const std::vector MissionSpec::all_inventory_commands = { "swapInventoryItems", "combineInventoryItems", "discardCurrentItem", "hotbar.1", "hotbar.2", "hotbar.3", "hotbar.4", "hotbar.5", "hotbar.6", "hotbar.7", "hotbar.8", "hotbar.9" }; const std::vector MissionSpec::all_simplecraft_commands = { "craft" }; + const std::vector MissionSpec::all_nearbycraft_commands = { "craftNearby" }; + const std::vector MissionSpec::all_nearbysmelt_commands = { "smeltNearby" }; const std::vector MissionSpec::all_chat_commands = { "chat" }; const std::vector MissionSpec::all_mission_quit_commands = { "quit" }; const std::vector MissionSpec::all_human_level_commands = { "forward", "left", "right", "jump", "sneak", "sprint", "inventory", "swapHands", "drop", "use", "attack", "moveMouse", @@ -110,6 +112,7 @@ namespace malmo child.erase("FlatWorldGenerator"); child.erase("FileWorldGenerator"); child.erase("DefaultWorldGenerator"); + child.erase("BiomeGenerator"); } } @@ -137,6 +140,10 @@ namespace malmo if (defaultWorldGenerator) { defaultWorldGenerator.get().put(".forceReset", true); } + const auto& biomeWorldGenerator = mission.get_child_optional("Mission.ServerSection.ServerHandlers.BiomeGenerator"); + if (biomeWorldGenerator) { + biomeWorldGenerator.get().put(".forceReset", true); + } } void MissionSpec::setTimeOfDay(int t,bool allowTimeToPass) @@ -359,6 +366,11 @@ namespace malmo mission.put("Mission.AgentSection.AgentHandlers.ObservationFromChat", ""); } + void MissionSpec::observeCompass() + { + mission.put("Mission.AgentSection.AgentHandlers.ObservationFromCompass", ""); + } + // ------------------ settings for the agents : command handlers -------------------------------- void MissionSpec::removeAllCommandHandlers() @@ -371,6 +383,9 @@ namespace malmo child.erase("DiscreteMovementCommands"); child.erase("AbsoluteMovementCommands"); child.erase("SimpleCraftCommands"); + child.erase("NearbyCraftCommands"); + child.erase("NearbySmeltCommands"); + child.erase("PlaceCommands"); child.erase("ChatCommands"); child.erase("MissionQuitCommands"); } @@ -415,11 +430,16 @@ namespace malmo { addVerbToCommandType(verb, "Mission.AgentSection.AgentHandlers.InventoryCommands"); } - + void MissionSpec::allowAllChatCommands() { mission.put("Mission.AgentSection.AgentHandlers.ChatCommands", ""); } + + void MissionSpec::allowAllPlaceCommands() + { + mission.put("Mission.AgentSection.AgentHandlers.PlaceCommands", ""); + } // ------------------------------- information --------------------------------------------------- @@ -517,6 +537,15 @@ namespace malmo if (e.second.get_child_optional("AgentHandlers.SimpleCraftCommands")) command_handlers.push_back("SimpleCraft"); + + if (e.second.get_child_optional("AgentHandlers.NearbyCraftCommands")) + command_handlers.push_back("NearbyCraft"); + + if (e.second.get_child_optional("AgentHandlers.NearbySmeltCommands")) + command_handlers.push_back("NearbySmelt"); + + if (e.second.get_child_optional("AgentHandlers.PlaceCommands")) + command_handlers.push_back("Place"); if (e.second.get_child_optional("AgentHandlers.MissionQuitCommands")) command_handlers.push_back("MissionQuit"); @@ -577,6 +606,12 @@ namespace malmo else if (command_handler == "SimpleCraft") { allowed_commands = all_simplecraft_commands; } + else if (command_handler == "NearbyCraft") { + allowed_commands = all_nearbycraft_commands; + } + else if (command_handler == "NearbySmelt") { + allowed_commands = all_nearbysmelt_commands; + } else if (command_handler == "Chat") { allowed_commands = all_chat_commands; } diff --git a/Malmo/src/MissionSpec.h b/Malmo/src/MissionSpec.h index d79d4b078..2595495b4 100755 --- a/Malmo/src/MissionSpec.h +++ b/Malmo/src/MissionSpec.h @@ -242,6 +242,9 @@ namespace malmo //! Asks for chat messages to be included in the observations. void observeChat(); + //! Asks for compass information to be included in the observations. + void observeCompass(); + // -------------------- settings for the agents : command handlers ------------------------- //! Remove any existing command handlers from the mission specification. Use with other functions to add exactly the command handlers you want. @@ -296,6 +299,10 @@ namespace malmo //! Only applies to the first agent in the mission. For multi-agent missions, specify the command handlers for each in the XML. void allowAllChatCommands(); + //! Adds a place command handler if none present, with neither an allow-list or a deny-list, thus allowing any command to be sent. + //! Only applies to the first agent in the mission. For multi-agent missions, specify the command handlers for each in the XML. + void allowAllPlaceCommands(); + // ------------------------- information -------------------------------------- //! Returns the short description of the mission. @@ -379,6 +386,8 @@ namespace malmo static const std::vector all_discrete_movement_commands; static const std::vector all_inventory_commands; static const std::vector all_simplecraft_commands; + static const std::vector all_nearbycraft_commands; + static const std::vector all_nearbysmelt_commands; static const std::vector all_chat_commands; static const std::vector all_mission_quit_commands; static const std::vector all_human_level_commands; diff --git a/MalmoEnv/malmoenv/commands.py b/MalmoEnv/malmoenv/commands.py index b428a9aa6..27b6bdd7f 100644 --- a/MalmoEnv/malmoenv/commands.py +++ b/MalmoEnv/malmoenv/commands.py @@ -35,6 +35,8 @@ class CommandParser: inventoryCommands = "Inventory" chatCommands = "Chat" simpleCraftCommands = "SimpleCraft" + nearbyCraftCommands = "NearbyCraft" + nearbySmeltCommands = "NearbySmelt" missionQuitCommands = "MissionQuit" humanLevelCommands = "HumanLevel" @@ -50,6 +52,8 @@ class CommandParser: "hotbar.7", "hotbar.8", "hotbar.9"] all_chat = ["chat"] all_simplecraft = ["craft"] + all_nearbycraft = ["nearbyCraft"] + all_nearbysmelt = ["nearbySmelt"] all_mission_quit = ["quit"] all_human_level = ["forward", "left", "right", "jump", "sneak", "sprint", "inventory", "swapHands", "drop", "use", "attack", "moveMouse", @@ -126,6 +130,14 @@ def get_actions(self, commands): if verb != 'chat': raise CommandHandlerException("Invalid chat command") actions.append(verb) + elif type == 'NearbyCraft': + if verb != 'nearbyCraft': + raise CommandHandlerException("Invalid nearby craft command") + actions.append(verb) + elif type == 'NearbySmelt': + if verb != 'nearbySmelt': + raise CommandHandlerException("Invalid nearby smelt command") + actions.append(verb) elif type == 'SimpleCraft': if verb != 'craft': raise CommandHandlerException("Invalid craft command") @@ -152,6 +164,10 @@ def _command_hander(self, handlers, turnbased, commands): commands.extend(self._add_commands(CommandParser.chatCommands, turnbased, ch)) elif ch.tag == CommandParser.ns + "SimpleCraftCommands": commands.extend(self._add_commands(CommandParser.simpleCraftCommands, turnbased, ch)) + elif ch.tag == CommandParser.ns + "NearbyCraftCommands": + commands.extend(self._add_commands(CommandParser.nearbyCraftCommands, turnbased, ch)) + elif ch.tag == CommandParser.ns + "NearbySmeltCommands": + commands.extend(self._add_commands(CommandParser.nearbySmeltCommands, turnbased, ch)) elif ch.tag == CommandParser.ns + "MissionQuitCommands": commands.extend(self._add_commands(CommandParser.missionQuitCommands, turnbased, ch)) elif ch.tag == CommandParser.ns + "HumanLevelCommands": @@ -189,6 +205,10 @@ def _fill_command_list(self, command_type, turnbased, allow, deny): allow = [(command_type, turnbased, c) for c in CommandParser.all_human_level] elif command_type == CommandParser.simpleCraftCommands: allow = [(command_type, turnbased, c) for c in CommandParser.all_simplecraft] + elif command_type == CommandParser.nearbyCraftCommands: + allow = [(command_type, turnbased, c) for c in CommandParser.all_nearbycraft] + elif command_type == CommandParser.nearbySmeltCommands: + allow = [(command_type, turnbased, c) for c in CommandParser.all_nearbysmelt] elif command_type == CommandParser.missionQuitCommands: allow = [(command_type, turnbased, c) for c in CommandParser.missionQuitCommands] elif command_type == CommandParser.chatCommands: diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MalmoMod.java b/Minecraft/src/main/java/com/microsoft/Malmo/MalmoMod.java index 65c076a2a..1f1c7ddca 100755 --- a/Minecraft/src/main/java/com/microsoft/Malmo/MalmoMod.java +++ b/Minecraft/src/main/java/com/microsoft/Malmo/MalmoMod.java @@ -19,6 +19,7 @@ package com.microsoft.Malmo; +import com.microsoft.Malmo.MissionHandlers.*; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufInputStream; import io.netty.buffer.ByteBufOutputStream; @@ -57,14 +58,6 @@ import net.minecraftforge.fml.relauncher.Side; import com.microsoft.Malmo.Client.MalmoModClient; -import com.microsoft.Malmo.MissionHandlers.AbsoluteMovementCommandsImplementation; -import com.microsoft.Malmo.MissionHandlers.DiscreteMovementCommandsImplementation; -import com.microsoft.Malmo.MissionHandlers.InventoryCommandsImplementation; -import com.microsoft.Malmo.MissionHandlers.ObservationFromFullInventoryImplementation; -import com.microsoft.Malmo.MissionHandlers.ObservationFromFullStatsImplementation; -import com.microsoft.Malmo.MissionHandlers.ObservationFromGridImplementation; -import com.microsoft.Malmo.MissionHandlers.ObservationFromSystemImplementation; -import com.microsoft.Malmo.MissionHandlers.SimpleCraftCommandsImplementation; import com.microsoft.Malmo.Schemas.MissionInit; import com.microsoft.Malmo.Server.MalmoModServer; import com.microsoft.Malmo.Utils.AddressHelper; @@ -134,6 +127,8 @@ public void preInit(FMLPreInitializationEvent event) network.registerMessage(ObservationFromGridImplementation.GridRequestMessageHandler.class, ObservationFromGridImplementation.GridRequestMessage.class, 2, Side.SERVER); network.registerMessage(MalmoMessageHandler.class, MalmoMessage.class, 3, Side.CLIENT); // Malmo messages from server to client network.registerMessage(SimpleCraftCommandsImplementation.CraftMessageHandler.class, SimpleCraftCommandsImplementation.CraftMessage.class, 4, Side.SERVER); + network.registerMessage(NearbyCraftCommandsImplementation.CraftNearbyMessageHandler.class, NearbyCraftCommandsImplementation.CraftNearbyMessage.class, 13, Side.SERVER); + network.registerMessage(NearbySmeltCommandsImplementation.SmeltNearbyMessageHandler.class, NearbySmeltCommandsImplementation.SmeltNearbyMessage.class, 14, 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); diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/AgentQuitFromCollectingItemQuantityImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/AgentQuitFromCollectingItemQuantityImplementation.java new file mode 100644 index 000000000..74abf11ac --- /dev/null +++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/AgentQuitFromCollectingItemQuantityImplementation.java @@ -0,0 +1,184 @@ +// -------------------------------------------------------------------------------------------------- +// // Copyright (c) 2016 Microsoft Corporation +// // +// // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +// // associated documentation files (the "Software"), to deal in the Software without restriction, +// // including without limitation the rights to use, copy, modify, merge, publish, distribute, +// // sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +// // furnished to do so, subject to the following conditions: +// // +// // The above copyright notice and this permission notice shall be included in all copies or +// // substantial portions of the Software. +// // +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +// // NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// // -------------------------------------------------------------------------------------------------- + +package com.microsoft.Malmo.MissionHandlers; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.entity.player.EntityItemPickupEvent; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; + +import com.microsoft.Malmo.MissionHandlerInterfaces.IWantToQuit; +import com.microsoft.Malmo.MissionHandlers.RewardForCollectingItemImplementation.GainItemEvent; +import com.microsoft.Malmo.Schemas.AgentQuitFromCollectingItem; +import com.microsoft.Malmo.Schemas.BlockOrItemSpecWithDescription; +import com.microsoft.Malmo.Schemas.MissionInit; +import net.minecraftforge.fml.common.gameevent.PlayerEvent; + +/** + * Quits the mission when the agent has collected the right amount of items. The count on the item collection is absolute. + */ +public class AgentQuitFromCollectingItemQuantityImplementation extends HandlerBase implements IWantToQuit { + private AgentQuitFromCollectingItem params; + private HashMap collectedItems; + private List matchers; + private String quitCode = ""; + private boolean wantToQuit = false; + + public static class ItemQuitMatcher extends RewardForItemBase.ItemMatcher { + String description; + + ItemQuitMatcher(BlockOrItemSpecWithDescription spec) { + super(spec); + this.description = spec.getDescription(); + } + + String description() { + return this.description; + } + } + + @Override + public boolean parseParameters(Object params) { + if (!(params instanceof AgentQuitFromCollectingItem)) + return false; + + this.params = (AgentQuitFromCollectingItem) params; + this.matchers = new ArrayList(); + for (BlockOrItemSpecWithDescription bs : this.params.getItem()) + this.matchers.add(new ItemQuitMatcher(bs)); + return true; + } + + @Override + public boolean doIWantToQuit(MissionInit missionInit) { + return this.wantToQuit; + } + + @Override + public String getOutcome() { + return this.quitCode; + } + + @Override + public void prepare(MissionInit missionInit) { + MinecraftForge.EVENT_BUS.register(this); + collectedItems = new HashMap(); + } + + @Override + public void cleanup() { + MinecraftForge.EVENT_BUS.unregister(this); + } + + @SubscribeEvent + public void onGainItem(GainItemEvent event) { + if (event.stack != null && event.cause == 0) + checkForMatch(event.stack); + } + + @SubscribeEvent + public void onPickupItem(EntityItemPickupEvent event) { + if (event.getItem() != null && event.getEntityPlayer() instanceof EntityPlayerMP) + checkForMatch(event.getItem().getEntityItem()); + } + + @SubscribeEvent + public void onItemCraft(PlayerEvent.ItemCraftedEvent event) { + if (event.player instanceof EntityPlayerMP && !event.crafting.isEmpty()) + checkForMatch(event.crafting); + } + + @SubscribeEvent + public void onItemSmelt(PlayerEvent.ItemSmeltedEvent event) { + if (event.player instanceof EntityPlayerMP && !event.smelting.isEmpty()) + checkForMatch(event.smelting); + } + + /** + * Checks whether the ItemStack matches a variant stored in the item list. If + * so, returns true, else returns false. + * + * @param is The item stack + * @return If the stack is allowed in the item matchers and has color or + * variants enabled, returns true, else false. + */ + private boolean getVariant(ItemStack is) { + for (ItemQuitMatcher matcher : matchers) { + if (matcher.allowedItemTypes.contains(is.getItem().getUnlocalizedName())) { + if (matcher.matchSpec.getColour() != null && matcher.matchSpec.getColour().size() > 0) + return true; + if (matcher.matchSpec.getVariant() != null && matcher.matchSpec.getVariant().size() > 0) + return true; + } + } + + return false; + } + + private void addCollectedItemCount(ItemStack is) { + boolean variant = getVariant(is); + + if (variant) { + int prev = (collectedItems.get(is.getUnlocalizedName()) == null ? 0 + : collectedItems.get(is.getUnlocalizedName())); + collectedItems.put(is.getUnlocalizedName(), prev + is.getCount()); + } else { + int prev = (collectedItems.get(is.getItem().getUnlocalizedName()) == null ? 0 + : collectedItems.get(is.getItem().getUnlocalizedName())); + collectedItems.put(is.getItem().getUnlocalizedName(), prev + is.getCount()); + } + } + + private int getCollectedItemCount(ItemStack is) { + boolean variant = getVariant(is); + + if (variant) + return (collectedItems.get(is.getUnlocalizedName()) == null) ? 0 : collectedItems.get(is.getUnlocalizedName()); + else + return (collectedItems.get(is.getItem().getUnlocalizedName()) == null) ? 0 + : collectedItems.get(is.getItem().getUnlocalizedName()); + } + + private void checkForMatch(ItemStack is) { + int savedCollected = getCollectedItemCount(is); + if (is != null) { + for (ItemQuitMatcher matcher : this.matchers) { + if (matcher.matches(is)) { + if (savedCollected != 0) { + if (is.getCount() + savedCollected >= matcher.matchSpec.getAmount()) { + this.quitCode = matcher.description(); + this.wantToQuit = true; + } + } else if (is.getCount() >= matcher.matchSpec.getAmount()) { + this.quitCode = matcher.description(); + this.wantToQuit = true; + } + } + } + + addCollectedItemCount(is); + } + } +} diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/AgentQuitFromCraftingItemImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/AgentQuitFromCraftingItemImplementation.java new file mode 100644 index 000000000..b089626eb --- /dev/null +++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/AgentQuitFromCraftingItemImplementation.java @@ -0,0 +1,169 @@ +// -------------------------------------------------------------------------------------------------- +// Copyright (c) 2016 Microsoft Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +// associated documentation files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, publish, distribute, +// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// -------------------------------------------------------------------------------------------------- + +package com.microsoft.Malmo.MissionHandlers; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.gameevent.PlayerEvent; + +import com.microsoft.Malmo.MissionHandlerInterfaces.IWantToQuit; +import com.microsoft.Malmo.Schemas.AgentQuitFromCraftingItem; +import com.microsoft.Malmo.Schemas.BlockOrItemSpecWithDescription; +import com.microsoft.Malmo.Schemas.MissionInit; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +/** + * @author Cayden Codel, Carnegie Mellon University + *

+ * Gives agents rewards when items are crafted. Handles variants and colors. + */ +public class AgentQuitFromCraftingItemImplementation extends HandlerBase implements IWantToQuit { + private AgentQuitFromCraftingItem params; + private HashMap craftedItems; + private List matchers; + private String quitCode = ""; + private boolean wantToQuit = false; + + public static class ItemQuitMatcher extends RewardForItemBase.ItemMatcher { + String description; + + ItemQuitMatcher(BlockOrItemSpecWithDescription spec) { + super(spec); + this.description = spec.getDescription(); + } + + String description() { + return this.description; + } + } + + @Override + public boolean parseParameters(Object params) { + if (!(params instanceof AgentQuitFromCraftingItem)) + return false; + + this.params = (AgentQuitFromCraftingItem) params; + this.matchers = new ArrayList(); + for (BlockOrItemSpecWithDescription bs : this.params.getItem()) + this.matchers.add(new ItemQuitMatcher(bs)); + return true; + } + + @Override + public boolean doIWantToQuit(MissionInit missionInit) { + return this.wantToQuit; + } + + @Override + public String getOutcome() { + return this.quitCode; + } + + @Override + public void prepare(MissionInit missionInit) { + MinecraftForge.EVENT_BUS.register(this); + craftedItems = new HashMap(); + } + + @Override + public void cleanup() { + MinecraftForge.EVENT_BUS.unregister(this); + } + + @SubscribeEvent + @SideOnly(Side.CLIENT) + public void onItemCraft(PlayerEvent.ItemCraftedEvent event) { + if (event.player instanceof EntityPlayerMP && !event.crafting.isEmpty()) + checkForMatch(event.crafting); + } + + /** + * Checks whether the ItemStack matches a variant stored in the item list. If + * so, returns true, else returns false. + * + * @param is The item stack + * @return If the stack is allowed in the item matchers and has color or + * variants enabled, returns true, else false. + */ + private boolean getVariant(ItemStack is) { + for (ItemQuitMatcher matcher : matchers) { + if (matcher.allowedItemTypes.contains(is.getItem().getUnlocalizedName())) { + if (matcher.matchSpec.getColour() != null && matcher.matchSpec.getColour().size() > 0) + return true; + if (matcher.matchSpec.getVariant() != null && matcher.matchSpec.getVariant().size() > 0) + return true; + } + } + + return false; + } + + private int getCraftedItemCount(ItemStack is) { + boolean variant = getVariant(is); + + if (variant) + return (craftedItems.get(is.getUnlocalizedName()) == null) ? 0 : craftedItems.get(is.getUnlocalizedName()); + else + return (craftedItems.get(is.getItem().getUnlocalizedName()) == null) ? 0 + : craftedItems.get(is.getItem().getUnlocalizedName()); + } + + private void addCraftedItemCount(ItemStack is) { + boolean variant = getVariant(is); + + if (variant) { + int prev = (craftedItems.get(is.getUnlocalizedName()) == null ? 0 + : craftedItems.get(is.getUnlocalizedName())); + craftedItems.put(is.getUnlocalizedName(), prev + is.getCount()); + } else { + int prev = (craftedItems.get(is.getItem().getUnlocalizedName()) == null ? 0 + : craftedItems.get(is.getItem().getUnlocalizedName())); + craftedItems.put(is.getItem().getUnlocalizedName(), prev + is.getCount()); + } + } + + private void checkForMatch(ItemStack is) { + int savedCrafted = getCraftedItemCount(is); + if (is != null) { + for (ItemQuitMatcher matcher : this.matchers) { + if (matcher.matches(is)) { + if (savedCrafted != 0) { + if (is.getCount() + savedCrafted >= matcher.matchSpec.getAmount()) { + this.quitCode = matcher.description(); + this.wantToQuit = true; + } + } else if (is.getCount() >= matcher.matchSpec.getAmount()) { + this.quitCode = matcher.description(); + this.wantToQuit = true; + } + } + } + + addCraftedItemCount(is); + } + } +} diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/AgentQuitFromPossessingItemImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/AgentQuitFromPossessingItemImplementation.java new file mode 100644 index 000000000..a81f9ca8b --- /dev/null +++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/AgentQuitFromPossessingItemImplementation.java @@ -0,0 +1,230 @@ +// -------------------------------------------------------------------------------------------------- +// Copyright (c) 2016 Microsoft Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +// associated documentation files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, publish, distribute, +// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// -------------------------------------------------------------------------------------------------- + +package com.microsoft.Malmo.MissionHandlers; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import com.microsoft.Malmo.Schemas.AgentQuitFromPossessingItem; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.entity.item.ItemTossEvent; +import net.minecraftforge.event.entity.player.EntityItemPickupEvent; +import net.minecraftforge.event.entity.player.PlayerDestroyItemEvent; +import net.minecraftforge.event.world.BlockEvent.PlaceEvent; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; + +import com.microsoft.Malmo.MissionHandlerInterfaces.IWantToQuit; +import com.microsoft.Malmo.MissionHandlers.RewardForCollectingItemImplementation.GainItemEvent; +import com.microsoft.Malmo.MissionHandlers.RewardForDiscardingItemImplementation.LoseItemEvent; +import com.microsoft.Malmo.Schemas.BlockOrItemSpecWithDescription; +import com.microsoft.Malmo.Schemas.MissionInit; +import net.minecraftforge.fml.common.gameevent.PlayerEvent; + +/** + * @author Cayden Codel, Carnegie Mellon University + *

+ * Quits the mission when the agent has possessed the right amount of items. The count on the item collection is non-absolute. + *

+ * In order to quit the mission, the agent must have the requisite items in its inventory all at one time. + */ +public class AgentQuitFromPossessingItemImplementation extends HandlerBase implements IWantToQuit { + private AgentQuitFromPossessingItem params; + private HashMap possessedItems; + private List matchers; + private String quitCode = ""; + private boolean wantToQuit = false; + + public static class ItemQuitMatcher extends RewardForItemBase.ItemMatcher { + String description; + + ItemQuitMatcher(BlockOrItemSpecWithDescription spec) { + super(spec); + this.description = spec.getDescription(); + } + + String description() { + return this.description; + } + } + + @Override + public boolean parseParameters(Object params) { + if (!(params instanceof AgentQuitFromPossessingItem)) + return false; + + this.params = (AgentQuitFromPossessingItem) params; + this.matchers = new ArrayList(); + for (BlockOrItemSpecWithDescription bs : this.params.getItem()) + this.matchers.add(new ItemQuitMatcher(bs)); + return true; + } + + @Override + public boolean doIWantToQuit(MissionInit missionInit) { + return this.wantToQuit; + } + + @Override + public String getOutcome() { + return this.quitCode; + } + + @Override + public void prepare(MissionInit missionInit) { + MinecraftForge.EVENT_BUS.register(this); + possessedItems = new HashMap(); + } + + @Override + public void cleanup() { + MinecraftForge.EVENT_BUS.unregister(this); + } + + @SubscribeEvent + public void onGainItem(GainItemEvent event) { + if (event.stack != null && event.cause == 0) + checkForMatch(event.stack); + } + + @SubscribeEvent + public void onPickupItem(EntityItemPickupEvent event) { + if (event.getItem() != null && event.getEntityPlayer() instanceof EntityPlayerMP) + checkForMatch(event.getItem().getEntityItem()); + } + + @SubscribeEvent + public void onItemCraft(PlayerEvent.ItemCraftedEvent event) { + if (event.player instanceof EntityPlayerMP && !event.crafting.isEmpty()) + checkForMatch(event.crafting); + } + + @SubscribeEvent + public void onItemSmelt(PlayerEvent.ItemSmeltedEvent event) { + if (event.player instanceof EntityPlayerMP && !event.smelting.isEmpty()) + checkForMatch(event.smelting); + } + + @SubscribeEvent + public void onLoseItem(LoseItemEvent event) { + if (event.stack != null && event.cause == 0) + removeCollectedItemCount(event.stack); + } + + @SubscribeEvent + public void onDropItem(ItemTossEvent event) { + if (event.getPlayer() instanceof EntityPlayerMP) + removeCollectedItemCount(event.getEntityItem().getEntityItem()); + } + + @SubscribeEvent + public void onDestroyItem(PlayerDestroyItemEvent event) { + if (event.getEntityPlayer() instanceof EntityPlayerMP) + removeCollectedItemCount(event.getOriginal()); + } + + @SubscribeEvent + public void onBlockPlace(PlaceEvent event) { + if (!event.isCanceled() && event.getPlacedBlock() != null && event.getPlayer() instanceof EntityPlayerMP) + removeCollectedItemCount(new ItemStack(event.getPlacedBlock().getBlock())); + } + + /** + * Checks whether the ItemStack matches a variant stored in the item list. If + * so, returns true, else returns false. + * + * @param is The item stack + * @return If the stack is allowed in the item matchers and has color or + * variants enabled, returns true, else false. + */ + private boolean getVariant(ItemStack is) { + for (ItemQuitMatcher matcher : matchers) { + if (matcher.allowedItemTypes.contains(is.getItem().getUnlocalizedName())) { + if (matcher.matchSpec.getColour() != null && matcher.matchSpec.getColour().size() > 0) + return true; + if (matcher.matchSpec.getVariant() != null && matcher.matchSpec.getVariant().size() > 0) + return true; + } + } + + return false; + } + + private void addCollectedItemCount(ItemStack is) { + boolean variant = getVariant(is); + + if (variant) { + int prev = (possessedItems.get(is.getUnlocalizedName()) == null ? 0 + : possessedItems.get(is.getUnlocalizedName())); + possessedItems.put(is.getUnlocalizedName(), prev + is.getCount()); + } else { + int prev = (possessedItems.get(is.getItem().getUnlocalizedName()) == null ? 0 + : possessedItems.get(is.getItem().getUnlocalizedName())); + possessedItems.put(is.getItem().getUnlocalizedName(), prev + is.getCount()); + } + } + + private void removeCollectedItemCount(ItemStack is) { + boolean variant = getVariant(is); + + if (variant) { + int prev = (possessedItems.get(is.getUnlocalizedName()) == null ? 0 + : possessedItems.get(is.getUnlocalizedName())); + possessedItems.put(is.getUnlocalizedName(), prev - is.getCount()); + } else { + int prev = (possessedItems.get(is.getItem().getUnlocalizedName()) == null ? 0 + : possessedItems.get(is.getItem().getUnlocalizedName())); + possessedItems.put(is.getItem().getUnlocalizedName(), prev - is.getCount()); + } + } + + private int getCollectedItemCount(ItemStack is) { + boolean variant = getVariant(is); + + if (variant) + return (possessedItems.get(is.getUnlocalizedName()) == null) ? 0 : possessedItems.get(is.getUnlocalizedName()); + else + return (possessedItems.get(is.getItem().getUnlocalizedName()) == null) ? 0 + : possessedItems.get(is.getItem().getUnlocalizedName()); + } + + private void checkForMatch(ItemStack is) { + int savedCollected = getCollectedItemCount(is); + if (is != null) { + for (ItemQuitMatcher matcher : this.matchers) { + if (matcher.matches(is)) { + if (savedCollected != 0) { + if (is.getCount() + savedCollected >= matcher.matchSpec.getAmount()) { + this.quitCode = matcher.description(); + this.wantToQuit = true; + } + } else if (is.getCount() >= matcher.matchSpec.getAmount()) { + this.quitCode = matcher.description(); + this.wantToQuit = true; + } + } + } + + addCollectedItemCount(is); + } + } +} diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/AgentQuitFromSmeltingItemImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/AgentQuitFromSmeltingItemImplementation.java new file mode 100644 index 000000000..60e5fb372 --- /dev/null +++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/AgentQuitFromSmeltingItemImplementation.java @@ -0,0 +1,168 @@ +// -------------------------------------------------------------------------------------------------- +// Copyright (c) 2016 Microsoft Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +// associated documentation files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, publish, distribute, +// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// -------------------------------------------------------------------------------------------------- + +package com.microsoft.Malmo.MissionHandlers; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.gameevent.PlayerEvent; + +import com.microsoft.Malmo.MissionHandlerInterfaces.IWantToQuit; +import com.microsoft.Malmo.MissionHandlers.RewardForItemBase.ItemMatcher; +import com.microsoft.Malmo.Schemas.AgentQuitFromSmeltingItem; +import com.microsoft.Malmo.Schemas.BlockOrItemSpecWithDescription; +import com.microsoft.Malmo.Schemas.MissionInit; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +/** + * @author Cayden Codel, Carnegie Mellon University + *

+ * Gives agents rewards when items are smelted. Handles variants and colors. + */ +public class AgentQuitFromSmeltingItemImplementation extends HandlerBase implements IWantToQuit { + private AgentQuitFromSmeltingItem params; + private HashMap smeltedItems; + private List matchers; + private String quitCode = ""; + private boolean wantToQuit = false; + + public static class ItemQuitMatcher extends RewardForItemBase.ItemMatcher { + String description; + + ItemQuitMatcher(BlockOrItemSpecWithDescription spec) { + super(spec); + this.description = spec.getDescription(); + } + + String description() { + return this.description; + } + } + + @Override + public boolean parseParameters(Object params) { + if (!(params instanceof AgentQuitFromSmeltingItem)) + return false; + + this.params = (AgentQuitFromSmeltingItem) params; + this.matchers = new ArrayList(); + for (BlockOrItemSpecWithDescription bs : this.params.getItem()) + this.matchers.add(new ItemQuitMatcher(bs)); + return true; + } + + @Override + public boolean doIWantToQuit(MissionInit missionInit) { + return this.wantToQuit; + } + + @Override + public String getOutcome() { + return this.quitCode; + } + + @Override + public void prepare(MissionInit missionInit) { + MinecraftForge.EVENT_BUS.register(this); + smeltedItems = new HashMap(); + } + + @Override + public void cleanup() { + MinecraftForge.EVENT_BUS.unregister(this); + } + + @SubscribeEvent + @SideOnly(Side.CLIENT) + public void onItemSmelt(PlayerEvent.ItemSmeltedEvent event) { + if (event.player instanceof EntityPlayerMP && !event.smelting.isEmpty()) + checkForMatch(event.smelting); + } + + /** + * Checks whether the ItemStack matches a variant stored in the item list. If + * so, returns true, else returns false. + * + * @param is The item stack + * @return If the stack is allowed in the item matchers and has color or + * variants enabled, returns true, else false. + */ + private boolean getVariant(ItemStack is) { + for (ItemMatcher matcher : matchers) { + if (matcher.allowedItemTypes.contains(is.getItem().getUnlocalizedName())) { + if (matcher.matchSpec.getColour() != null && matcher.matchSpec.getColour().size() > 0) + return true; + if (matcher.matchSpec.getVariant() != null && matcher.matchSpec.getVariant().size() > 0) + return true; + } + } + + return false; + } + + private int getSmeltedItemCount(ItemStack is) { + boolean variant = getVariant(is); + + if (variant) + return (smeltedItems.get(is.getUnlocalizedName()) == null) ? 0 : smeltedItems.get(is.getUnlocalizedName()); + else + return (smeltedItems.get(is.getItem().getUnlocalizedName()) == null) ? 0 + : smeltedItems.get(is.getItem().getUnlocalizedName()); + } + + private void addSmeltedItemCount(ItemStack is) { + boolean variant = getVariant(is); + + if (variant) { + int prev = (smeltedItems.get(is.getUnlocalizedName()) == null ? 0 + : smeltedItems.get(is.getUnlocalizedName())); + smeltedItems.put(is.getUnlocalizedName(), prev + is.getCount()); + } else { + int prev = (smeltedItems.get(is.getItem().getUnlocalizedName()) == null ? 0 + : smeltedItems.get(is.getItem().getUnlocalizedName())); + smeltedItems.put(is.getItem().getUnlocalizedName(), prev + is.getCount()); + } + } + + private void checkForMatch(ItemStack is) { + int savedSmelted = getSmeltedItemCount(is); + for (ItemQuitMatcher matcher : this.matchers) { + if (matcher.matches(is)) { + if (savedSmelted != 0) { + if (is.getCount() + savedSmelted >= matcher.matchSpec.getAmount()) { + this.quitCode = matcher.description(); + this.wantToQuit = true; + } + } else if (is.getCount() >= matcher.matchSpec.getAmount()) { + this.quitCode = matcher.description(); + this.wantToQuit = true; + } + } + } + + addSmeltedItemCount(is); + } +} diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/NavigationDecoratorImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/NavigationDecoratorImplementation.java index 6fd351032..791b898ad 100644 --- a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/NavigationDecoratorImplementation.java +++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/NavigationDecoratorImplementation.java @@ -21,7 +21,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; - import com.microsoft.Malmo.MissionHandlerInterfaces.IWorldDecorator; import com.microsoft.Malmo.Schemas.MissionInit; import com.microsoft.Malmo.Schemas.NavigationDecorator; @@ -35,7 +34,7 @@ /** * Creates a decorator that sets a random block and then points all compasses * towards the block. - * + * * @author Cayden Codel, Carnegie Mellon University * */ @@ -47,6 +46,7 @@ public class NavigationDecoratorImplementation extends HandlerBase implements IW private double placementX, placementY, placementZ; private double radius; private double minDist, maxDist; + private double minRad, maxRad; @Override public boolean parseParameters(Object params) { @@ -62,29 +62,27 @@ public void buildOnWorld(MissionInit missionInit, World world) throws DecoratorE originX = nparams.getRandomPlacementProperties().getOrigin().getX().doubleValue(); else originX = world.getSpawnPoint().getX(); - if (nparams.getRandomPlacementProperties().getOrigin() != null) originY = nparams.getRandomPlacementProperties().getOrigin().getY().doubleValue(); else originY = world.getSpawnPoint().getY(); - if (nparams.getRandomPlacementProperties().getOrigin() != null) originZ = nparams.getRandomPlacementProperties().getOrigin().getZ().doubleValue(); else originZ = world.getSpawnPoint().getZ(); - radius = nparams.getRandomPlacementProperties().getRadius().doubleValue(); + maxRad = nparams.getRandomPlacementProperties().getMaxRadius().doubleValue(); + minRad = nparams.getRandomPlacementProperties().getMinRadius().doubleValue(); + radius = (int) (Math.random() * (maxRad - minRad) + minRad); + minDist = nparams.getMinRandomizedDistance().doubleValue(); maxDist = nparams.getMaxRandomizedDistance().doubleValue(); - placementX = 0; placementY = 0; placementZ = 0; - if (nparams.getRandomPlacementProperties().getPlacement().equals("surface")) { placementX = ((Math.random() - 0.5) * 2 * radius); placementZ = (Math.random() > 0.5 ? -1 : 1) * Math.sqrt((radius * radius) - (placementX * placementX)); - // Change center to origin now placementX += originX; placementZ += originZ; @@ -93,7 +91,6 @@ public void buildOnWorld(MissionInit missionInit, World world) throws DecoratorE placementX = ((Math.random() - 0.5) * 2 * radius); placementY = originY; placementZ = (Math.random() > 0.5 ? -1 : 1) * Math.sqrt((radius * radius) - (placementX * placementX)); - // Change center to origin now placementX += originX; placementZ += originZ; @@ -102,17 +99,14 @@ public void buildOnWorld(MissionInit missionInit, World world) throws DecoratorE placementY = (Math.random() - 0.5) * 2 * Math.sqrt((radius * radius) - (placementX * placementX)); placementZ = (Math.random() > 0.5 ? -1 : 1) * Math.sqrt((radius * radius) - (placementX * placementX) - (placementY * placementY)); - // Change center to origin now placementX += originX; placementY += originY; placementZ += originZ; } - IBlockState state = MinecraftTypeHelper .ParseBlockType(nparams.getRandomPlacementProperties().getBlock().value()); world.setBlockState(new BlockPos(placementX, placementY, placementZ), state); - // Set compass location to the block double xDel = 0, zDel = 0; if (nparams.isRandomizeCompassLocation()) { @@ -123,7 +117,6 @@ public void buildOnWorld(MissionInit missionInit, World world) throws DecoratorE dist = Math.sqrt(xDel * xDel + zDel * zDel); } while (dist <= maxDist && dist >= minDist); } - placementX += xDel; placementZ += zDel; } @@ -159,4 +152,4 @@ public boolean targetedUpdate(String nextAgentName) { @Override public void getTurnParticipants(ArrayList participants, ArrayList participantSlots) { } -} +} \ No newline at end of file diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/NearbyCraftCommandsImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/NearbyCraftCommandsImplementation.java new file mode 100644 index 000000000..cda7bbf85 --- /dev/null +++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/NearbyCraftCommandsImplementation.java @@ -0,0 +1,192 @@ +// -------------------------------------------------------------------------------------------------- +// Copyright (c) 2016 Microsoft Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +// associated documentation files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, publish, distribute, +// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// -------------------------------------------------------------------------------------------------- + +package com.microsoft.Malmo.MissionHandlers; + +import com.microsoft.Malmo.Schemas.NearbyCraftCommand; +import io.netty.buffer.ByteBuf; + +import java.util.ArrayList; +import java.util.List; + +import net.minecraft.block.BlockWorkbench; +import net.minecraft.client.Minecraft; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.crafting.IRecipe; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.world.BlockEvent; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.network.ByteBufUtils; +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.MissionInit; +import com.microsoft.Malmo.Schemas.NearbyCraftCommands; +import com.microsoft.Malmo.Utils.CraftingHelper; + +/** + * @author Cayden Codel, Carnegie Mellon University + *

+ * Extends the functionality of the SimpleCraftCommands by requiring a crafting table close-by. Only handles crafting, no smelting. + */ +public class NearbyCraftCommandsImplementation extends CommandBase { + private boolean isOverriding; + private static ArrayList craftingTables; + + public static class CraftNearbyMessage implements IMessage { + String parameters; + + public CraftNearbyMessage() { + } + + public CraftNearbyMessage(String parameters) { + this.parameters = parameters; + } + + @Override + public void fromBytes(ByteBuf buf) { + this.parameters = ByteBufUtils.readUTF8String(buf); + } + + @Override + public void toBytes(ByteBuf buf) { + ByteBufUtils.writeUTF8String(buf, this.parameters); + } + } + + @SubscribeEvent + public void onBlockPlace(BlockEvent.PlaceEvent event) { + if (!event.isCanceled() && event.getPlacedBlock().getBlock() instanceof BlockWorkbench) + craftingTables.add(event.getPos()); + } + + @SubscribeEvent + public void onBlockDestroy(BlockEvent.BreakEvent event) { + if (!event.isCanceled() && event.getState().getBlock() instanceof BlockWorkbench) + for (int i = craftingTables.size() - 1; i >= 0; i--) + if (craftingTables.get(i).equals(event.getPos())) + craftingTables.remove(i); + } + + public static class CraftNearbyMessageHandler implements IMessageHandler { + @Override + public IMessage onMessage(CraftNearbyMessage message, MessageContext ctx) { + EntityPlayerMP player = ctx.getServerHandler().playerEntity; + Vec3d headPos = new Vec3d(player.posX, player.posY + 1.6, player.posZ); + + // Location checking + boolean closeTable = false; + for (BlockPos furnace : craftingTables) { + Vec3d blockVec = new Vec3d(furnace.getX() + 0.5, furnace.getY() + 0.5, furnace.getZ() + 0.5); + + if (headPos.squareDistanceTo(blockVec) <= 25.0) { + // Within a reasonable FOV? + // Lots of trig, let's go + double fov = Minecraft.getMinecraft().gameSettings.fovSetting; + double height = Minecraft.getMinecraft().displayHeight; + double width = Minecraft.getMinecraft().displayWidth; + Vec3d lookVec = player.getLookVec(); + Vec3d toBlock = blockVec.subtract(headPos); + + // Projection of block onto player look vector - if greater than 0, then in front of us + double scalarProjection = lookVec.dotProduct(toBlock) / lookVec.lengthVector(); + if (scalarProjection > 0) { + Vec3d yUnit = new Vec3d(0, 1.0, 0); + Vec3d lookCross = lookVec.crossProduct(yUnit); + Vec3d blockProjectedOntoCross = lookCross.scale(lookCross.dotProduct(toBlock) / lookCross.lengthVector()); + Vec3d blockProjectedOntoPlayerPlane = toBlock.subtract(blockProjectedOntoCross); + double xyDot = lookVec.dotProduct(blockProjectedOntoPlayerPlane); + double pitchTheta = Math.acos(xyDot / (lookVec.lengthVector() * blockProjectedOntoPlayerPlane.lengthVector())); + + Vec3d playerY = lookCross.crossProduct(lookVec); + Vec3d blockProjectedOntoPlayerY = playerY.scale(playerY.dotProduct(toBlock) / playerY.lengthVector()); + Vec3d blockProjectedOntoYawPlane = toBlock.subtract(blockProjectedOntoPlayerY); + double xzDot = lookVec.dotProduct(blockProjectedOntoYawPlane); + double yawTheta = Math.acos(xzDot / (lookVec.lengthVector() * blockProjectedOntoYawPlane.lengthVector())); + + if (Math.abs(Math.toDegrees(yawTheta)) <= Math.min(1, width / height) * (fov / 2.0) && Math.abs(Math.toDegrees(pitchTheta)) <= Math.min(1, height / width) * (fov / 2.0)) + closeTable = true; + } + } + } + + if (closeTable) { + // We are close enough, try crafting recipes + List matching_recipes; + String[] split = message.parameters.split(" "); + if (split.length > 1) + matching_recipes = CraftingHelper.getRecipesForRequestedOutput(message.parameters, true); + else + matching_recipes = CraftingHelper.getRecipesForRequestedOutput(message.parameters, false); + + for (IRecipe recipe : matching_recipes) + if (CraftingHelper.attemptCrafting(player, recipe)) + return null; + } + + return null; + } + } + + @Override + protected boolean onExecute(String verb, String parameter, MissionInit missionInit) { + if (verb.equalsIgnoreCase(NearbyCraftCommand.CRAFT_NEARBY.value())) { + MalmoMod.network.sendToServer(new CraftNearbyMessage(parameter)); + return true; + } + return false; + } + + @Override + public boolean parseParameters(Object params) { + if (!(params instanceof NearbyCraftCommands)) + return false; + + craftingTables = new ArrayList(); + + NearbyCraftCommands cParams = (NearbyCraftCommands) params; + setUpAllowAndDenyLists(cParams.getModifierList()); + return true; + } + + @Override + public void install(MissionInit missionInit) { + CraftingHelper.reset(); + MinecraftForge.EVENT_BUS.register(this); + } + + @Override + public void deinstall(MissionInit missionInit) { + MinecraftForge.EVENT_BUS.unregister(this); + } + + @Override + public boolean isOverriding() { + return this.isOverriding; + } + + @Override + public void setOverriding(boolean b) { + this.isOverriding = b; + } +} \ No newline at end of file diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/NearbySmeltCommandsImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/NearbySmeltCommandsImplementation.java new file mode 100644 index 000000000..6a29b83c0 --- /dev/null +++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/NearbySmeltCommandsImplementation.java @@ -0,0 +1,184 @@ +// -------------------------------------------------------------------------------------------------- +// Copyright (c) 2016 Microsoft Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +// associated documentation files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, publish, distribute, +// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// -------------------------------------------------------------------------------------------------- + +package com.microsoft.Malmo.MissionHandlers; + +import io.netty.buffer.ByteBuf; + +import java.util.ArrayList; + +import net.minecraft.block.BlockFurnace; +import net.minecraft.client.Minecraft; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.world.BlockEvent; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.network.ByteBufUtils; +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.MissionInit; +import com.microsoft.Malmo.Schemas.NearbySmeltCommand; +import com.microsoft.Malmo.Schemas.NearbySmeltCommands; +import com.microsoft.Malmo.Utils.CraftingHelper; + +/** + * @author Cayden Codel, Carnegie Mellon University + *

+ * Extends the functionality of the SimpleCraftCommands by requiring a furnace close-by. Only handles smelting, no crafting. + */ +public class NearbySmeltCommandsImplementation extends CommandBase { + private boolean isOverriding; + private static ArrayList furnaces; + + public static class SmeltNearbyMessage implements IMessage { + String parameters; + + public SmeltNearbyMessage() { + } + + public SmeltNearbyMessage(String parameters) { + this.parameters = parameters; + } + + @Override + public void fromBytes(ByteBuf buf) { + this.parameters = ByteBufUtils.readUTF8String(buf); + } + + @Override + public void toBytes(ByteBuf buf) { + ByteBufUtils.writeUTF8String(buf, this.parameters); + } + } + + @SubscribeEvent + public void onBlockPlace(BlockEvent.PlaceEvent event) { + if (!event.isCanceled() && event.getPlacedBlock().getBlock() instanceof BlockFurnace) + furnaces.add(event.getPos()); + } + + @SubscribeEvent + public void onBlockDestroy(BlockEvent.BreakEvent event) { + if (!event.isCanceled() && event.getState().getBlock() instanceof BlockFurnace) + for (int i = furnaces.size() - 1; i >= 0; i--) + if (furnaces.get(i).equals(event.getPos())) + furnaces.remove(i); + } + + public static class SmeltNearbyMessageHandler implements IMessageHandler { + @Override + public IMessage onMessage(SmeltNearbyMessage message, MessageContext ctx) { + EntityPlayerMP player = ctx.getServerHandler().playerEntity; + Vec3d headPos = new Vec3d(player.posX, player.posY + 1.6, player.posZ); + + // Location checking + boolean closeFurnace = false; + for (BlockPos furnace : furnaces) { + Vec3d blockVec = new Vec3d(furnace.getX() + 0.5, furnace.getY() + 0.5, furnace.getZ() + 0.5); + + if (headPos.squareDistanceTo(blockVec) <= 25.0) { + // Within a reasonable FOV? + // Lots of trig, let's go + double fov = Minecraft.getMinecraft().gameSettings.fovSetting; + double height = Minecraft.getMinecraft().displayHeight; + double width = Minecraft.getMinecraft().displayWidth; + Vec3d lookVec = player.getLookVec(); + Vec3d toBlock = blockVec.subtract(headPos); + + // Projection of block onto player look vector - if greater than 0, then in front of us + double scalarProjection = lookVec.dotProduct(toBlock) / lookVec.lengthVector(); + if (scalarProjection > 0) { + Vec3d yUnit = new Vec3d(0, 1.0, 0); + Vec3d lookCross = lookVec.crossProduct(yUnit); + Vec3d blockProjectedOntoCross = lookCross.scale(lookCross.dotProduct(toBlock) / lookCross.lengthVector()); + Vec3d blockProjectedOntoPlayerPlane = toBlock.subtract(blockProjectedOntoCross); + double xyDot = lookVec.dotProduct(blockProjectedOntoPlayerPlane); + double pitchTheta = Math.acos(xyDot / (lookVec.lengthVector() * blockProjectedOntoPlayerPlane.lengthVector())); + + Vec3d playerY = lookCross.crossProduct(lookVec); + Vec3d blockProjectedOntoPlayerY = playerY.scale(playerY.dotProduct(toBlock) / playerY.lengthVector()); + Vec3d blockProjectedOntoYawPlane = toBlock.subtract(blockProjectedOntoPlayerY); + double xzDot = lookVec.dotProduct(blockProjectedOntoYawPlane); + double yawTheta = Math.acos(xzDot / (lookVec.lengthVector() * blockProjectedOntoYawPlane.lengthVector())); + + if (Math.abs(Math.toDegrees(yawTheta)) <= Math.min(1, width / height) * (fov / 2.0) && Math.abs(Math.toDegrees(pitchTheta)) <= Math.min(1, height / width) * (fov / 2.0)) + closeFurnace = true; + } + } + } + + if (closeFurnace) { + ItemStack input = CraftingHelper.getSmeltingRecipeForRequestedOutput(message.parameters); + if (input != null) + if (CraftingHelper.attemptSmelting(player, input)) + return null; + } + + return null; + } + } + + @Override + protected boolean onExecute(String verb, String parameter, MissionInit missionInit) { + if (verb.equalsIgnoreCase(NearbySmeltCommand.SMELT_NEARBY.value())) { + MalmoMod.network.sendToServer(new SmeltNearbyMessage(parameter)); + return true; + } + return false; + } + + @Override + public boolean parseParameters(Object params) { + furnaces = new ArrayList(); + + if (!(params instanceof NearbySmeltCommands)) + return false; + + NearbySmeltCommands cParams = (NearbySmeltCommands) params; + setUpAllowAndDenyLists(cParams.getModifierList()); + return true; + } + + @Override + public void install(MissionInit missionInit) { + CraftingHelper.reset(); + MinecraftForge.EVENT_BUS.register(this); + } + + @Override + public void deinstall(MissionInit missionInit) { + MinecraftForge.EVENT_BUS.unregister(this); + } + + @Override + public boolean isOverriding() { + return this.isOverriding; + } + + @Override + public void setOverriding(boolean b) { + this.isOverriding = b; + } +} \ No newline at end of file diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/PlaceCommandsImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/PlaceCommandsImplementation.java new file mode 100644 index 000000000..de815c25d --- /dev/null +++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/PlaceCommandsImplementation.java @@ -0,0 +1,120 @@ +// -------------------------------------------------------------------------------------------------- +// Copyright (c) 2016 Microsoft Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +// associated documentation files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, publish, distribute, +// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// -------------------------------------------------------------------------------------------------- + +package com.microsoft.Malmo.MissionHandlers; + +import com.microsoft.Malmo.MalmoMod; +import com.microsoft.Malmo.Schemas.*; +import com.microsoft.Malmo.Schemas.MissionInit; +import net.minecraft.block.Block; +import net.minecraft.client.Minecraft; +import net.minecraft.client.entity.EntityPlayerSP; + +import com.microsoft.Malmo.MissionHandlerInterfaces.ICommandHandler; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.RayTraceResult; + +/** + * @author Cayden Codel, Carnegie Mellon University + *

+ * Place commands allow agents to place blocks in the world without having to worry about inventory management. + */ +public class PlaceCommandsImplementation extends CommandBase implements ICommandHandler { + private boolean isOverriding; + + @Override + protected boolean onExecute(String verb, String parameter, MissionInit missionInit) { + EntityPlayerSP player = Minecraft.getMinecraft().player; + if (player == null) + return false; + + if (!verb.equalsIgnoreCase("place")) + return false; + + Item item = Item.getByNameOrId(parameter); + Block block = Block.getBlockFromItem(item); + if (item == null || item.getRegistryName() == null || block.getRegistryName() == null) + return false; + + InventoryPlayer inv = player.inventory; + boolean blockInInventory = false; + ItemStack stackInInventory = null; + int stackIndex = -1; + for (int i = 0; !blockInInventory && i < inv.getSizeInventory(); i++) { + Item stack = inv.getStackInSlot(i).getItem(); + if (stack.getRegistryName() != null && stack.getRegistryName().equals(item.getRegistryName())) { + stackInInventory = inv.getStackInSlot(i); + stackIndex = i; + blockInInventory = true; + } + } + + // We don't have that item in our inventories + if (!blockInInventory) + return false; + + RayTraceResult mop = Minecraft.getMinecraft().objectMouseOver; + if (mop.typeOfHit == RayTraceResult.Type.BLOCK) { + BlockPos pos = mop.getBlockPos().add(mop.sideHit.getDirectionVec()); + // Can we place this block here? + AxisAlignedBB axisalignedbb = block.getDefaultState().getCollisionBoundingBox(player.world, pos); + if (axisalignedbb == null || player.world.checkNoEntityCollision(axisalignedbb.offset(pos), null)) { + MalmoMod.network.sendToServer(new DiscreteMovementCommandsImplementation.UseActionMessage(mop.getBlockPos(), new ItemStack(block), mop.sideHit, false, mop.hitVec)); + if (stackInInventory.getCount() == 1) + inv.setInventorySlotContents(stackIndex, new ItemStack(Block.getBlockById(0))); + else + stackInInventory.setCount(stackInInventory.getCount() - 1); + } + } + + return true; + } + + @Override + public boolean parseParameters(Object params) { + if (!(params instanceof PlaceCommands)) + return false; + + PlaceCommands pParams = (PlaceCommands) params; + setUpAllowAndDenyLists(pParams.getModifierList()); + return true; + } + + @Override + public void install(MissionInit missionInit) { + } + + @Override + public void deinstall(MissionInit missionInit) { + } + + @Override + public boolean isOverriding() { + return this.isOverriding; + } + + @Override + public void setOverriding(boolean b) { + this.isOverriding = b; + } +} diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForCollectingItemImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForCollectingItemImplementation.java index 6907e56d2..25137240a 100755 --- a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForCollectingItemImplementation.java +++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForCollectingItemImplementation.java @@ -62,14 +62,21 @@ public void onMessage(MalmoMessageType messageType, Map data) } } - public static class GainItemEvent extends Event - { + public static class GainItemEvent extends Event { public final ItemStack stack; - public GainItemEvent(ItemStack stack) - { + /** + * Sets the cause of the GainItemEvent. By default, is 0. If it is from auto-crafting, then is 1. If it is from auto-smelting, then is 2. + */ + public int cause = 0; + + public GainItemEvent(ItemStack stack) { this.stack = stack; } + + public void setCause(int cause) { + this.cause = cause; + } } @Override diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForCollectingItemQuantityImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForCollectingItemQuantityImplementation.java new file mode 100644 index 000000000..435bb36fe --- /dev/null +++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForCollectingItemQuantityImplementation.java @@ -0,0 +1,195 @@ +// -------------------------------------------------------------------------------------------------- +// Copyright (c) 2016 Microsoft Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +// associated documentation files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, publish, distribute, +// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// -------------------------------------------------------------------------------------------------- + +package com.microsoft.Malmo.MissionHandlers; + +import java.util.ArrayList; +import java.util.HashMap; + +import com.microsoft.Malmo.MissionHandlerInterfaces.IRewardProducer; +import com.microsoft.Malmo.Schemas.BlockOrItemSpecWithReward; +import com.microsoft.Malmo.Schemas.MissionInit; +import com.microsoft.Malmo.Schemas.RewardForCollectingItemQuantity; + +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.entity.player.EntityItemPickupEvent; +import net.minecraftforge.fml.common.eventhandler.Event; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.gameevent.PlayerEvent; + +/** + * @author Cayden Codel, Carnegie Mellon University + *

+ * Sends a reward when the agent collected the specified item with + * specified amounts. Counter is absolute. + */ +public class RewardForCollectingItemQuantityImplementation extends RewardForItemBase implements IRewardProducer { + private RewardForCollectingItemQuantity params; + private ArrayList matchers; + private HashMap collectedItems; + + @SubscribeEvent + public void onGainItem(RewardForCollectingItemImplementation.GainItemEvent event) { + if (event.stack != null && event.cause == 0) + checkForMatch(event.stack); + } + + @SubscribeEvent + public void onPickupItem(EntityItemPickupEvent event) { + if (event.getItem() != null && event.getEntityPlayer() instanceof EntityPlayerMP) + checkForMatch(event.getItem().getEntityItem()); + } + + @SubscribeEvent + public void onItemCraft(PlayerEvent.ItemCraftedEvent event) { + if (event.player instanceof EntityPlayerMP && !event.crafting.isEmpty()) + checkForMatch(event.crafting); + } + + @SubscribeEvent + public void onItemSmelt(PlayerEvent.ItemSmeltedEvent event) { + if (event.player instanceof EntityPlayerMP && !event.smelting.isEmpty()) + checkForMatch(event.smelting); + } + + /** + * Checks whether the ItemStack matches a variant stored in the item list. If + * so, returns true, else returns false. + * + * @param is The item stack + * @return If the stack is allowed in the item matchers and has color or + * variants enabled, returns true, else false. + */ + private boolean getVariant(ItemStack is) { + for (ItemMatcher matcher : matchers) { + if (matcher.allowedItemTypes.contains(is.getItem().getUnlocalizedName())) { + if (matcher.matchSpec.getColour() != null && matcher.matchSpec.getColour().size() > 0) + return true; + if (matcher.matchSpec.getVariant() != null && matcher.matchSpec.getVariant().size() > 0) + return true; + } + } + + return false; + } + + private int getCollectedItemCount(ItemStack is) { + boolean variant = getVariant(is); + + if (variant) + return (collectedItems.get(is.getUnlocalizedName()) == null) ? 0 : collectedItems.get(is.getUnlocalizedName()); + else + return (collectedItems.get(is.getItem().getUnlocalizedName()) == null) ? 0 + : collectedItems.get(is.getItem().getUnlocalizedName()); + } + + private void addCollectedItemCount(ItemStack is) { + boolean variant = getVariant(is); + + if (variant) { + int prev = (collectedItems.get(is.getUnlocalizedName()) == null ? 0 + : collectedItems.get(is.getUnlocalizedName())); + collectedItems.put(is.getUnlocalizedName(), prev + is.getCount()); + } else { + int prev = (collectedItems.get(is.getItem().getUnlocalizedName()) == null ? 0 + : collectedItems.get(is.getItem().getUnlocalizedName())); + collectedItems.put(is.getItem().getUnlocalizedName(), prev + is.getCount()); + } + } + + private void checkForMatch(ItemStack is) { + if (is != null) { + for (ItemMatcher matcher : this.matchers) { + int savedCollected = getCollectedItemCount(is) % matcher.matchSpec.getAmount(); + if (matcher.matches(is)) { + if (!params.isSparse()) { + if (savedCollected != 0 && savedCollected < matcher.matchSpec.getAmount()) { + for (int i = savedCollected; i < matcher.matchSpec.getAmount() + && i - savedCollected < is.getCount(); i++) { + int dimension = params.getDimension(); + float adjusted_reward = this.adjustAndDistributeReward( + ((BlockOrItemSpecWithReward) matcher.matchSpec).getReward().floatValue(), + params.getDimension(), + ((BlockOrItemSpecWithReward) matcher.matchSpec).getDistribution()); + addCachedReward(dimension, adjusted_reward); + } + } else if (true || savedCollected == 0) { + for (int i = 0; i < is.getCount() && i < matcher.matchSpec.getAmount(); i++) { + int dimension = params.getDimension(); + float adjusted_reward = this.adjustAndDistributeReward( + ((BlockOrItemSpecWithReward) matcher.matchSpec).getReward().floatValue(), + params.getDimension(), + ((BlockOrItemSpecWithReward) matcher.matchSpec).getDistribution()); + addCachedReward(dimension, adjusted_reward); + } + } + } else { + System.out.println("savedCollected " + savedCollected + " amount " + matcher.matchSpec.getAmount() + " count " + is.getCount()); + + if (savedCollected < matcher.matchSpec.getAmount() + && savedCollected + is.getCount() >= matcher.matchSpec.getAmount()) { + int dimension = params.getDimension(); + float adjusted_reward = this.adjustAndDistributeReward( + ((BlockOrItemSpecWithReward) matcher.matchSpec).getReward().floatValue(), + params.getDimension(), + ((BlockOrItemSpecWithReward) matcher.matchSpec).getDistribution()); + addCachedReward(dimension, adjusted_reward); + } + } + } + } + + addCollectedItemCount(is); + } + } + + @Override + public boolean parseParameters(Object params) { + if (!(params instanceof RewardForCollectingItemQuantity)) + return false; + + matchers = new ArrayList(); + + this.params = (RewardForCollectingItemQuantity) params; + for (BlockOrItemSpecWithReward spec : this.params.getItem()) + this.matchers.add(new ItemMatcher(spec)); + + return true; + } + + @Override + public void prepare(MissionInit missionInit) { + super.prepare(missionInit); + MinecraftForge.EVENT_BUS.register(this); + collectedItems = new HashMap(); + } + + @Override + public void getReward(MissionInit missionInit, MultidimensionalReward reward) { + super.getReward(missionInit, reward); + } + + @Override + public void cleanup() { + super.cleanup(); + MinecraftForge.EVENT_BUS.unregister(this); + } +} diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForCraftingItemImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForCraftingItemImplementation.java new file mode 100644 index 000000000..aa845b816 --- /dev/null +++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForCraftingItemImplementation.java @@ -0,0 +1,173 @@ +// -------------------------------------------------------------------------------------------------- +// Copyright (c) 2016 Microsoft Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +// associated documentation files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, publish, distribute, +// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// -------------------------------------------------------------------------------------------------- + +package com.microsoft.Malmo.MissionHandlers; + +import java.util.ArrayList; +import java.util.HashMap; + +import com.microsoft.Malmo.MissionHandlerInterfaces.IRewardProducer; +import com.microsoft.Malmo.Schemas.BlockOrItemSpecWithReward; +import com.microsoft.Malmo.Schemas.MissionInit; +import com.microsoft.Malmo.Schemas.RewardForCraftingItem; + +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.gameevent.PlayerEvent; + +/** + * @author Cayden Codel, Carnegie Mellon University + *

+ * Sends a reward when the agent crafts the specified item with + * specified amounts. + */ +public class RewardForCraftingItemImplementation extends RewardForItemBase implements IRewardProducer { + private RewardForCraftingItem params; + private ArrayList matchers; + private HashMap craftedItems; + + @SubscribeEvent + public void onItemCraft(PlayerEvent.ItemCraftedEvent event) { + if (event.player instanceof EntityPlayerMP && !event.crafting.isEmpty()) + checkForMatch(event.crafting); + } + + /** + * Checks whether the ItemStack matches a variant stored in the item list. If + * so, returns true, else returns false. + * + * @param is The item stack + * @return If the stack is allowed in the item matchers and has color or + * variants enabled, returns true, else false. + */ + private boolean getVariant(ItemStack is) { + for (ItemMatcher matcher : matchers) { + if (matcher.allowedItemTypes.contains(is.getItem().getUnlocalizedName())) { + if (matcher.matchSpec.getColour() != null && matcher.matchSpec.getColour().size() > 0) + return true; + if (matcher.matchSpec.getVariant() != null && matcher.matchSpec.getVariant().size() > 0) + return true; + } + } + + return false; + } + + private int getCraftedItemCount(ItemStack is) { + boolean variant = getVariant(is); + + if (variant) + return (craftedItems.get(is.getUnlocalizedName()) == null) ? 0 : craftedItems.get(is.getUnlocalizedName()); + else + return (craftedItems.get(is.getItem().getUnlocalizedName()) == null) ? 0 + : craftedItems.get(is.getItem().getUnlocalizedName()); + } + + private void addCraftedItemCount(ItemStack is) { + boolean variant = getVariant(is); + + if (variant) { + int prev = (craftedItems.get(is.getUnlocalizedName()) == null ? 0 + : craftedItems.get(is.getUnlocalizedName())); + craftedItems.put(is.getUnlocalizedName(), prev + is.getCount()); + } else { + int prev = (craftedItems.get(is.getItem().getUnlocalizedName()) == null ? 0 + : craftedItems.get(is.getItem().getUnlocalizedName())); + craftedItems.put(is.getItem().getUnlocalizedName(), prev + is.getCount()); + } + } + + private void checkForMatch(ItemStack is) { + int savedCrafted = getCraftedItemCount(is); + if (is != null) { + for (ItemMatcher matcher : this.matchers) { + if (matcher.matches(is)) { + if (!params.isSparse()) { + if (savedCrafted != 0 && savedCrafted < matcher.matchSpec.getAmount()) { + for (int i = savedCrafted; i < matcher.matchSpec.getAmount() + && i - savedCrafted < is.getCount(); i++) { + int dimension = params.getDimension(); + float adjusted_reward = this.adjustAndDistributeReward( + ((BlockOrItemSpecWithReward) matcher.matchSpec).getReward().floatValue(), + params.getDimension(), + ((BlockOrItemSpecWithReward) matcher.matchSpec).getDistribution()); + addCachedReward(dimension, adjusted_reward); + } + } else if (savedCrafted == 0) { + for (int i = 0; i < is.getCount() && i < matcher.matchSpec.getAmount(); i++) { + int dimension = params.getDimension(); + float adjusted_reward = this.adjustAndDistributeReward( + ((BlockOrItemSpecWithReward) matcher.matchSpec).getReward().floatValue(), + params.getDimension(), + ((BlockOrItemSpecWithReward) matcher.matchSpec).getDistribution()); + addCachedReward(dimension, adjusted_reward); + } + } + } else { + if (savedCrafted < matcher.matchSpec.getAmount() + && savedCrafted + is.getCount() >= matcher.matchSpec.getAmount()) { + int dimension = params.getDimension(); + float adjusted_reward = this.adjustAndDistributeReward( + ((BlockOrItemSpecWithReward) matcher.matchSpec).getReward().floatValue(), + params.getDimension(), + ((BlockOrItemSpecWithReward) matcher.matchSpec).getDistribution()); + addCachedReward(dimension, adjusted_reward); + } + } + } + } + + addCraftedItemCount(is); + } + } + + @Override + public boolean parseParameters(Object params) { + if (!(params instanceof RewardForCraftingItem)) + return false; + + matchers = new ArrayList(); + + this.params = (RewardForCraftingItem) params; + for (BlockOrItemSpecWithReward spec : this.params.getItem()) + this.matchers.add(new ItemMatcher(spec)); + + return true; + } + + @Override + public void prepare(MissionInit missionInit) { + super.prepare(missionInit); + MinecraftForge.EVENT_BUS.register(this); + craftedItems = new HashMap(); + } + + @Override + public void getReward(MissionInit missionInit, MultidimensionalReward reward) { + super.getReward(missionInit, reward); + } + + @Override + public void cleanup() { + super.cleanup(); + MinecraftForge.EVENT_BUS.unregister(this); + } +} diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForDiscardingItemImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForDiscardingItemImplementation.java index a5a8cefb8..fc9e934d4 100755 --- a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForDiscardingItemImplementation.java +++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForDiscardingItemImplementation.java @@ -67,10 +67,19 @@ public static class LoseItemEvent extends Event { public final ItemStack stack; + /** + * Sets the cause of the LoseItemEvent. By default, is 0. If it is from auto-crafting, then is 1. If it is from auto-smelting, then is 2. + */ + public int cause = 0; + public LoseItemEvent(ItemStack stack) { this.stack = stack; } + + public void setCause(int cause) { + this.cause = cause; + } } @Override diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForPossessingItemImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForPossessingItemImplementation.java new file mode 100644 index 000000000..7c6e9cd8d --- /dev/null +++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForPossessingItemImplementation.java @@ -0,0 +1,279 @@ +// -------------------------------------------------------------------------------------------------- +// Copyright (c) 2016 Microsoft Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +// associated documentation files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, publish, distribute, +// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// -------------------------------------------------------------------------------------------------- + +package com.microsoft.Malmo.MissionHandlers; + +import java.util.ArrayList; +import java.util.HashMap; + +import com.microsoft.Malmo.MissionHandlerInterfaces.IRewardProducer; +import com.microsoft.Malmo.Schemas.BlockOrItemSpecWithReward; +import com.microsoft.Malmo.Schemas.MissionInit; +import com.microsoft.Malmo.MissionHandlers.RewardForDiscardingItemImplementation.LoseItemEvent; +import com.microsoft.Malmo.Schemas.RewardForPossessingItem; + +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.entity.item.ItemTossEvent; +import net.minecraftforge.event.entity.player.EntityItemPickupEvent; +import net.minecraftforge.event.entity.player.PlayerDestroyItemEvent; +import net.minecraftforge.event.world.BlockEvent.PlaceEvent; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.gameevent.PlayerEvent; + +/** + * @author Cayden Codel, Carnegie Mellon University + *

+ * Sends a reward when the agent possesses the specified item with specified amounts. + * The counter is relative, meaning it goes down if items are placed, lost, or destroyed. + */ +public class RewardForPossessingItemImplementation extends RewardForItemBase implements IRewardProducer { + private RewardForPossessingItem params; + private ArrayList matchers; + /** + * A current mapping of strings to the amount of item we have + */ + private HashMap possessedItems; + + /** + * A mapping of strings to the highest amount of an item we had at any single time + */ + private HashMap maxPossessedItems; + + @SubscribeEvent + public void onGainItem(RewardForCollectingItemImplementation.GainItemEvent event) { + if (event.stack != null) + checkForMatch(event.stack); + } + + @SubscribeEvent + public void onPickupItem(EntityItemPickupEvent event) { + if (event.getItem() != null && event.getEntityPlayer() instanceof EntityPlayerMP) + checkForMatch(event.getItem().getEntityItem()); + } + + @SubscribeEvent + public void onItemCraft(PlayerEvent.ItemCraftedEvent event) { + if (event.player instanceof EntityPlayerMP && !event.crafting.isEmpty()) + checkForMatch(event.crafting); + } + + @SubscribeEvent + public void onItemSmelt(PlayerEvent.ItemSmeltedEvent event) { + if (event.player instanceof EntityPlayerMP && !event.smelting.isEmpty()) + checkForMatch(event.smelting); + } + + @SubscribeEvent + public void onLoseItem(LoseItemEvent event) { + if (event.stack != null && event.cause == 0) + removeCollectedItemCount(event.stack); + } + + @SubscribeEvent + public void onDropItem(ItemTossEvent event) { + if (event.getPlayer() instanceof EntityPlayerMP) + removeCollectedItemCount(event.getEntityItem().getEntityItem()); + } + + @SubscribeEvent + public void onDestroyItem(PlayerDestroyItemEvent event) { + if (event.getEntityPlayer() instanceof EntityPlayerMP) + removeCollectedItemCount(event.getOriginal()); + } + + @SubscribeEvent + public void onBlockPlace(PlaceEvent event) { + if (!event.isCanceled() && event.getPlacedBlock() != null && event.getPlayer() instanceof EntityPlayerMP) + removeCollectedItemCount(new ItemStack(event.getPlacedBlock().getBlock())); + } + + /** + * Checks whether the ItemStack matches a variant stored in the item list. If + * so, returns true, else returns false. + * + * @param is The item stack + * @return If the stack is allowed in the item matchers and has color or + * variants enabled, returns true, else false. + */ + private boolean getVariant(ItemStack is) { + for (ItemMatcher matcher : matchers) { + if (matcher.allowedItemTypes.contains(is.getItem().getUnlocalizedName())) { + if (matcher.matchSpec.getColour() != null && matcher.matchSpec.getColour().size() > 0) + return true; + if (matcher.matchSpec.getVariant() != null && matcher.matchSpec.getVariant().size() > 0) + return true; + } + } + + return false; + } + + /** + * Since there are two counters, returns the current value of the items we have collected. + * Logic regarding the difference between active and max counter of items done below. + * + * @param is The item stack to get the count from + * @return The count, 0 if not encountered/collected before + */ + private int getCollectedItemCount(ItemStack is) { + boolean variant = getVariant(is); + + if (variant) + return (possessedItems.get(is.getUnlocalizedName()) == null) ? 0 : possessedItems.get(is.getUnlocalizedName()); + else + return (possessedItems.get(is.getItem().getUnlocalizedName()) == null) ? 0 + : possessedItems.get(is.getItem().getUnlocalizedName()); + } + + /** + * Since there are two counters, returns the max value of the items we have collected. + * Logic regarding the difference between active and max counter of items done below. + * + * @param is The item stack to get the count from + * @return The count, 0 if not encountered/collected before + */ + private int getMaxCollectedItemCount(ItemStack is) { + boolean variant = getVariant(is); + + if (variant) + return (maxPossessedItems.get(is.getUnlocalizedName()) == null) ? 0 : maxPossessedItems.get(is.getUnlocalizedName()); + else + return (maxPossessedItems.get(is.getItem().getUnlocalizedName()) == null) ? 0 + : maxPossessedItems.get(is.getItem().getUnlocalizedName()); + } + + private void addCollectedItemCount(ItemStack is) { + boolean variant = getVariant(is); + + if (variant) { + int prev = (possessedItems.get(is.getUnlocalizedName()) == null ? 0 + : possessedItems.get(is.getUnlocalizedName())); + int maxPrev = (maxPossessedItems.get(is.getUnlocalizedName()) == null) ? 0 + : maxPossessedItems.get(is.getUnlocalizedName()); + possessedItems.put(is.getUnlocalizedName(), prev + is.getCount()); + + if (prev + is.getCount() > maxPrev) + maxPossessedItems.put(is.getUnlocalizedName(), prev + is.getCount()); + } else { + int prev = (possessedItems.get(is.getItem().getUnlocalizedName()) == null ? 0 + : possessedItems.get(is.getItem().getUnlocalizedName())); + int maxPrev = (maxPossessedItems.get(is.getItem().getUnlocalizedName()) == null) ? 0 + : maxPossessedItems.get(is.getItem().getUnlocalizedName()); + possessedItems.put(is.getItem().getUnlocalizedName(), prev + is.getCount()); + + if (prev + is.getCount() > maxPrev) + maxPossessedItems.put(is.getItem().getUnlocalizedName(), prev + is.getCount()); + } + } + + private void removeCollectedItemCount(ItemStack is) { + boolean variant = getVariant(is); + + if (variant) { + int prev = (possessedItems.get(is.getUnlocalizedName()) == null ? 0 + : possessedItems.get(is.getUnlocalizedName())); + possessedItems.put(is.getUnlocalizedName(), prev - is.getCount()); + } else { + int prev = (possessedItems.get(is.getItem().getUnlocalizedName()) == null ? 0 + : possessedItems.get(is.getItem().getUnlocalizedName())); + possessedItems.put(is.getItem().getUnlocalizedName(), prev - is.getCount()); + } + } + + private void checkForMatch(ItemStack is) { + int savedCollected = getCollectedItemCount(is); + int maxCollected = getMaxCollectedItemCount(is); + if (is != null) { + for (ItemMatcher matcher : this.matchers) { + if (matcher.matches(is)) { + if (!params.isSparse()) { + if (savedCollected != 0 && savedCollected < matcher.matchSpec.getAmount()) { + for (int i = savedCollected; i < matcher.matchSpec.getAmount() + && i - savedCollected < is.getCount(); i++) { + if (i >= maxCollected) { + int dimension = params.getDimension(); + float adjusted_reward = this.adjustAndDistributeReward( + ((BlockOrItemSpecWithReward) matcher.matchSpec).getReward().floatValue(), + params.getDimension(), + ((BlockOrItemSpecWithReward) matcher.matchSpec).getDistribution()); + addCachedReward(dimension, adjusted_reward); + } + } + } else if (savedCollected == 0) + for (int i = 0; i < is.getCount() && i < matcher.matchSpec.getAmount(); i++) { + if (i >= maxCollected) { + int dimension = params.getDimension(); + float adjusted_reward = this.adjustAndDistributeReward( + ((BlockOrItemSpecWithReward) matcher.matchSpec).getReward().floatValue(), + params.getDimension(), + ((BlockOrItemSpecWithReward) matcher.matchSpec).getDistribution()); + addCachedReward(dimension, adjusted_reward); + } + } + } else if (savedCollected < matcher.matchSpec.getAmount() + && savedCollected + is.getCount() >= matcher.matchSpec.getAmount()) { + int dimension = params.getDimension(); + float adjusted_reward = this.adjustAndDistributeReward( + ((BlockOrItemSpecWithReward) matcher.matchSpec).getReward().floatValue(), + params.getDimension(), + ((BlockOrItemSpecWithReward) matcher.matchSpec).getDistribution()); + addCachedReward(dimension, adjusted_reward); + } + } + } + + addCollectedItemCount(is); + } + } + + @Override + public boolean parseParameters(Object params) { + if (!(params instanceof RewardForPossessingItem)) + return false; + + matchers = new ArrayList(); + + this.params = (RewardForPossessingItem) params; + for (BlockOrItemSpecWithReward spec : this.params.getItem()) + this.matchers.add(new ItemMatcher(spec)); + + return true; + } + + @Override + public void prepare(MissionInit missionInit) { + super.prepare(missionInit); + MinecraftForge.EVENT_BUS.register(this); + possessedItems = new HashMap(); + maxPossessedItems = new HashMap(); + } + + @Override + public void getReward(MissionInit missionInit, MultidimensionalReward reward) { + super.getReward(missionInit, reward); + } + + @Override + public void cleanup() { + super.cleanup(); + MinecraftForge.EVENT_BUS.unregister(this); + } +} diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForSmeltingItemImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForSmeltingItemImplementation.java new file mode 100644 index 000000000..e3c3e742c --- /dev/null +++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForSmeltingItemImplementation.java @@ -0,0 +1,167 @@ +// -------------------------------------------------------------------------------------------------- +// Copyright (c) 2016 Microsoft Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +// associated documentation files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, publish, distribute, +// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// -------------------------------------------------------------------------------------------------- + +package com.microsoft.Malmo.MissionHandlers; +import java.util.ArrayList; +import java.util.HashMap; + +import com.microsoft.Malmo.MissionHandlerInterfaces.IRewardProducer; +import com.microsoft.Malmo.Schemas.BlockOrItemSpecWithReward; +import com.microsoft.Malmo.Schemas.MissionInit; + +import com.microsoft.Malmo.Schemas.RewardForSmeltingItem; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.gameevent.PlayerEvent; + +/** + * @author Cayden Codel, Carnegie Mellon University + *

+ * Sends a reward when the agent smelts the specified item with + * specified amounts. + */ +public class RewardForSmeltingItemImplementation extends RewardForItemBase implements IRewardProducer { + private RewardForSmeltingItem params; + private ArrayList matchers; + private HashMap smeltedItems; + + @SubscribeEvent + public void onItemSmelt(PlayerEvent.ItemSmeltedEvent event) { + if (event.player instanceof EntityPlayerMP && !event.smelting.isEmpty()) + checkForMatch(event.smelting); + } + + /** + * Checks whether the ItemStack matches a variant stored in the item list. If + * so, returns true, else returns false. + * + * @param is The item stack + * @return If the stack is allowed in the item matchers and has color or + * variants enabled, returns true, else false. + */ + private boolean getVariant(ItemStack is) { + for (ItemMatcher matcher : matchers) { + if (matcher.allowedItemTypes.contains(is.getItem().getUnlocalizedName())) { + if (matcher.matchSpec.getColour() != null && matcher.matchSpec.getColour().size() > 0) + return true; + if (matcher.matchSpec.getVariant() != null && matcher.matchSpec.getVariant().size() > 0) + return true; + } + } + + return false; + } + + private int getSmeltedItemCount(ItemStack is) { + boolean variant = getVariant(is); + + if (variant) + return (smeltedItems.get(is.getUnlocalizedName()) == null) ? 0 : smeltedItems.get(is.getUnlocalizedName()); + else + return (smeltedItems.get(is.getItem().getUnlocalizedName()) == null) ? 0 + : smeltedItems.get(is.getItem().getUnlocalizedName()); + } + + private void addSmeltedItemCount(ItemStack is) { + boolean variant = getVariant(is); + + if (variant) { + int prev = (smeltedItems.get(is.getUnlocalizedName()) == null ? 0 + : smeltedItems.get(is.getUnlocalizedName())); + smeltedItems.put(is.getUnlocalizedName(), prev + is.getCount()); + } else { + int prev = (smeltedItems.get(is.getItem().getUnlocalizedName()) == null ? 0 + : smeltedItems.get(is.getItem().getUnlocalizedName())); + smeltedItems.put(is.getItem().getUnlocalizedName(), prev + is.getCount()); + } + } + + private void checkForMatch(ItemStack is) { + int savedSmelted = getSmeltedItemCount(is); + for (ItemMatcher matcher : this.matchers) { + if (matcher.matches(is)) { + if (!params.isSparse()) { + if (savedSmelted != 0 && savedSmelted < matcher.matchSpec.getAmount()) { + for (int i = savedSmelted; i < matcher.matchSpec.getAmount() + && i - savedSmelted < is.getCount(); i++) { + int dimension = params.getDimension(); + float adjusted_reward = this.adjustAndDistributeReward( + ((BlockOrItemSpecWithReward) matcher.matchSpec).getReward().floatValue(), + params.getDimension(), + ((BlockOrItemSpecWithReward) matcher.matchSpec).getDistribution()); + addCachedReward(dimension, adjusted_reward); + } + } else if (savedSmelted == 0) + for (int i = 0; i < is.getCount() && i < matcher.matchSpec.getAmount(); i++) { + int dimension = params.getDimension(); + float adjusted_reward = this.adjustAndDistributeReward( + ((BlockOrItemSpecWithReward) matcher.matchSpec).getReward().floatValue(), + params.getDimension(), + ((BlockOrItemSpecWithReward) matcher.matchSpec).getDistribution()); + addCachedReward(dimension, adjusted_reward); + } + } else if (savedSmelted < matcher.matchSpec.getAmount() + && savedSmelted + is.getCount() >= matcher.matchSpec.getAmount()) { + int dimension = params.getDimension(); + float adjusted_reward = this.adjustAndDistributeReward( + ((BlockOrItemSpecWithReward) matcher.matchSpec).getReward().floatValue(), + params.getDimension(), + ((BlockOrItemSpecWithReward) matcher.matchSpec).getDistribution()); + addCachedReward(dimension, adjusted_reward); + } + } + } + + addSmeltedItemCount(is); + } + + @Override + public boolean parseParameters(Object params) { + if (!(params instanceof RewardForSmeltingItem)) + return false; + + matchers = new ArrayList(); + + this.params = (RewardForSmeltingItem) params; + for (BlockOrItemSpecWithReward spec : this.params.getItem()) + this.matchers.add(new ItemMatcher(spec)); + + return true; + } + + @Override + public void prepare(MissionInit missionInit) { + super.prepare(missionInit); + MinecraftForge.EVENT_BUS.register(this); + smeltedItems = new HashMap(); + } + + @Override + public void getReward(MissionInit missionInit, MultidimensionalReward reward) { + super.getReward(missionInit, reward); + } + + @Override + public void cleanup() { + super.cleanup(); + MinecraftForge.EVENT_BUS.unregister(this); + } +} diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/SimpleCraftCommandsImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/SimpleCraftCommandsImplementation.java index d550377a4..6d0f8784c 100755 --- a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/SimpleCraftCommandsImplementation.java +++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/SimpleCraftCommandsImplementation.java @@ -73,7 +73,10 @@ public IMessage onMessage(CraftMessage message, MessageContext ctx) { EntityPlayerMP player = ctx.getServerHandler().playerEntity; // Try crafting recipes first: - List matching_recipes = CraftingHelper.getRecipesForRequestedOutput(message.parameters); + List matching_recipes; + String[] split = message.parameters.split(" "); + matching_recipes = CraftingHelper.getRecipesForRequestedOutput(message.parameters, split.length > 1); + for (IRecipe recipe : matching_recipes) { if (CraftingHelper.attemptCrafting(player, recipe)) @@ -104,7 +107,7 @@ protected boolean onExecute(String verb, String parameter, MissionInit missionIn @Override public boolean parseParameters(Object params) { - if (params == null || !(params instanceof SimpleCraftCommands)) + if (!(params instanceof SimpleCraftCommands)) return false; SimpleCraftCommands cparams = (SimpleCraftCommands)params; diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/Utils/CraftingHelper.java b/Minecraft/src/main/java/com/microsoft/Malmo/Utils/CraftingHelper.java index c8129fdaa..8b8a17ce7 100755 --- a/Minecraft/src/main/java/com/microsoft/Malmo/Utils/CraftingHelper.java +++ b/Minecraft/src/main/java/com/microsoft/Malmo/Utils/CraftingHelper.java @@ -41,131 +41,109 @@ import net.minecraft.block.properties.PropertyDirection; import net.minecraft.block.properties.PropertyEnum; import net.minecraft.block.state.IBlockState; +import net.minecraft.client.entity.EntityPlayerSP; import net.minecraft.creativetab.CreativeTabs; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.init.Blocks; import net.minecraft.init.Items; +import net.minecraft.inventory.InventoryCrafting; import net.minecraft.item.EnumRarity; import net.minecraft.item.Item; import net.minecraft.item.ItemBlock; import net.minecraft.item.ItemStack; -import net.minecraft.item.crafting.CraftingManager; -import net.minecraft.item.crafting.FurnaceRecipes; -import net.minecraft.item.crafting.IRecipe; -import net.minecraft.item.crafting.ShapedRecipes; -import net.minecraft.item.crafting.ShapelessRecipes; +import net.minecraft.item.crafting.*; import net.minecraft.tileentity.TileEntityFurnace; import net.minecraft.util.NonNullList; import net.minecraft.util.ResourceLocation; import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.fml.common.gameevent.PlayerEvent; import net.minecraftforge.oredict.OreDictionary; import net.minecraftforge.oredict.ShapedOreRecipe; import net.minecraftforge.oredict.ShapelessOreRecipe; -public class CraftingHelper -{ +public class CraftingHelper { private static Map fuelCaches = new HashMap(); - /** Reset caches
+ /** + * Reset caches
* Needed to make sure the player starts with a fresh fuel stash. */ - public static void reset() - { + public static void reset() { fuelCaches = new HashMap(); } - /** Attempt to return the raw ingredients required for this recipe.
+ /** + * Attempt to return the raw ingredients required for this recipe.
* Ignores all shaping. + * * @param recipe the IRecipe to dissect. * @return a list of ItemStacks, amalgamated so that all items of the same type are placed in the same stack. */ - public static List getIngredients(IRecipe recipe) - { + public static List getIngredients(IRecipe recipe) { // IRecipe helpfully has no method for inspecting the raw ingredients, so we need to do different things depending on the subclass. List ingredients = new ArrayList(); - if (recipe instanceof ShapelessRecipes) - { - List items = (List)((ShapelessRecipes)recipe).recipeItems; - for (Object obj : items) - { + if (recipe instanceof ShapelessRecipes) { + List items = (List) ((ShapelessRecipes) recipe).recipeItems; + for (Object obj : items) { if (obj instanceof ItemStack) - ingredients.add((ItemStack)obj); + ingredients.add((ItemStack) obj); } - } - else if (recipe instanceof ShapelessOreRecipe) - { - NonNullList objs = ((ShapelessOreRecipe)recipe).getInput(); - for (Object o : objs) - { - if (o != null) - { + } else if (recipe instanceof ShapelessOreRecipe) { + NonNullList objs = ((ShapelessOreRecipe) recipe).getInput(); + for (Object o : objs) { + if (o != null) { if (o instanceof ItemStack) - ingredients.add((ItemStack)o); - else if (o instanceof List) - { - List stacks = (List)o; - for (Object stack : stacks) - { + ingredients.add((ItemStack) o); + else if (o instanceof List) { + List stacks = (List) o; + for (Object stack : stacks) { if (stack instanceof ItemStack) - ingredients.add((ItemStack)stack); + ingredients.add((ItemStack) stack); } } } } - } - else if (recipe instanceof ShapedRecipes) - { - ItemStack[] stack = ((ShapedRecipes)recipe).recipeItems; - for (int i = 0; i < stack.length; i++) - { + } else if (recipe instanceof ShapedRecipes) { + ItemStack[] stack = ((ShapedRecipes) recipe).recipeItems; + for (int i = 0; i < stack.length; i++) { if (stack[i] != null) ingredients.add(stack[i]); } - } - else if (recipe instanceof ShapedOreRecipe) - { - Object[] items = ((ShapedOreRecipe)recipe).getInput(); - for (int i = 0; i < items.length; i++) - { + } else if (recipe instanceof ShapedOreRecipe) { + Object[] items = ((ShapedOreRecipe) recipe).getInput(); + for (int i = 0; i < items.length; i++) { Object obj = items[i]; - if (obj != null) - { + if (obj != null) { if (obj instanceof ItemStack) - ingredients.add((ItemStack)obj); - else if (obj instanceof List) - { - List stacks = (List)items[i]; - for (Object stack : stacks) - { + ingredients.add((ItemStack) obj); + else if (obj instanceof List) { + List stacks = (List) items[i]; + for (Object stack : stacks) { if (stack instanceof ItemStack) - ingredients.add((ItemStack)stack); + ingredients.add((ItemStack) stack); } } } } - } - else - { + } else { return null; } return consolidateItemStacks(ingredients); } - - /** Take a list of ItemStacks and amalgamate where possible.
+ + /** + * Take a list of ItemStacks and amalgamate where possible.
+ * * @param inputStacks a list of ItemStacks * @return a list of ItemStacks, where all items of the same type are grouped into one stack. */ - public static List consolidateItemStacks(List inputStacks) - { + public static List consolidateItemStacks(List inputStacks) { // Horrible n^2 method - we should do something nicer if this ever becomes a bottleneck. List outputStacks = new ArrayList(); - for (ItemStack sourceIS : inputStacks) - { + for (ItemStack sourceIS : inputStacks) { boolean bFound = false; - for (ItemStack destIS : outputStacks) - { - if (destIS != null && sourceIS != null && itemStackIngredientsMatch(destIS, sourceIS)) - { + for (ItemStack destIS : outputStacks) { + if (destIS != null && sourceIS != null && itemStackIngredientsMatch(destIS, sourceIS)) { bFound = true; destIS.setCount(destIS.getCount() + sourceIS.getCount()); } @@ -175,23 +153,47 @@ public static List consolidateItemStacks(List inputStacks) } return outputStacks; } - - /** Inspect a player's inventory to see whether they have enough items to form the supplied list of ItemStacks.
+ + /** + * Inspect a player's inventory to see whether they have enough items to form the supplied list of ItemStacks.
+ * The ingredients list MUST be amalgamated such that no two ItemStacks contain the same type of item. + * + * @param player + * @param ingredients an amalgamated list of ingredients + * @return true if the player's inventory contains sufficient quantities of all the required items. + */ + public static boolean playerHasIngredients(EntityPlayerMP player, List ingredients) { + NonNullList main = player.inventory.mainInventory; + NonNullList arm = player.inventory.armorInventory; + + for (ItemStack isIngredient : ingredients) { + int target = isIngredient.getCount(); + for (int i = 0; i < main.size() + arm.size() && target > 0; i++) { + ItemStack isPlayer = (i >= main.size()) ? arm.get(i - main.size()) : main.get(i); + if (isPlayer != null && isIngredient != null && itemStackIngredientsMatch(isPlayer, isIngredient)) + target -= isPlayer.getCount(); + } + if (target > 0) + return false; // Don't have enough of this. + } + return true; + } + + /** + * Inspect a player's inventory to see whether they have enough items to form the supplied list of ItemStacks.
* The ingredients list MUST be amalgamated such that no two ItemStacks contain the same type of item. + * * @param player * @param ingredients an amalgamated list of ingredients * @return true if the player's inventory contains sufficient quantities of all the required items. */ - public static boolean playerHasIngredients(EntityPlayerMP player, List ingredients) - { + public static boolean playerHasIngredients(EntityPlayerSP player, List ingredients) { NonNullList main = player.inventory.mainInventory; NonNullList arm = player.inventory.armorInventory; - for (ItemStack isIngredient : ingredients) - { + for (ItemStack isIngredient : ingredients) { int target = isIngredient.getCount(); - for (int i = 0; i < main.size() + arm.size() && target > 0; i++) - { + for (int i = 0; i < main.size() + arm.size() && target > 0; i++) { ItemStack isPlayer = (i >= main.size()) ? arm.get(i - main.size()) : main.get(i); if (isPlayer != null && isIngredient != null && itemStackIngredientsMatch(isPlayer, isIngredient)) target -= isPlayer.getCount(); @@ -202,13 +204,14 @@ public static boolean playerHasIngredients(EntityPlayerMP player, List + /** + * Consume fuel from the player's inventory.
* Take it first from their cache, if present, and then from their inventory, starting * at the first slot and working upwards. + * * @param player * @param burnAmount amount of fuel to burn, in ticks. */ - public static void burnInventory(EntityPlayerMP player, int burnAmount, ItemStack input) - { + public static void burnInventory(EntityPlayerMP player, int burnAmount, ItemStack input) { if (!fuelCaches.containsKey(player)) fuelCaches.put(player, -burnAmount); else fuelCaches.put(player, fuelCaches.get(player) - burnAmount); int index = 0; - while (fuelCaches.get(player) < 0 && index < player.inventory.mainInventory.size()) - { + while (fuelCaches.get(player) < 0 && index < player.inventory.mainInventory.size()) { ItemStack is = player.inventory.mainInventory.get(index); - if (is != null) - { + if (is != null) { int burnTime = TileEntityFurnace.getItemBurnTime(is); - if (burnTime != 0) - { + if (burnTime != 0) { // Consume item: if (is.getCount() > 1) is.setCount(is.getCount() - 1); - else - { + else { // If this is a bucket of lava, we need to consume the lava but leave the bucket. - if (is.getItem() == Items.LAVA_BUCKET) - { + if (is.getItem() == Items.LAVA_BUCKET) { // And if we're cooking wet sponge, we need to leave the bucket filled with water. if (input.getItem() == Item.getItemFromBlock(Blocks.SPONGE) && input.getMetadata() == 1) player.inventory.mainInventory.set(index, new ItemStack(Items.WATER_BUCKET)); else player.inventory.mainInventory.set(index, new ItemStack(Items.BUCKET)); - } - else + } else player.inventory.mainInventory.get(index).setCount(0); index++; } fuelCaches.put(player, fuelCaches.get(player) + burnTime); - } - else + } else index++; - } - else + } else index++; } } - /** Manually attempt to remove ingredients from the player's inventory.
+ /** + * Manually attempt to remove ingredients from the player's inventory.
+ * * @param player * @param ingredients */ - public static void removeIngredientsFromPlayer(EntityPlayerMP player, List ingredients) - { + public static void removeIngredientsFromPlayer(EntityPlayerMP player, List ingredients) { NonNullList main = player.inventory.mainInventory; NonNullList arm = player.inventory.armorInventory; - for (ItemStack isIngredient : ingredients) - { + for (ItemStack isIngredient : ingredients) { int target = isIngredient.getCount(); - for (int i = 0; i < main.size() + arm.size() && target > 0; i++) - { + for (int i = 0; i < main.size() + arm.size() && target > 0; i++) { ItemStack isPlayer = (i >= main.size()) ? arm.get(i - main.size()) : main.get(i); - if (isPlayer != null && isIngredient != null && itemStackIngredientsMatch(isPlayer, isIngredient)) - { - if (target >= isPlayer.getCount()) - { + if (itemStackIngredientsMatch(isPlayer, isIngredient)) { + if (target >= isPlayer.getCount()) { // Consume this stack: target -= isPlayer.getCount(); if (i >= main.size()) arm.get(i - main.size()).setCount(0); else main.get(i).setCount(0); - } - else - { + } else { isPlayer.setCount(isPlayer.getCount() - target); target = 0; } @@ -322,105 +313,175 @@ public static void removeIngredientsFromPlayer(EntityPlayerMP player, List getRecipesForRequestedOutput(String output) - { + public static List getRecipesForRequestedOutput(String output, boolean variant) { List matchingRecipes = new ArrayList(); ItemStack target = MinecraftTypeHelper.getItemStackFromParameterString(output); List recipes = CraftingManager.getInstance().getRecipeList(); - for (Object obj : recipes) - { + for (Object obj : recipes) { if (obj == null) continue; - if (obj instanceof IRecipe) - { - ItemStack is = ((IRecipe)obj).getRecipeOutput(); - if (is == null) + if (obj instanceof IRecipe) { + ItemStack is = ((IRecipe) obj).getRecipeOutput(); + if (target == null) continue; - if (ItemStack.areItemsEqual(is, target)) - { - matchingRecipes.add((IRecipe)obj); - } + if (variant && ItemStack.areItemsEqual(is, target)) + matchingRecipes.add((IRecipe) obj); + else if (!variant && is.getItem() == target.getItem()) + matchingRecipes.add((IRecipe) obj); + } + } + return matchingRecipes; + } + + /** + * Attempt to find all recipes that result in an item of the requested output. + * + * @param output the desired item, eg from Types.xsd - "diamond_pickaxe" etc - or as a Minecraft name - eg "tile.woolCarpet.blue" + * @param variant if variants should be obeyed in constructing the recipes, i.e. if false, variant blind + * @return a list of IRecipe objects that result in this item. + */ + public static List getRecipesForRequestedOutput(ItemStack output, boolean variant) { + List matchingRecipes = new ArrayList(); + List recipes = CraftingManager.getInstance().getRecipeList(); + for (Object obj : recipes) { + if (obj == null) + continue; + if (obj instanceof IRecipe) { + ItemStack is = ((IRecipe) obj).getRecipeOutput(); + if (output == null) + continue; + if (variant && ItemStack.areItemsEqual(is, output)) + matchingRecipes.add((IRecipe) obj); + else if (!variant && is.getItem() == output.getItem()) + matchingRecipes.add((IRecipe) obj); } } return matchingRecipes; } - /** Attempt to find a smelting recipe that results in the requested output. - * @param output + /** + * Attempt to find a smelting recipe that results in the requested output. + * + * @param output The output of the furnace burn * @return an ItemStack representing the required input. */ - public static ItemStack getSmeltingRecipeForRequestedOutput(String output) - { + public static ItemStack getSmeltingRecipeForRequestedOutput(String output) { ItemStack target = MinecraftTypeHelper.getItemStackFromParameterString(output); Iterator furnaceIt = FurnaceRecipes.instance().getSmeltingList().keySet().iterator(); - while (furnaceIt.hasNext()) - { - ItemStack isInput = (ItemStack)furnaceIt.next(); - ItemStack isOutput = (ItemStack)FurnaceRecipes.instance().getSmeltingList().get(isInput); + while (furnaceIt.hasNext()) { + ItemStack isInput = (ItemStack) furnaceIt.next(); + ItemStack isOutput = FurnaceRecipes.instance().getSmeltingList().get(isInput); if (itemStackIngredientsMatch(target, isOutput)) return isInput; } return null; } - /** Attempt to craft the given recipe.
+ /** + * Attempt to craft the given recipe.
* This pays no attention to tedious things like using the right crafting table / brewing stand etc, or getting the right shape.
* It simply takes the raw ingredients out of the player's inventory, and inserts the output of the recipe, if possible. + * * @param player the SERVER SIDE player that will do the crafting. * @param recipe the IRecipe we wish to craft. * @return true if the recipe had an output, and the player had the required ingredients to create it; false otherwise. */ - public static boolean attemptCrafting(EntityPlayerMP player, IRecipe recipe) - { + public static boolean attemptCrafting(EntityPlayerMP player, IRecipe recipe) { if (player == null || recipe == null) return false; ItemStack is = recipe.getRecipeOutput(); - if (is == null) - return false; List ingredients = getIngredients(recipe); - if (playerHasIngredients(player, ingredients)) - { + if (playerHasIngredients(player, ingredients)) { // We have the ingredients we need, so directly manipulate the inventory. // First, remove the ingredients: removeIngredientsFromPlayer(player, ingredients); // Now add the output of the recipe: - ItemStack resultForInventory = is.copy(); ItemStack resultForReward = is.copy(); player.inventory.addItemStackToInventory(resultForInventory); RewardForCollectingItemImplementation.GainItemEvent event = new RewardForCollectingItemImplementation.GainItemEvent(resultForReward); + event.setCause(1); MinecraftForge.EVENT_BUS.post(event); + // Now trigger a craft event + List recipes = getRecipesForRequestedOutput(resultForReward, true); + for (IRecipe iRecipe : recipes) { + if (iRecipe instanceof ShapedRecipes) { + ShapedRecipes shapedRecipe = (ShapedRecipes) iRecipe; + InventoryCrafting craftMatrix; + if (shapedRecipe.recipeItems.length <= 4) + craftMatrix = new InventoryCrafting(player.inventoryContainer, 2, 2); + else + craftMatrix = new InventoryCrafting(player.inventoryContainer, 3, 3); + for (int i = 0; i < shapedRecipe.recipeItems.length; i++) + craftMatrix.setInventorySlotContents(i, shapedRecipe.recipeItems[i]); + + MinecraftForge.EVENT_BUS.post(new PlayerEvent.ItemCraftedEvent(player, resultForReward, craftMatrix)); + break; + } else if (iRecipe instanceof ShapelessRecipes) { + ShapelessRecipes shapelessRecipe = (ShapelessRecipes) iRecipe; + InventoryCrafting craftMatrix; + if (shapelessRecipe.recipeItems.size() <= 4) { + craftMatrix = new InventoryCrafting(player.inventoryContainer, 2, 2); + for (int i = 0; i < shapelessRecipe.recipeItems.size(); i++) + craftMatrix.setInventorySlotContents(i, shapelessRecipe.recipeItems.get(i)); + } else { + craftMatrix = new InventoryCrafting(player.inventoryContainer, 3, 3); + for (int i = 0; i < shapelessRecipe.recipeItems.size(); i++) + craftMatrix.setInventorySlotContents(i, shapelessRecipe.recipeItems.get(i)); + } + + MinecraftForge.EVENT_BUS.post(new PlayerEvent.ItemCraftedEvent(player, resultForReward, craftMatrix)); + break; + } else if (iRecipe instanceof ShapedOreRecipe) { + ShapedOreRecipe oreRecipe = (ShapedOreRecipe) iRecipe; + Object[] input = oreRecipe.getInput(); + InventoryCrafting craftMatrix = new InventoryCrafting(player.inventoryContainer, 3, 3); + for (int i = 0; i < input.length; i++) { + if (input[i] instanceof ItemStack) + craftMatrix.setInventorySlotContents(i, (ItemStack) input[i]); + else if (input[i] instanceof NonNullList) + if (((NonNullList) input[i]).size() != 0) + craftMatrix.setInventorySlotContents(i, (ItemStack) ((NonNullList) input[i]).get(0)); + } + + MinecraftForge.EVENT_BUS.post(new PlayerEvent.ItemCraftedEvent(player, resultForReward, craftMatrix)); + } + } + return true; } return false; } - /** Attempt to smelt the given item.
+ /** + * Attempt to smelt the given item.
* This returns instantly, callously disregarding such frivolous niceties as cooking times or the presence of a furnace.
* It will, however, consume fuel from the player's inventory. + * * @param player - * @param input the raw ingredients we want to cook. + * @param input the raw ingredients we want to cook. * @return true if cooking was successful. */ - public static boolean attemptSmelting(EntityPlayerMP player, ItemStack input) - { + public static boolean attemptSmelting(EntityPlayerMP player, ItemStack input) { if (player == null || input == null) return false; List ingredients = new ArrayList(); ingredients.add(input); - ItemStack isOutput = (ItemStack)FurnaceRecipes.instance().getSmeltingList().get(input); + ItemStack isOutput = FurnaceRecipes.instance().getSmeltingList().get(input); if (isOutput == null) return false; int cookingTime = 200; // Seems to be hard-coded in TileEntityFurnace. - if (playerHasIngredients(player, ingredients) && totalBurnTimeInInventory(player) >= cookingTime) - { + if (playerHasIngredients(player, ingredients) && totalBurnTimeInInventory(player) >= cookingTime) { removeIngredientsFromPlayer(player, ingredients); burnInventory(player, cookingTime, input); @@ -428,31 +489,33 @@ public static boolean attemptSmelting(EntityPlayerMP player, ItemStack input) ItemStack resultForReward = isOutput.copy(); player.inventory.addItemStackToInventory(resultForInventory); RewardForCollectingItemImplementation.GainItemEvent event = new RewardForCollectingItemImplementation.GainItemEvent(resultForReward); + event.setCause(2); MinecraftForge.EVENT_BUS.post(event); + // Now trigger a smelt event + MinecraftForge.EVENT_BUS.post(new PlayerEvent.ItemSmeltedEvent(player, resultForReward)); return true; } return false; } - /** Little utility method for dumping out a list of all the Minecraft items, plus as many useful attributes as + /** + * Little utility method for dumping out a list of all the Minecraft items, plus as many useful attributes as * we can find for them. This is primarily used by decision_tree_test.py but might be useful for real-world applications too. + * * @param filename location to save the dumped list. * @throws IOException */ - public static void dumpItemProperties(String filename) throws IOException - { + public static void dumpItemProperties(String filename) throws IOException { FileOutputStream fos = new FileOutputStream("..//..//build//install//Python_Examples//item_database.json"); OutputStreamWriter osw = new OutputStreamWriter(fos, "utf-8"); BufferedWriter writer = new BufferedWriter(osw); JsonArray itemTypes = new JsonArray(); - for (ResourceLocation i : Item.REGISTRY.getKeys()) - { + for (ResourceLocation i : Item.REGISTRY.getKeys()) { Item item = Item.REGISTRY.getObject(i); - if (item != null) - { + if (item != null) { JsonObject json = new JsonObject(); - json.addProperty("type", Item.REGISTRY.getNameForObject(item).toString().replace("minecraft:","")); + json.addProperty("type", Item.REGISTRY.getNameForObject(item).toString().replace("minecraft:", "")); json.addProperty("damageable", item.isDamageable()); json.addProperty("rendersIn3D", item.isFull3D()); json.addProperty("repairable", item.isRepairable()); @@ -468,9 +531,8 @@ public static void dumpItemProperties(String filename) throws IOException json.addProperty("maxUseDuration", is.getMaxItemUseDuration()); json.addProperty("block", item instanceof ItemBlock); json.addProperty("hasContainerItem", item.hasContainerItem()); - if (item instanceof ItemBlock) - { - ItemBlock ib = (ItemBlock)item; + if (item instanceof ItemBlock) { + ItemBlock ib = (ItemBlock) item; Block b = ib.getBlock(); IBlockState bs = b.getDefaultState(); json.addProperty("slipperiness", b.slipperiness); @@ -479,8 +541,7 @@ public static void dumpItemProperties(String filename) throws IOException json.addProperty("canProvidePower", bs.canProvidePower()); json.addProperty("translucent", bs.isTranslucent()); Material mat = bs.getMaterial(); - if (mat != null) - { + if (mat != null) { json.addProperty("canBurn", mat.getCanBurn()); json.addProperty("isLiquid", mat.isLiquid()); json.addProperty("blocksMovement", mat.blocksMovement()); @@ -489,29 +550,27 @@ public static void dumpItemProperties(String filename) throws IOException json.addProperty("pistonPushable", mat.getMobilityFlag() == EnumPushReaction.NORMAL); json.addProperty("woodenMaterial", mat == Material.WOOD); json.addProperty("ironMaterial", mat == Material.IRON); - json.addProperty("glassyMaterial", mat == Material.GLASS); - json.addProperty("clothMaterial", mat == Material.CLOTH); + json.addProperty("glassyMaterial", mat == Material.GLASS); + json.addProperty("clothMaterial", mat == Material.CLOTH); } boolean hasDirection = false; boolean hasColour = false; boolean hasVariant = false; - for (IProperty prop : bs.getProperties().keySet()) - { + for (IProperty prop : bs.getProperties().keySet()) { System.out.println(Item.REGISTRY.getNameForObject(item).toString() + " -- " + prop); if (prop instanceof PropertyDirection) hasDirection = true; if (prop instanceof PropertyEnum && prop.getName().equals("color")) hasColour = true; - if (prop instanceof PropertyEnum && prop.getName().equals("variant")) - { + if (prop instanceof PropertyEnum && prop.getName().equals("variant")) { hasVariant = true; json.addProperty("variant", bs.getValue(prop).toString()); } } json.addProperty("hasDirection", hasDirection); json.addProperty("hasColour", hasColour); - json.addProperty("hasVariant", hasVariant); + json.addProperty("hasVariant", hasVariant); } itemTypes.add(json); } @@ -520,32 +579,30 @@ public static void dumpItemProperties(String filename) throws IOException writer.close(); } - /** Little utility method for dumping out a list of all the recipes we understand. + /** + * Little utility method for dumping out a list of all the recipes we understand. + * * @param filename location to save the dumped list. * @throws IOException */ - public static void dumpRecipes(String filename) throws IOException - { + public static void dumpRecipes(String filename) throws IOException { FileOutputStream fos = new FileOutputStream(filename); OutputStreamWriter osw = new OutputStreamWriter(fos, "utf-8"); BufferedWriter writer = new BufferedWriter(osw); List recipes = CraftingManager.getInstance().getRecipeList(); - for (Object obj : recipes) - { + for (Object obj : recipes) { if (obj == null) continue; - if (obj instanceof IRecipe) - { - ItemStack is = ((IRecipe)obj).getRecipeOutput(); + if (obj instanceof IRecipe) { + ItemStack is = ((IRecipe) obj).getRecipeOutput(); if (is == null) continue; String s = is.getCount() + "x" + is.getUnlocalizedName() + " = "; - List ingredients = getIngredients((IRecipe)obj); + List ingredients = getIngredients((IRecipe) obj); if (ingredients == null) continue; boolean first = true; - for (ItemStack isIngredient : ingredients) - { + for (ItemStack isIngredient : ingredients) { if (!first) s += ", "; s += isIngredient.getCount() + "x" + isIngredient.getUnlocalizedName(); @@ -557,10 +614,9 @@ public static void dumpRecipes(String filename) throws IOException } } Iterator furnaceIt = FurnaceRecipes.instance().getSmeltingList().keySet().iterator(); - while (furnaceIt.hasNext()) - { - ItemStack isInput = (ItemStack)furnaceIt.next(); - ItemStack isOutput = (ItemStack)FurnaceRecipes.instance().getSmeltingList().get(isInput); + while (furnaceIt.hasNext()) { + ItemStack isInput = (ItemStack) furnaceIt.next(); + ItemStack isOutput = (ItemStack) FurnaceRecipes.instance().getSmeltingList().get(isInput); String s = isOutput.getCount() + "x" + isOutput.getUnlocalizedName() + " = FUEL + " + isInput.getCount() + "x" + isInput.getUnlocalizedName() + "\n"; writer.write(s); } diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/Utils/MinecraftTypeHelper.java b/Minecraft/src/main/java/com/microsoft/Malmo/Utils/MinecraftTypeHelper.java index a992c69e2..e70125936 100755 --- a/Minecraft/src/main/java/com/microsoft/Malmo/Utils/MinecraftTypeHelper.java +++ b/Minecraft/src/main/java/com/microsoft/Malmo/Utils/MinecraftTypeHelper.java @@ -524,7 +524,7 @@ public static ItemStack getItemStackFromDrawItem(DrawItem i) /** Select the request variant of the Minecraft block, if applicable * @param state The block to be varied - * @param colour The new variation + * @param variant The new variation * @return A new blockstate which is the requested variant of the original, if such a variant exists; otherwise it returns the original block. */ static IBlockState applyVariant(IBlockState state, Variation variant) @@ -609,7 +609,7 @@ else if (prop.getValueClass() == EnumOrientation.class) * through the translation layer. This ensures the result matches what we use in Types.XSD, * and prevents things like "entity.ShulkerBullet.name" being returned, where there is no * translation provided in the .lang file. - * @param ent The entity + * @param e The entity * @return The entity's name. */ public static String getUnlocalisedEntityName(Entity e) diff --git a/Schemas/Mission.xsd b/Schemas/Mission.xsd index 0fe0d6591..04e08434c 100755 --- a/Schemas/Mission.xsd +++ b/Schemas/Mission.xsd @@ -353,6 +353,9 @@ + + + @@ -367,6 +370,9 @@ + + + @@ -376,6 +382,9 @@ + + + diff --git a/Schemas/Mission.xsd.in b/Schemas/Mission.xsd.in index 1048707b1..c216fe1bb 100644 --- a/Schemas/Mission.xsd.in +++ b/Schemas/Mission.xsd.in @@ -353,6 +353,9 @@ + + + @@ -367,6 +370,9 @@ + + + @@ -376,6 +382,9 @@ + + + diff --git a/Schemas/MissionHandlers.xsd b/Schemas/MissionHandlers.xsd index 5d4ba773e..a19566bbb 100755 --- a/Schemas/MissionHandlers.xsd +++ b/Schemas/MissionHandlers.xsd @@ -8,2554 +8,2877 @@ jaxb:version="2.1" version="0.37"> - - - - - - - An absolute position in the 3D Minecraft world. - - - - - - - - - - - An absolute position in the 3D Minecraft world that includes yaw and pitch. - - - - - - - - Defaults to facing South (0). North is 180. - - - - - - - - - - - - - Defaults to looking straight ahead (0). +90 = look down. -90 = look up. - - - - - - - - - - - - - - - - - - + + - - - - Generates a superflat world with a specified preset string - see e.g. [[http://www.minecraft101.net/superflat/]] - - - - + - - The superflat customization preset string. - + + An absolute position in the 3D Minecraft world. + - - + + + + + + - - Set this to true to force the world to be reloaded, otherwise the current world will be used (provided it matches the requested generator string). - Force reloading is slow, but will guarantee that no world changes will carry over between missions. - + + An absolute position in the 3D Minecraft world that includes yaw and pitch. + - - + + + + + + Defaults to facing South (0). North is 180. + + + + + + + + + + + + + Defaults to looking straight ahead (0). +90 = look down. -90 = look up. + + + + + + + + + + + + + + + + + + + + - - The world seed - leave blank (default) to get a random world. - + + Generates a superflat world with a specified preset string - see e.g. [[http://www.minecraft101.net/superflat/]] + - - + + + + + The superflat customization preset string. + + + + + + + Set this to true to force the world to be reloaded, otherwise the current world will be used (provided it matches the requested generator string). + Force reloading is slow, but will guarantee that no world changes will carry over between missions. + + + + + + + The world seed - leave blank (default) to get a random world. + + + + + + + Set this to true to force the world data files to be deleted after the mission is done. + Enabling this setting prevents the disk being filled with old worlds. + + + + + + + - - Set this to true to force the world data files to be deleted after the mission is done. - Enabling this setting prevents the disk being filled with old worlds. - + + Generates the default terrain. + - - - - - - - - Generates the default terrain. - - - - - - - The world seed - leave blank (default) to get a random world. - - - - - - - Set this to true to force the world to be reloaded, otherwise the current world will be used (provided it matches the requested seed). - Force reloading is slow, but will guarantee that no world changes will carry over between missions. - - - - - - - Set this to true to force the world data files to be deleted after the mission is done. - Enabling this setting prevents the disk being filled with old worlds. - - - - - - - - - - Loads a saved world from disk. You can find the saved worlds in "{{{Minecraft\run\saves}}}". Use the full path to one of those folders. - - If Minecraft is running on a different machine then copy the folder to a readable network location and update the path accordingly. Example: - - {{{<FileWorldGenerator src="\\\\machine-id\\shared\\ProjectMalmo\\saved_maps\\arena" />}}} - - - - - - - The path to the saved world folder. - - - - - - - Set this to true to force the world to be reloaded, otherwise the current world will be used (provided it matches the requested source filename). - Force reloading is slow, but will guarantee that no world changes will carry over between missions. - - - - - - - Set this to true to force the world data files to be deleted after the mission is done. - Enabling this setting prevents the disk being filled with old worlds. - - - - - - - - - - Generates a survival world with the specified biome. - - - - - - - The biome type for the world. Each chunk will be loaded with the biome specified. - - If left blank, the world will be a normal survival world. - - Biome ID #'s can be found here: https://minecraft.gamepedia.com/Biome#Biome_IDs - - - - - - - Set this to true to force the world to be reloaded, otherwise the current world will be used (provided it matches the requested seed). - Force reloading is slow, but will guarantee that no world changes will carry over between missions. - - - - - - - Set this to true to force the world data files to be deleted after the mission is done. - Enabling this setting prevents the disk being filled with old worlds. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Creates a moving two-block target which takes random moves within a specified arena. Can be linked to the turn scheduler. - This can be made more general in the future, but is currently tailored specifically for the Malmo collaborative challenge. - - - - - - - - Define the bounds of the arena within which the target can move. - - - - - - - - The master seed for the random number generator used to move the target. - - - - - - Either an integer number, or the string "random". - - - - - - - - - - - The length, in ticks, between each update, or the string "turnbased" to use the turn scheduler. - - - - - - Either an integer number, or the string "turnbased". - - - - - - - - - - - - - - - - - - Adds a snake made of blocks, that grows at one end and shrinks at the other. - - - - - - - - The master seed for the random number generator used to make the snake. - - - - - - Either an integer number, or the string "random". - - - - - - - - - - - Optional seed for determining block types. - - - - - - Either an integer number, or the string "random". - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Adds a maze into the world. - - - - - - - - The master seed for the random number generator used to make the maze. - - - - - - Either an integer number, or the string "random". - - - - - - - - - - - Seed for the random number generator for determining block types - omit to allow master seed to control block types. - - - - - - Either an integer number, or the string "random". - - - - - - - - - - - - - - - - - - - - - - - - - - - - Omit this element if you want the optimal path to be unmarked. - - - - - - - Omit this element if you want the subgoal points to be unmarked. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Base class for all draw objects. - - - - - - - - Base class for all block-based draw objects. - - - - - - - - - - - - - - - - Draws structures into the world. - - - - - - - - - - - - - Specify a block by location and type. - - - - - - - - - - - - - - - Specify an item by location and type. - - - - - - - - - - - - - - - - - - - - - - - - - - Specify a container item by location and type and contents. - - - - - - - - - - - - - - - - - - - - - - Draw a sign at the required location with the specified text. - - - - - - - - - - - - - - - - - - - - - - - - Specify an entity by location and type. - - - - - - - - - - - - - - - - - - - - - Specify a filled cuboid by inclusive coordinates and block type. - - - - - - - - - - - - - - - - - - Specify a filled sphere by centre coordinates and inclusive radius. - - - - - - - - - - - - - - - - Specify a line by start and end coordinates and thickness. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Adds a series of joined rooms into the world. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Basic animation created by repeatedly applying a DrawingDecorator at different positions. - - - - - - - - - Create an animation where the (x,y,z) position are determined by parametric equations. Recognised tokens are: + + + + + The world seed - leave blank (default) to get a random world. + + + + + + + Set this to true to force the world to be reloaded, otherwise the current world will be used (provided it matches the requested seed). + Force reloading is slow, but will guarantee that no world changes will carry over between missions. + + + + + + + Set this to true to force the world data files to be deleted after the mission is done. + Enabling this setting prevents the disk being filled with old worlds. + + + + + - * basic arithmetic operations: +, -, /, *, % (modulo), ^ (to the power of) - * basic trig: sin, cos, tan, asin, acos, atan - * abs (absolute value) - * rand - replaces with a random float between 0 and 1 - * t - the integer time variable, incremented with each time interval + + + + Loads a saved world from disk. You can find the saved worlds in "{{{Minecraft\run\saves}}}". Use the full path to one of those folders. - For example, to create a structure which orbits in the x-z plane about the point (100,100,100) at a radius of 20, use: - "100+20*cos(t)" and "100+20*sin(t)" for the x and z strings. + If Minecraft is running on a different machine then copy the folder to a readable network location and update the path accordingly. Example: - The parser is not robust to mismatched brackets, typos, unrecognised tokens etc, and will fail silently. - - - - - - - - - + {{{<FileWorldGenerator src="\\\\machine-id\\shared\\ProjectMalmo\\saved_maps\\arena" />}}} + + + + + + + The path to the saved world folder. + + + + - - The master seed for the random number generator used for any stochastic elements. - + + Set this to true to force the world to be reloaded, otherwise the current world will be used (provided it matches the requested source filename). + Force reloading is slow, but will guarantee that no world changes will carry over between missions. + - - + + + - Either an integer number, or the string "random". + Set this to true to force the world data files to be deleted after the mission is done. + Enabling this setting prevents the disk being filled with old worlds. - - - - - - - - - - - - Create an animation where the (x,y,z) position is determined by the starting position, a constant velocity, and a bounding box. + + + + - Each time step, the position is updated by adding the velocity values. If the object goes outside of the bounding box in one dimension, that dimension's velocity will be flipped to reverse the direction. - - - - - - + + + + Generates a survival world with the specified biome. + + + + + - Define the bounds of the canvas within which to move the object. + The biome type for the world. Each chunk will be loaded with the biome specified. + + If left blank, the world will be a normal survival world. + + Biome ID #'s can be found here: https://minecraft.gamepedia.com/Biome#Biome_IDs - - - - + + + + - Define the starting position of the drawing's origin. + Set this to true to force the world to be reloaded, otherwise the current world will be used (provided it matches the requested seed). + Force reloading is slow, but will guarantee that no world changes will carry over between missions. - - - - + + + + - Define the initial velocity of the drawing's origin. + Set this to true to force the world data files to be deleted after the mission is done. + Enabling this setting prevents the disk being filled with old worlds. - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Creates a moving two-block target which takes random moves within a specified arena. Can be linked to the turn scheduler. + This can be made more general in the future, but is currently tailored specifically for the Malmo collaborative challenge. + + + + + + + + Define the bounds of the arena within which the target can move. + + - - - - - - + + + + + The master seed for the random number generator used to move the target. + + + + + + Either an integer number, or the string "random". + + + + + + + + + + + The length, in ticks, between each update, or the string "turnbased" to use the turn scheduler. + + + + + + Either an integer number, or the string "turnbased". + + + + + + + + + + + + + + + + + + Adds a snake made of blocks, that grows at one end and shrinks at the other. + + + + + + + + The master seed for the random number generator used to make the snake. + + + + + + Either an integer number, or the string "random". + + + + + + + + + + + Optional seed for determining block types. + + + + + + Either an integer number, or the string "random". + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - Define the drawing, relative to (0,0,0), which will be drawn for each frame of the animation. + Adds a maze into the world. - - - - + + + + + + + The master seed for the random number generator used to make the maze. + + + + + + Either an integer number, or the string "random". + + + + + + + + + + + Seed for the random number generator for determining block types - omit to allow master seed to control block types. + + + + + + Either an integer number, or the string "random". + + + + + + + + + + + + + + + + + + + + + + + + + + + + Omit this element if you want the optimal path to be unmarked. + + + + + + + Omit this element if you want the subgoal points to be unmarked. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - The number of server ticks between each update of the animation. - + + Base class for all draw objects. + - - - - - - - - Sets up a build battle area, with a source structure that can't be edited, and a goal structure, with optional recolouring of blocks to indicate correct/incorrect placement. - - NOTE: Make sure to add a {{{RewardForStructureCopying}}} handler to the AgentHandlers if you want your agent to be rewarded for contributing to the build. - - - - - - - - Define the bounds of the structure to be copied. Anything in this volume when the mission starts will be used as the blueprint - eg anything drawn here using the {{{DrawingDecorator}}}, etc. - - - - - - - Define the bounds in which the agent should build their copy. - - - - - - - If present, correctly placed blocks (in the source and the copy) will be changed to this block type. - - - - - - - If present, incorrectly placed blocks (in the copy only) will be changed to this block type. - - - - - - - - - - - - Helps specify where random blocks should be placed in the world. - - - - - - Specifies the origin point or spawn point in the Minecraft world. - - If omitted, set to the world spawn point. - - - - - - - Specifies the radius of the circle around which the block may be randomly placed. - - - - - - - Specifies the block type of the block to be randomly placed. - - - - - - - - Specifies the placement behavior of the block. Default is "surface." - - String of "sphere" will place equally randomly in a sphere of radius specified. - - String of "circle" will place randomly in a circle at the y-coordinate specified. - - String of "surface" will place randomly in a circle, then raise to the highest available block on the surface. - - - - - - - - - - - - - - - - Adds a specified block to the world and sets compass to that block. - - Block is placed randomly along a radius around the origin specified. - - Can force the block to appear at the highest available y-value. - - - - - - - - Properties for placing a block in the world randomly. - - - - - - - Sets whether or not the compass location should be randomized within a certain radius. - - If false, will set the compass location to the block that was randomly placed. - - If true, will set the compass location to a random spot within the radius specified below. - - - - - - - The minimum distance a randomized compass location must be from the block that was randomly placed. - - If omitted, set to 0 blocks. - - - - - - - The maximum distance a randomized compass location must be from the block that was randomly placed. - - If omitted, set to 8 blocks. - - - - - - - - - - - - - Specifies a time limit that applies to all agents. - - - - - - - - - - - - Specifies that the mission ends when any of the agents finish. - - - - - - - - - - - - - - - - Requests that 32bpp depth frames be sent. - - - - - - - - - - - - - - Requests an 8bpp grayscale image. - - - - - - - - - - - - - - - - - - - - - - - - - Requests a 24bpp colour map - each object/entity represented by a solid block of colour. - - - - - - - - - - - + + + + + Base class for all block-based draw objects. + + + + + + + + + + - - - - - - Requests that video frames be sent. - - - - - - - - - - - - - - - - - - - - - - - - - - - - Set to false to specify the min and max depths manually. Default is true, where uses the min and max depths in the scene. - - - - - - - + + - - If true, returns depth in the fourth channel as RGBDRGBD.... Else as RGBRGB... - + + Draws structures into the world. + - - + + + + + + + + - - Sets the camera viewpoint. 0 = first-person, 1 = behind, 2 = facing. - + + Specify a block by location and type. + - - - - - - - + + + + + + + - - - - - - - - - - - - Commands for smooth movement. Some examples: - - "{{{move 0.5}}}" - start moving forward at 50% of the normal walking speed (-ve = backward, +ve = forward). - - "{{{strafe -1}}}" - start moving left at 100% of the normal walking speed (-ve = left, +ve = right). - - "{{{pitch 0.1}}}" - start tilting the agent's head down at 10% of the maximum speed (-ve = up, +ve = down). The maximum speed is set by {{{turnSpeedDegs}}} in {{{ContinuousMovementCommands}}}. - - "{{{turn 0.1}}}" - start turning right at 10% of the maximum speed (-ve = anti-clockwise/left, +ve = clockwise/right). The maximum speed is set by {{{turnSpeedDegs}}} in {{{ContinuousMovementCommands}}}. - - "{{{jump 1}}}" - start jumping (1 = start, 0 = stop). - - "{{{crouch 1}}}" - start crouching (1 = start, 0 = stop). - - "{{{attack 1}}}" - start attacking (1 = start, 0 = stop). The 'attack' command is for destroying blocks and attacking mobs. - - "{{{use 1}}}" - start 'use'ing (1 = start, 0 = stop). The 'use' command is for placing blocks and for other things too. - - - - - - - - - - - - - - - - - - Commands to set position and orientation directly. Some examples: - - "{{{tp 23.5 1 -34.5}}}" - teleport the agent to the absolute position x y z (space delimited). - - "{{{tpx 230}}}" - set the agent's x coordinate, without altering the y and z. - - "{{{tpy 103.2}}}" - set the agent's y coordinate, without altering the x and z. - - "{{{tpz -32.5}}}" - set the agent's z coordinate, without altering the x and y. - - "{{{setYaw 30}}}" - set the agent's body orientation to be 30 degrees clockwise from south. - - "{{{setPitch 20}}}" - set the agent's body orientation to be 20 degrees down from horizontal. - - - - - - - - - - - - - - - - Commands for moving and turning in discrete increments. Some examples: - - "{{{move 1}}}" - move the agent one block forwards (1 = forwards, -1 = backwards). - - "{{{jumpmove 1}}}" - move the agent one block up and forwards (1 = forwards, -1 = backwards). - - "{{{strafe 1}}}" - move the agent one block sideways (1 = right, -1 = left). - - "{{{jumpstrafe 1}}}" - move the agent one block up and sideways (1 = right, -1 = left). - - "{{{turn 1}}}" - rotate the agent 90 degrees right (1 = right, -1 = left). - - "{{{movenorth 1}}}" - move the agent one block north. - - "{{{moveeast 1}}}" - move the agent one block east. - - "{{{movesouth 1}}}" - move the agent one block south. - - "{{{movewest 1}}}" - move the agent one block west. - - "{{{jumpnorth 1}}}" - move the agent one block up and north. - - "{{{jumpeast 1}}}" - move the agent one block up and east. - - "{{{jumpsouth 1}}}" - move the agent one block up and south. - - "{{{jumpwest 1}}}" - move the agent one block up and west. - - "{{{jump 1}}}" - move the agent one block up. - - "{{{look 1}}}" - look down by 45 degrees (-ve = up, +ve = down). - - "{{{attack 1}}}" - destroy the block that has focus. - - "{{{use 1}}}" - place the held block item on the block face that has focus. - - "{{{jumpuse}}}" - simultaneously jump and place the held block on the block face that has focus. - - - - - - - - - - - - - - - - - - - - - - - - - - - - Commands for changing the contents of the inventory and hotbar. - - 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: - - "{{{swapInventoryItems 13 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: - - "{{{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: - - "{{{hotbar.1 1}}}" - "{{{hotbar.1 0}}}" - - Send both commands to select hotbar slot 1 as the currently-held tool. This affects the attack and use commands - - e.g. if the agent does 'use' while holding a block item it will place the block into the world. - - If the agent is currently pointed at a container item - eg a chest, shulker box, dispenser etc - then the swap and combine commands - can be extended to allow access to the container's inventory. To do this, simply prefix the slot number with the name of the foreign - inventory (which will be provided by the {{{ObservationFromFullInventory}}} observations). - - So to move an item out of the hotbar and into slot 0 of a chest, say: - - "{{{swapInventoryItems 3 Chest:0}}}" - - Note that this is the same as writing - - "{{{swapInventoryItems Inventory:3 Chest:0}}}" - - "Inventory" is the name of the player's inventory. - - See inventory_test.py for a working example of this. - - - - - - - - - - - - - - - - - - - - - - A command for simple crafting: - - Will look up all recipes that produce the requested object, and attempt each one in turn until one is successful or all have failed. This ignores all issues like requiring a crafting table / brewing stand etc, and the shape of the recipe (which items go in which slots on the crafting table). It will simply check to see whether the player has the necessary raw ingredients, and, if so, will remove them from the player's inventory and add the result of the recipe. - - For basic objects, use the ItemTypes or BlockTypes found in Types.xsd. Eg: - - "{{{craft diamond_pickaxe}}}" will remove three diamonds and two sticks from the player's inventory, and add a diamond pickaxe. - - For more control over colours, types etc, add a Variation or Colour. Eg: - - "{{{craft carpet PINK}}}" - - etc. - - - - - - - - - - - A command for broadcasting text messages to the other players. An example: - - "{{{chat I have found diamonds!}}}" - broadcasts the string "{{{I have found diamonds!}}}". - - Chat messages from other players can be observed using {{{ObservationFromChat}}}. - - - - - - - - - - - A command for ending the mission, example: - - "{{{quit}}}" - terminates the current mission. - - - - - - - - - - - Commands for controlling Minecraft at the level of keyboard and mouse events. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Allow-list/deny-list base class - restricted by each command handler to only allow that handler's commands. - - - - - - - - - - - - - - - - - - - - - When present, the Mod will accept commands that control smooth movement. - - Commands take the form of "verb <value>" e.g. "{{{move 1}}}" to move forwards with normal speed. - - - - - - - - - - - - - - - - - - - - This sets the maximum speed for both turning the agent and adjusting the camera pitch, in degrees per second. - - The turn and pitch command values are both scaled by this - eg "{{{turn -0.5}}}" to turn left (anti-clockwise) at half this maximum speed. - - - + + + + + Specify an item by location and type. + + + + + + + + + + + + + - - - - - - When present, the Mod will accept commands to set the player's position and orientation directly. - - Commands take the form of "verb <value>" e.g. "{{{tpx 13}}}" to set the x-coordinate to 13. - - - - - - - - - - - - - - - - + + + + + + - - - - - - When present, the Mod will accept commands that control movement in discrete jumps. - - Commands take the form of "verb <value>" e.g. "{{{move 1}}}" to move forwards one square. - - - - - - - - - - + + + + + Specify a container item by location and type and contents. + + + + + + - - - - - - - + + + + + + + + + - - - - - - When present, the Mod will accept commands that control the player's inventory. - - - - - - - - - - - - - - - - + + + + + Draw a sign at the required location with the specified text. + + + + + + + + + + + + + + + + + + - - - - - - When present, the Mod will accept simple commands that implement a basic form of crafting. - - - - - - - - - - - - - - - - + + + + + Specify an entity by location and type. + + + + + + + + + + + + + + + - - - - - - When present, the Mod will accept commands that send chat messages to the other players. - - - - - - - - - - - - - - - - + + + + + Specify a filled cuboid by inclusive coordinates and block type. + + + + + + + + + + + + - - - - - - When present, the Mod will accept a command that quits the mission. - - - - - - - - - - - - - - - - - + + + + + Specify a filled sphere by centre coordinates and inclusive radius. + + + + + + + + + + - - - - - - Allows a user to specify that certain commands must be sent on a turn-by-turn basis - ie, in a multi-agent mission, placing the {{{DiscreteMovementCommand}}} handler inside the TurnBasedCommands section will mean that each agent must take it in turns to send a discrete movement command. See turn_based_test.py in the Python Samples for a demonstration/explanation of this. - - - - - - - - - - - + + + + + Specify a line by start and end coordinates and thickness. + + + + + + + + + + + + + - - - - - - When present, the Mod will accept commands on the level of mouse and keyboard events. - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + Adds a series of joined rooms into the world. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - When present, the Mod will return observations that say what commands have been acted on since the last report, in the JSON element {{{CommandsSinceLastObservation}}}. - - Note that the commands returned might not yet have taken effect, depending on the command and the way in which Minecraft responds to it - - but they will have been processed by the command handling chain. - - - - - - - - - Automatically addd by Malmo when the user specifies the {{{TurnBasedCommands}}} handler. This provides vital observations back to the agent to allow them to make use of the turn scheduler. When it is the agent's turn, the JSON will contain {{{turn_number}}} - an integer which tracks the number of turns the agent has taken, and {{{turn_key}}} - a one-shot key which must be passed back to Malmo as a parameter in {{{sendCommand}}} in order for the command to be accepted. - - - - - - - - - When present, the Mod will return observations that indicate the direction to follow to the next subgoal. - The value to turn by is returned in the JSON element {{{yawDelta}}}. - - - - - - + + + + + + + - - - - - - When present, the Mod will return observations that say what is in the hotbar. - - Up to four values are returned for each slot, if not empty: e.g. {{{Hotbar_1_size}}} and {{{Hotbar_1_item}}} containing the number and - type of the item(s) in the slot, respectively, and {{{Hotbar_1_colour}}} and {{{Hotbar_1_variant}}} if the item has a colour/variation. Hotbar slots are numbered 0 to 8 inclusive. - - - - - - - - - When present, the Mod will return several observations: - - * Achievement statistics: {{{DistanceTravelled}}}, {{{TimeAlive}}}, {{{MobsKilled}}}, {{{PlayersKilled}}}, {{{DamageTaken}}}, {{{DamageDealt}}} - * Life statistics: {{{Life}}}, {{{Score}}}, {{{Food}}}, {{{Air}}}, {{{XP}}}, {{{IsAlive}}}, {{{Name}}} - * Position statistics: {{{XPos}}}, {{{YPos}}}, {{{ZPos}}}, {{{Pitch}}}, {{{Yaw}}} - * Environment statistics: {{{WorldTime}}} - current time in ticks, {{{TotalTime}}} - total world time, unaffected by ServerInitialConditions - - - - - - - - - When present, the Mod will return low-level keyboard and mouse events. - - - - - - - - - When present, the Mod will return information on the current performance of the Minecraft system - eg tick speeds, etc. - - - - - - - - - When present, the Mod will return a JSON object called "LineOfSight", containing observations about the block or entity which is currently in the centre of the screen: - - * Hit details: {{{hitType}}} - will be "block" for a block, "entity" for an entity (eg spider, rabbit etc) or "item" for a free-floating item that can be picked up. {{{inRange}}} will be true if the block/entity is within the agent's reach - ie attacking or using will have an effect on the object. {{{distance}}} gives the straight-line distance from the agent. - * Position: {{{x}}}, {{{y}}}, {{{z}}} - in the case of block hits, will be the precise point when the ray intercepts the block. {{{yaw}}}, {{{pitch}}} are also added for entities. - * Type information: {{{type}}}, {{{colour}}}, {{{variant}}}, {{{facing}}} - * Extra properties: in the case of block types, any extra properties will be returned by their minecraft name, prefixed with "prop_" (eg, for leaves, "prop_decayable" and "prop_check_decay") - this is the same data as can be seen by exploring Minecraft with the F3 debug information displayed. For floating items, the stack size is returned in {{{stackSize}}} - * NBTTagCompound: for tile entity blocks, optionally returns a json object called "NBTTagCompound" which contains the entity's entire NBTTagCompound - eg useful for reading the text off signs, etc. Set {{{includeNBT}}} to true to receive this data. - - - - + + + + + + + + + + + + + + + + + + + + + + + + + Basic animation created by repeatedly applying a DrawingDecorator at different positions. + + + + + + + + + Create an animation where the (x,y,z) position are determined by parametric equations. Recognised tokens are: + + * basic arithmetic operations: +, -, /, *, % (modulo), ^ (to the power of) + * basic trig: sin, cos, tan, asin, acos, atan + * abs (absolute value) + * rand - replaces with a random float between 0 and 1 + * t - the integer time variable, incremented with each time interval + + For example, to create a structure which orbits in the x-z plane about the point (100,100,100) at a radius of 20, use: + "100+20*cos(t)" and "100+20*sin(t)" for the x and z strings. + + The parser is not robust to mismatched brackets, typos, unrecognised tokens etc, and will fail silently. + + + + + + + + + + + + The master seed for the random number generator used for any stochastic elements. + + + + + + Either an integer number, or the string "random". + + + + + + + + + + + + + Create an animation where the (x,y,z) position is determined by the starting position, a constant velocity, and a bounding box. + + Each time step, the position is updated by adding the velocity values. If the object goes outside of the bounding box in one dimension, that dimension's velocity will be flipped to reverse the direction. + + + + + + + + Define the bounds of the canvas within which to move the object. + + + + + + + Define the starting position of the drawing's origin. + + + + + + + Define the initial velocity of the drawing's origin. + + + + + + + + + + + Define the drawing, relative to (0,0,0), which will be drawn for each frame of the animation. + + + + + + + + The number of server ticks between each update of the animation. + + + + + + + + + + Sets up a build battle area, with a source structure that can't be edited, and a goal structure, with optional recolouring of blocks to indicate correct/incorrect placement. + + NOTE: Make sure to add a {{{RewardForStructureCopying}}} handler to the AgentHandlers if you want your agent to be rewarded for contributing to the build. + + + + + + + + Define the bounds of the structure to be copied. Anything in this volume when the mission starts will be used as the blueprint - eg anything drawn here using the {{{DrawingDecorator}}}, etc. + + + + + + + Define the bounds in which the agent should build their copy. + + + + + + + If present, correctly placed blocks (in the source and the copy) will be changed to this block type. + + + + + + + If present, incorrectly placed blocks (in the copy only) will be changed to this block type. + + + + + + + + + + + + Helps specify where random blocks should be placed in the world. + + + + + + Specifies the origin point or spawn point in the Minecraft world. + + If omitted, set to the world spawn point. + + + + + + + Specifies the minumum radius of the circle around which the block may be randomly placed. + + + + + + + Specifies the maximum radius of the circle around which the block may be randomly placed. + + + + + + + Specifies the block type of the block to be randomly placed. + + + + + + + + Specifies the placement behavior of the block. Default is "surface." + + String of "sphere" will place equally randomly in a sphere of radius specified. + + String of "circle" will place randomly in a circle at the y-coordinate specified. + + String of "surface" will place randomly in a circle, then raise to the highest available block on the surface. + + + + + + + + + + + + + Sets whether or not the target block radius should be randomized within a certain radius. + If false, will set the default target radius to the given radius. + If true, will set the default target radius randomly within the bound specified below. + + + + + + + The minimum distance for a randomly placed target block. + + If omitted, and randomization of radius enabled, set to 10 blocks. + + + + + + + The maximum distance for a randomly placed target block. + + If omitted, and randomization of radius enabled, set to 64 blocks. + + + + + + + + + + Adds a specified block to the world and sets compass to that block. + + Block is placed randomly along a radius around the origin specified. + + Can force the block to appear at the highest available y-value. + + + + + + + + Properties for placing a block in the world randomly. + + + + + + + Sets whether or not the compass location should be randomized within a certain radius. + If false, will set the compass location to the block that was randomly placed. + If true, will set the compass location to a random spot within the radius specified below. + + + + + + + The minimum distance a randomized compass location must be from the block that was randomly placed. + + If omitted, set to 0 blocks. + + + + + + + The maximum distance a randomized compass location must be from the block that was randomly placed. + + If omitted, set to 8 blocks. + + + + + + + + + + + + + Specifies a time limit that applies to all agents. + + + + + + + + + + + + Specifies that the mission ends when any of the agents finish. + + + + + + + + + + + + + + + + + Requests that 32bpp depth frames be sent. + + + + + + + + + + + + + + Requests an 8bpp grayscale image. + + + + + + + + + + + + + + + + + + + + + + + + + Requests a 24bpp colour map - each object/entity represented by a solid block of colour. + + + + + + + + + + + + + + + + + + Requests that video frames be sent. + + + + + + + + + + + + + + + + + + + + + + + + + + + + Set to false to specify the min and max depths manually. Default is true, where uses the min and max depths in the scene. + + + + + + + + + + If true, returns depth in the fourth channel as RGBDRGBD.... Else as RGBRGB... + + + + + + + Sets the camera viewpoint. 0 = first-person, 1 = behind, 2 = facing. + + + + + + + + + + + + + + + + + + + + + + Commands for smooth movement. Some examples: + + "{{{move 0.5}}}" - start moving forward at 50% of the normal walking speed (-ve = backward, +ve = forward). + + "{{{strafe -1}}}" - start moving left at 100% of the normal walking speed (-ve = left, +ve = right). + + "{{{pitch 0.1}}}" - start tilting the agent's head down at 10% of the maximum speed (-ve = up, +ve = down). The maximum speed is set by {{{turnSpeedDegs}}} in {{{ContinuousMovementCommands}}}. + + "{{{turn 0.1}}}" - start turning right at 10% of the maximum speed (-ve = anti-clockwise/left, +ve = clockwise/right). The maximum speed is set by {{{turnSpeedDegs}}} in {{{ContinuousMovementCommands}}}. + + "{{{jump 1}}}" - start jumping (1 = start, 0 = stop). + + "{{{crouch 1}}}" - start crouching (1 = start, 0 = stop). + + "{{{attack 1}}}" - start attacking (1 = start, 0 = stop). The 'attack' command is for destroying blocks and attacking mobs. + + "{{{use 1}}}" - start 'use'ing (1 = start, 0 = stop). The 'use' command is for placing blocks and for other things too. + + + + + + + + + + + + + + + + + + Commands to set position and orientation directly. Some examples: + + "{{{tp 23.5 1 -34.5}}}" - teleport the agent to the absolute position x y z (space delimited). + + "{{{tpx 230}}}" - set the agent's x coordinate, without altering the y and z. + + "{{{tpy 103.2}}}" - set the agent's y coordinate, without altering the x and z. + + "{{{tpz -32.5}}}" - set the agent's z coordinate, without altering the x and y. + + "{{{setYaw 30}}}" - set the agent's body orientation to be 30 degrees clockwise from south. + + "{{{setPitch 20}}}" - set the agent's body orientation to be 20 degrees down from horizontal. + + + + + + + + + + + + + + + + Commands for moving and turning in discrete increments. Some examples: + + "{{{move 1}}}" - move the agent one block forwards (1 = forwards, -1 = backwards). + + "{{{jumpmove 1}}}" - move the agent one block up and forwards (1 = forwards, -1 = backwards). + + "{{{strafe 1}}}" - move the agent one block sideways (1 = right, -1 = left). + + "{{{jumpstrafe 1}}}" - move the agent one block up and sideways (1 = right, -1 = left). + + "{{{turn 1}}}" - rotate the agent 90 degrees right (1 = right, -1 = left). + + "{{{movenorth 1}}}" - move the agent one block north. + + "{{{moveeast 1}}}" - move the agent one block east. + + "{{{movesouth 1}}}" - move the agent one block south. + + "{{{movewest 1}}}" - move the agent one block west. + + "{{{jumpnorth 1}}}" - move the agent one block up and north. + + "{{{jumpeast 1}}}" - move the agent one block up and east. + + "{{{jumpsouth 1}}}" - move the agent one block up and south. + + "{{{jumpwest 1}}}" - move the agent one block up and west. + + "{{{jump 1}}}" - move the agent one block up. + + "{{{look 1}}}" - look down by 45 degrees (-ve = up, +ve = down). + + "{{{attack 1}}}" - destroy the block that has focus. + + "{{{use 1}}}" - place the held block item on the block face that has focus. + + "{{{jumpuse}}}" - simultaneously jump and place the held block on the block face that has focus. + + + + + + + + + + + + + + + + + + + + + + + + + + + + Commands for changing the contents of the inventory and hotbar. + + 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: + + "{{{swapInventoryItems 13 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: + + "{{{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: + + "{{{hotbar.1 1}}}" + "{{{hotbar.1 0}}}" + + Send both commands to select hotbar slot 1 as the currently-held tool. This affects the attack and use commands + - e.g. if the agent does 'use' while holding a block item it will place the block into the world. + + If the agent is currently pointed at a container item - eg a chest, shulker box, dispenser etc - then the swap and combine commands + can be extended to allow access to the container's inventory. To do this, simply prefix the slot number with the name of the foreign + inventory (which will be provided by the {{{ObservationFromFullInventory}}} observations). + + So to move an item out of the hotbar and into slot 0 of a chest, say: + + "{{{swapInventoryItems 3 Chest:0}}}" + + Note that this is the same as writing + + "{{{swapInventoryItems Inventory:3 Chest:0}}}" + + "Inventory" is the name of the player's inventory. + + See inventory_test.py for a working example of this. + + + + + + + + + + + + + + + + + + + + + + A command for simple crafting: + + "{{{craft carpet PINK}}}" + + etc. + + + + + + + + + + + A command for simple nearby crafting: + + "{{{craftNearby carpet PINK}}}" + + etc. + + + + + + + + + + + A command for simple smelting: + + "{{{smeltNearby carpet PINK}}}" + + etc. + + + + + + + + + + + A command for placing blocks in the agent's inventory: + + Will look in the agent's inventory. If the block exists, will try to place the block in the world. + + For basic objects, use BlockTypes found in Types.xsd. Eg: + + "{{{place diamond_block}}}" will attempt to place a diamond block from the agent's inventory. + + for more control over colours, types, etc, add a Variation or Colour. Eg: + + "{{{place carpet PINK}}}" + + + + + + + + + + + A command for broadcasting text messages to the other players. An example: + + "{{{chat I have found diamonds!}}}" - broadcasts the string "{{{I have found diamonds!}}}". + + Chat messages from other players can be observed using {{{ObservationFromChat}}}. + + + + + + + + + + + A command for ending the mission, example: + + "{{{quit}}}" - terminates the current mission. + + + + + + + + + + + Commands for controlling Minecraft at the level of keyboard and mouse events. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Allow-list/deny-list base class - restricted by each command handler to only allow that handler's commands. + + + + + + + + + + + + + - - - - - - When present, the Mod will return observations that describe the contents of the player's inventory. - There are two modes - "flat" (the default) is provided for backwards compatibility, and behaves like this: - - The inventory contents are returned in a flat format in the root of the json observations. - Up to four values are returned for each slot, if not empty: e.g. {{{InventorySlot_0_size}}} and {{{InventorySlot_0_item}}} containing the number and - type of the item(s) in the slot, respectively, and {{{InventorySlot_0_colour}}} and {{{InventorySlot_0_variant}}} if the item has a colour/variation. - Inventory slots are numbered 0 to 39 inclusive. - If there is a container item available (eg the player is pointed at a chest), this will be returned in the same way, but "InventorySlot" - will be replaced by "ContainerNameSlot" - eg {{{ShulkerBoxSlot_0_item}}} etc. - - If {{{flat}}} is false (recommended), the data is returned as an array of objects, one for each item in the inventory/inventories. - The JSON array is called "inventory", and each item in the array will contain: - * {{{type}}} - the type of the object in that ItemStack - * {{{colour}}} - the colour, if relevant - * {{{variant}}} - the variant, if relevant - * {{{quantity}}} - the number of objects in the ItemStack - * {{{index}}} - the slot number - * {{{inventory}}} - the name of the inventory - will be "Inventory" for the player, or, for example, "ShulkerBox", "Chest" etc, if a container is available. - This index and inventory information can be used to specify the item in the {{{InventoryCommands}}} - items are specified as inventory:index - - eg "ShulkerBox:12" - - In addition to this information, whether {{{flat}}} is true or false, an array called "inventoriesAvailable" will also be returned. - This will contain a list of all the inventories available (usually just the player's, but if the player is pointed at a container, this - will also be available.) - For each inventory, an object will be returned that specifies: - * {{{name}}} - the inventory name (same as will be returned in the {{{inventory}}} field for any items in that inventory) - * {{{size}}} - the number of slots the inventory provides. - - For a working example please see inventory_test.py in the Python samples folder. - - - - + + + + + + + When present, the Mod will accept commands that control smooth movement. + + Commands take the form of "verb <value>" e.g. "{{{move 1}}}" to move forwards with normal speed. + + + + + + + + + + + + + + + + + + + + This sets the maximum speed for both turning the agent and adjusting the camera pitch, in degrees per second. + + The turn and pitch command values are both scaled by this - eg "{{{turn -0.5}}}" to turn left (anti-clockwise) at half this maximum speed. + + + + + + + + + + When present, the Mod will accept commands to set the player's position and orientation directly. + + Commands take the form of "verb <value>" e.g. "{{{tpx 13}}}" to set the x-coordinate to 13. + + + + + + + + + + + + + + + + + + + + + + + When present, the Mod will accept commands that control movement in discrete jumps. + + Commands take the form of "verb <value>" e.g. "{{{move 1}}}" to move forwards one square. + + + + + + + + + + + + + + + + + + + + + + + + + When present, the Mod will accept commands that control the player's inventory. + + + + + + + + + + + + + + + + + + + + + + + When present, the Mod will accept simple commands that implement a basic form of crafting. + + + + + + + + + + + + + + + + + + + + + + + + When present, the Mod will accept simple commands that implement a basic form of crafting. Success of the craft command depends on the presence of a nearby crafting table previously placed by the agent and within reach and in field of view. + + + + + + + + + + + + + + + + + + + + + + + + When present, the Mod will accept simple commands that implement a basic form of smelting. Success of the smelt command depends on the presence of a nearby furnace previously placed by the agent and within reach and in field of view. Fails when a command corresponds to an item not able to be smelted. Each command takes fuel as if the agent had placed the items in a furnace. + + Eg: + + If called on 16 iron ore, requires 2 coal, 2 charcoal, 11 planks, etc. + + If called twice separately on 8 iron ore, requires 1 coal and then 1 coal, etc. + + + + + + + + + + + + + + + + + + + + + + + + When present, the Mod will accept commands that allow for the placement of blocks. If the specified block is contained in the agent's inventory, then the agent will attempt to place the block in the world. + + If a non-block is specified, the command fails. + + If a block is specified, the block will swap to the hotbar, be placed, and then swapped back, so no changes to the inventory are made besides losing a block. + + + + + + + + + + + + + + + + + + + + + + + When present, the Mod will accept commands that send chat messages to the other players. + + + + + + + + + + + + + + + + + + + + + + + When present, the Mod will accept a command that quits the mission. + + + + + + + + + + + + + + + + + + + + + + + + Allows a user to specify that certain commands must be sent on a turn-by-turn basis - ie, in a multi-agent mission, placing the {{{DiscreteMovementCommand}}} handler inside the TurnBasedCommands section will mean that each agent must take it in turns to send a discrete movement command. See turn_based_test.py in the Python Samples for a demonstration/explanation of this. + + + + + + + + + + + + + + + + + + When present, the Mod will accept commands on the level of mouse and keyboard events. + + + + + + + + + + + + + + + + + + + + + + + + + When present, the Mod will return observations that say what commands have been acted on since the last report, in the JSON element {{{CommandsSinceLastObservation}}}. + + Note that the commands returned might not yet have taken effect, depending on the command and the way in which Minecraft responds to it - + but they will have been processed by the command handling chain. + + + + + + + + + Automatically addd by Malmo when the user specifies the {{{TurnBasedCommands}}} handler. This provides vital observations back to the agent to allow them to make use of the turn scheduler. When it is the agent's turn, the JSON will contain {{{turn_number}}} - an integer which tracks the number of turns the agent has taken, and {{{turn_key}}} - a one-shot key which must be passed back to Malmo as a parameter in {{{sendCommand}}} in order for the command to be accepted. + + + + + + + + + When present, the Mod will return observations that indicate the direction to follow to the next subgoal. + The value to turn by is returned in the JSON element {{{yawDelta}}}. + + + + + + + + + + + + + When present, the Mod will return observations that say what is in the hotbar. + + Up to four values are returned for each slot, if not empty: e.g. {{{Hotbar_1_size}}} and {{{Hotbar_1_item}}} containing the number and + type of the item(s) in the slot, respectively, and {{{Hotbar_1_colour}}} and {{{Hotbar_1_variant}}} if the item has a colour/variation. Hotbar slots are numbered 0 to 8 inclusive. + + + + + + + + + When present, the Mod will return several observations: + + * Achievement statistics: {{{DistanceTravelled}}}, {{{TimeAlive}}}, {{{MobsKilled}}}, {{{PlayersKilled}}}, {{{DamageTaken}}}, {{{DamageDealt}}} + * Life statistics: {{{Life}}}, {{{Score}}}, {{{Food}}}, {{{Air}}}, {{{XP}}}, {{{IsAlive}}}, {{{Name}}} + * Position statistics: {{{XPos}}}, {{{YPos}}}, {{{ZPos}}}, {{{Pitch}}}, {{{Yaw}}} + * Environment statistics: {{{WorldTime}}} - current time in ticks, {{{TotalTime}}} - total world time, unaffected by ServerInitialConditions + + + + + + + + + When present, the Mod will return low-level keyboard and mouse events. + + + + + + + + + When present, the Mod will return information on the current performance of the Minecraft system - eg tick speeds, etc. + + + + + + + + + When present, the Mod will return a JSON object called "LineOfSight", containing observations about the block or entity which is currently in the centre of the screen: + + * Hit details: {{{hitType}}} - will be "block" for a block, "entity" for an entity (eg spider, rabbit etc) or "item" for a free-floating item that can be picked up. {{{inRange}}} will be true if the block/entity is within the agent's reach - ie attacking or using will have an effect on the object. {{{distance}}} gives the straight-line distance from the agent. + * Position: {{{x}}}, {{{y}}}, {{{z}}} - in the case of block hits, will be the precise point when the ray intercepts the block. {{{yaw}}}, {{{pitch}}} are also added for entities. + * Type information: {{{type}}}, {{{colour}}}, {{{variant}}}, {{{facing}}} + * Extra properties: in the case of block types, any extra properties will be returned by their minecraft name, prefixed with "prop_" (eg, for leaves, "prop_decayable" and "prop_check_decay") - this is the same data as can be seen by exploring Minecraft with the F3 debug information displayed. For floating items, the stack size is returned in {{{stackSize}}} + * NBTTagCompound: for tile entity blocks, optionally returns a json object called "NBTTagCompound" which contains the entity's entire NBTTagCompound - eg useful for reading the text off signs, etc. Set {{{includeNBT}}} to true to receive this data. + + + + + + + + + + + When present, the Mod will return observations that describe the contents of the player's inventory. + There are two modes - "flat" (the default) is provided for backwards compatibility, and behaves like this: + + The inventory contents are returned in a flat format in the root of the json observations. + Up to four values are returned for each slot, if not empty: e.g. {{{InventorySlot_0_size}}} and {{{InventorySlot_0_item}}} containing the number and + type of the item(s) in the slot, respectively, and {{{InventorySlot_0_colour}}} and {{{InventorySlot_0_variant}}} if the item has a colour/variation. + Inventory slots are numbered 0 to 39 inclusive. + If there is a container item available (eg the player is pointed at a chest), this will be returned in the same way, but "InventorySlot" + will be replaced by "ContainerNameSlot" - eg {{{ShulkerBoxSlot_0_item}}} etc. + + If {{{flat}}} is false (recommended), the data is returned as an array of objects, one for each item in the inventory/inventories. + The JSON array is called "inventory", and each item in the array will contain: + * {{{type}}} - the type of the object in that ItemStack + * {{{colour}}} - the colour, if relevant + * {{{variant}}} - the variant, if relevant + * {{{quantity}}} - the number of objects in the ItemStack + * {{{index}}} - the slot number + * {{{inventory}}} - the name of the inventory - will be "Inventory" for the player, or, for example, "ShulkerBox", "Chest" etc, if a container is available. + This index and inventory information can be used to specify the item in the {{{InventoryCommands}}} - items are specified as inventory:index - + eg "ShulkerBox:12" + + In addition to this information, whether {{{flat}}} is true or false, an array called "inventoriesAvailable" will also be returned. + This will contain a list of all the inventories available (usually just the player's, but if the player is pointed at a container, this + will also be available.) + For each inventory, an object will be returned that specifies: + * {{{name}}} - the inventory name (same as will be returned in the {{{inventory}}} field for any items in that inventory) + * {{{size}}} - the number of slots the inventory provides. + + For a working example please see inventory_test.py in the Python samples folder. + + + + + + + + + + + When present, the Mod will return an observation of the player's position that is unique for every cell on the x/z plane. + This is useful for discrete-movement missions where we need to uniquely identify their location but don't mind how. + + The observation will contain the JSON element {{{cell}}} containing e.g. {{{(2,4)}}} if the player is standing at any location where + x = 2 and z = 4. + + + + + + + + + + + + - - - - - - When present, the Mod will return an observation of the player's position that is unique for every cell on the x/z plane. - This is useful for discrete-movement missions where we need to uniquely identify their location but don't mind how. - - The observation will contain the JSON element {{{cell}}} containing e.g. {{{(2,4)}}} if the player is standing at any location where - x = 2 and z = 4. - - - - - - - - + + + + + When present, the Mod will return an observation that specifies the distance to a named location. + + A JSON element {{{distanceFromNAME}}} will be returned (where {{{NAME}}} is replaced with the name of the NamedPoint), + with a value that is the distance. + + + + + + + + + + + + + {{{name}}} - Each grid has a text label to identify it. + + {{{absoluteCoords}}} - If true, the min and max coordinates of the grid are interpreted as world coordinates. If false (the default) + then the coordinates are relative to the player. + + {{{min}}}, {{{max}}} - The corners of the grid. + + + + + + - - - - - - - - - When present, the Mod will return an observation that specifies the distance to a named location. - - A JSON element {{{distanceFromNAME}}} will be returned (where {{{NAME}}} is replaced with the name of the NamedPoint), - with a value that is the distance. - - - - - - + - - - - - - {{{name}}} - Each grid has a text label to identify it. - - {{{absoluteCoords}}} - If true, the min and max coordinates of the grid are interpreted as world coordinates. If false (the default) - then the coordinates are relative to the player. - - {{{min}}}, {{{max}}} - The corners of the grid. - - - - - - - - - - - - - - When present, the Mod will return observations that say what the nearby blocks are. - - For each {{{Grid}}} entry, a named JSON element will be returned with a 1D array of block types, in order along the x, then z, then y axes. - - - - - - + + + + + When present, the Mod will return observations that say what the nearby blocks are. + + For each {{{Grid}}} entry, a named JSON element will be returned with a 1D array of block types, in order along the x, then z, then y axes. + + + + + + + + + + + + + Used by {{{ObservationFromNearbyEntities}}}. Defines the range within which entities will be returned. Eg a range of 10,1,10 will return all entities within +/-10 blocks of the agent in the x and z axes, and within +/-1 block vertically. + + {{{update_frequency}}} is measured in Minecraft world ticks, and allows the user to specify how often they would like to receive each observation. A value of 20, under normal Minecraft running conditions, for example, would return the entity list once per second. + + + + + + + - - - - - - Used by {{{ObservationFromNearbyEntities}}}. Defines the range within which entities will be returned. Eg a range of 10,1,10 will return all entities within +/-10 blocks of the agent in the x and z axes, and within +/-1 block vertically. - - {{{update_frequency}}} is measured in Minecraft world ticks, and allows the user to specify how often they would like to receive each observation. A value of 20, under normal Minecraft running conditions, for example, would return the entity list once per second. - - - - - - - - - - - - - When present, the Mod will return observations that list the positions of all entities that fall within the given ranges of the agent. - - A JSON array will be returned for each range requested, named using the {{{name}}} attribute of the range. Within the array will be a series of elements, one for each entity, containing the following: - - - name: a string describing the entity (eg from Types.xsd) - - - x: the x position of the entity - - - y: the y position of the entity - - - z: the z position of the entity - - - quantity: if items have been grouped together by Minecraft, this indicates the number in the stack - - - colour: if the item is a tile entity, with a colour, this will be present to describe the colour - - - variation: optional string to describe the variation - eg the type of egg, or brick, etc (see Types.xsd) - - - - - - + + + + + When present, the Mod will return observations that list the positions of all entities that fall within the given ranges of the agent. + + A JSON array will be returned for each range requested, named using the {{{name}}} attribute of the range. Within the array will be a series of elements, one for each entity, containing the following: + + - name: a string describing the entity (eg from Types.xsd) + + - x: the x position of the entity + + - y: the y position of the entity + + - z: the z position of the entity + + - quantity: if items have been grouped together by Minecraft, this indicates the number in the stack + + - colour: if the item is a tile entity, with a colour, this will be present to describe the colour + + - variation: optional string to describe the variation - eg the type of egg, or brick, etc (see Types.xsd) + + + + + + + + + + + + + When present, the Mod will return observations that say what chat messages have occurred and from which player. + It will also return messages for any title or subtitle change (eg via Minecraft's title command) + + A JSON element {{{Chat}}} will be returned, with a list of chat strings. + In the same way, title changes and subtitle changes will be returned in {{{Title}}} and {{{Subtitle}}} respectively. + + Note that unless the AgentHost ObservationsPolicy is set to KEEP_ALL_OBSERVATIONS it is likely that chat messages will be missed. + The default policy is LATEST_OBSERVATION_ONLY. + + + + + + + + + + When present, the Mod will return observations that detail how the agent is facing and what position the agent is in with respect to a compass. + + A JSON element will be returned for the compass in the agent's inventory. The JSON will contain the following + + - set: boolean on whether the compass is set + + - compass-x: the x coordinate value of the set compass, null if not set + + - compass-y: the y coordinate value of the set compass, null if not set + + - compass-z: the z coordinate value of the set compass, null if not set + + - relative-x: the relative x coordinate value of the player to the compass, null if not set + + - relative-y: the relative y coordinate value of the player to the compass, null if not set + + - relative-z: the relative z coordinate value of the player to the compass, null if not set + + - offset: the number of degrees the agent is facing away from the direction the compass is pointing + + - normalized-offset: the number of degrees the agent is facing away, with the agent considered 0 degrees + + - distance: the distance from the agent's location to the compass's location + + + + + + + + + + + + + For multi-dimensional rewards, specifies the dimension to allocate this reward to. All rewards on this dimension will be summed. + + + + + + + + + + + + + + + + + + + + + - - - - - - When present, the Mod will return observations that say what chat messages have occurred and from which player. - It will also return messages for any title or subtitle change (eg via Minecraft's title command) - - A JSON element {{{Chat}}} will be returned, with a list of chat strings. - In the same way, title changes and subtitle changes will be returned in {{{Title}}} and {{{Subtitle}}} respectively. - - Note that unless the AgentHost ObservationsPolicy is set to KEEP_ALL_OBSERVATIONS it is likely that chat messages will be missed. - The default policy is LATEST_OBSERVATION_ONLY. - - - - - - - - - When present, the Mod will return observations that detail how the agent is facing and what position the agent is in with respect to a compass. - - A JSON element will be returned for the compass in the agent's inventory. The JSON will contain the following - - - set: boolean on whether the compass is set - - - compass-x: the x coordinate value of the set compass, null if not set - - - compass-y: the y coordinate value of the set compass, null if not set - - - compass-z: the z coordinate value of the set compass, null if not set - - - relative-x: the relative x coordinate value of the player to the compass, null if not set - - - relative-y: the relative y coordinate value of the player to the compass, null if not set - - - relative-z: the relative z coordinate value of the player to the compass, null if not set - - - offset: the number of degrees the agent is facing away from the direction the compass is pointing - - - normalized-offset: the number of degrees the agent is facing away, with the agent considered 0 degrees - - - distance: the distance from the agent's location to the compass's location - - - - - - - - - - - For multi-dimensional rewards, specifies the dimension to allocate this reward to. All rewards on this dimension will be summed. - - - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - Sends a reward when an entity is damaged. - - - - - - - - - - - - - Sends a reward when a specified position is reached by the agent. - - - - - - - + + + + + Sends a reward when an entity is damaged. + + + + + + + + + + + + + + Sends a reward when a specified position is reached by the agent. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sends a rewards when an agent comes in contact with a specific block type. + + + + + + + + + + + + + + Sends a reward when the agent issues a command. + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + Sends a reward when the agent sends a chat message that matches a given regular expression (supports Java regex syntax). + + + + + + + + + + + - - - - - - - - - + + + + + + + Sends a reward when the agent collects a specific item. + + + + + + + + + + + + + + Sends a reward when the agent collects a specific item. + + If Sparse is set to true, will only give full reward on collecting entire amount. + + Otherwise, will give the reward amount notated for each item collected up to the amount noted. + + + + + + + + + + + + + + + Sends a reward when the agent collects a specific item. + + If Sparse is set to true, will only give full reward on possessing the entire amount. + + Otherwise, will give the rewards amount notated for each item crafted up to the amount noted. + + + + + + + + + + + + + + + Sends a reward when the agent crafts a specific item. + + If Sparse is set to true, will only give full reward on crafting of entire amount. + + Otherwise, will give the reward amount notated for each item crafted up to the amount noted. + + + + + + + + + + + + + + + Sends a reward when the agent smelts a specific item. + + If Sparse is set to true, will only give full reward on crafting of entire amount. + + Otherwise, will give the reward amount notated for each item crafted up to the amount noted. + + + + + + + + + + + + + + + Sends a reward when an agent discards a specific item. + + + + + + + + + + + - - - - - - - - - - - - - - - - - Sends a rewards when an agent comes in contact with a specific block type. - - - - - - - + - - - - - - Sends a reward when the agent issues a command. - - - - - - - - - - - - - - - - - - - - Sends a reward when the agent sends a chat message that matches a given regular expression (supports Java regex syntax). - - - - - - - - - - - - - - - - - - - - Sends a reward when the agent collects a specific item. - - - - - - - - - - - - - - Sends a reward when an agent discards a specific item. - - - - - - - - - - - - - - - - - - - - Sends a reward when the mission ends for a specified reason. - - - - - - - - - + + + + + Sends a reward when the mission ends for a specified reason. + + + + + + + + + + + + + + + + Reward type for {{{RewardForStructureCopying}}}: + + - {{{PER_BLOCK}}} - reward will be given whenever a block is placed/destroyed in the goal volume, and will be determined by {{{RewardScale}}}. An additional reward of {{{RewardForCompletion}}} will be added the *first* time the copy is correctly completed. + + - {{{MISSION_END}}} - no reward will be given until the mission ends. Reward will be scaled from 0-1, where 0 means "no blocks correct" and 1 means "all blocks correct", and then multipled by {{{RewardScale}}}. + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - Reward type for {{{RewardForStructureCopying}}}: - - - {{{PER_BLOCK}}} - reward will be given whenever a block is placed/destroyed in the goal volume, and will be determined by {{{RewardScale}}}. An additional reward of {{{RewardForCompletion}}} will be added the *first* time the copy is correctly completed. - - - {{{MISSION_END}}} - no reward will be given until the mission ends. Reward will be scaled from 0-1, where 0 means "no blocks correct" and 1 means "all blocks correct", and then multipled by {{{RewardScale}}}. - - - - - - - - - - - + + + + + Sends a reward when the agent copies blocks from a given structure. + + NOTE: This will do nothing unless you have set up a {{{BuildBattleDecorator}}} on the server. + + - - - + + + + + + If present, the mission will end when the copy exactly matches the original. Set {{{description}}} to the quit code you'd like to receive when this happens. (See {{{RewardForMissionEnd}}}.) + + + + + + + + + + + + This is the reward to be added or deducted for each block event. + + The rewards are calculated as follows: + - For destroying a correct block: -1 * rewardScale + - For destroying an incorrect block: +1 * rewardScale + - For placing a correct block: +1 * rewardScale + - For placing an incorrect block: -1 * rewardScale + + If {{{RewardDensity}}} is set to {{{{PER_BLOCK}}}, this reward will arrive after each block transaction. If it's set to {{{MISSION_END}}}, the individual rewards will not be sent, but will be summed throughout the course of the mission, and the resulting total will be sent as the final reward. + + + + + + + + This is the extra reward to be added when the copy exactly matches the original. (Will only be applied once.) + + + - - + + + + + + Reward type for {{{RewardForTimeTaken}}}: + + - {{{PER_TICK}}} - only the reward delta will be sent as a reward, at each tick. The initial reward will be ignored. + + - {{{PER_TICK_ACCUMULATED}}} - the initial reward will be adjusted by the delta at each tick, and the current total sent. Eg: if the initial reward is 1000 and the delta is -1, the agent will receive rewards of 1000,999,998,997 for the first four ticks. + + - {{{MISSION_END}}} - the initial reward will be adjusted by the delta at each tick, but no reward will be sent until the mission ends. Eg: if the initial reward is 1000, the delta is -1, and the mission runs for 800 ticks, then final reward sent will be 200. + + + + + + + + + + + + + Reward that is dependent on time. Can be received per tick, or just once at the end. + + - - - + + + + + - - - - - - - - Sends a reward when the agent copies blocks from a given structure. - - NOTE: This will do nothing unless you have set up a {{{BuildBattleDecorator}}} on the server. - - - - - - - - - If present, the mission will end when the copy exactly matches the original. Set {{{description}}} to the quit code you'd like to receive when this happens. (See {{{RewardForMissionEnd}}}.) - - - - - - - - - - - - This is the reward to be added or deducted for each block event. - - The rewards are calculated as follows: - - For destroying a correct block: -1 * rewardScale - - For destroying an incorrect block: +1 * rewardScale - - For placing a correct block: +1 * rewardScale - - For placing an incorrect block: -1 * rewardScale - - If {{{RewardDensity}}} is set to {{{{PER_BLOCK}}}, this reward will arrive after each block transaction. If it's set to {{{MISSION_END}}}, the individual rewards will not be sent, but will be summed throughout the course of the mission, and the resulting total will be sent as the final reward. - - - - - - + + + + - This is the extra reward to be added when the copy exactly matches the original. (Will only be applied once.) + Reward type for {{{RewardForDistanceTraveledToCompassTarget}}}: + + - {{{PER_TICK}}} - reward the distance traveled twards compass target each tick + + - {{{PER_TICK_ACCUMULATED}}} - the reward will be adjusted by the delta at each tick, and the current total sent. E.G. If the agent moves forward to the target and then back to start the reward will be 0 + + - {{{MISSION_END}}} - the reward will be adjusted by the delta at each tick, but no reward will be sent until the mission ends. Eg: if the initial reward is 1000, the delta is -1, and the mission runs for 800 ticks, then final reward sent will be 200. - - - - - - - - - Reward type for {{{RewardForTimeTaken}}}: - - - {{{PER_TICK}}} - only the reward delta will be sent as a reward, at each tick. The initial reward will be ignored. - - - {{{PER_TICK_ACCUMULATED}}} - the initial reward will be adjusted by the delta at each tick, and the current total sent. Eg: if the initial reward is 1000 and the delta is -1, the agent will receive rewards of 1000,999,998,997 for the first four ticks. - - - {{{MISSION_END}}} - the initial reward will be adjusted by the delta at each tick, but no reward will be sent until the mission ends. Eg: if the initial reward is 1000, the delta is -1, and the mission runs for 800 ticks, then final reward sent will be 200. - - - - - - - - - - - - - Reward that is dependent on time. Can be received per tick, or just once at the end. - - - - - - - - - - + + + + + + + - - - - - - - - - - - - - - Reward for cornering a mob, such that it cannot move from its current square without passing through an agent. - If {{{global}}} is true then the agent doesn't have to be involved in catching the mob; otherwise they must be adjacent to the mob. - For our purposes, a mob is deemed "caught" if there are no unoccupied air blocks immediately north, south, east or west of them for them to move into. (An air block is considered occupied if there is an agent standing in it.) This does not necessarily correspond to Minecraft's definition of caught, in which mobs can escape by jumping or passing through agents. - If {{{oneshot}}} is true, the reward will only be counted once per entity. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - When this is included the agent's mission will end when they reach a specified position. - - - - - - + + + + Reward that is dependent on distance to compass target. Can be received per tick, or just once at the end. + + + + + + + + + + + + + + + + + + - - - - - - When this is included the agent's mission will end when a certain amount of time has elapsed. - - - - - + + + + + Reward for cornering a mob, such that it cannot move from its current square without passing through an agent. + If {{{global}}} is true then the agent doesn't have to be involved in catching the mob; otherwise they must be adjacent to the mob. + For our purposes, a mob is deemed "caught" if there are no unoccupied air blocks immediately north, south, east or west of them for them to move into. (An air block is considered occupied if there is an agent standing in it.) This does not necessarily correspond to Minecraft's definition of caught, in which mobs can escape by jumping or passing through agents. + If {{{oneshot}}} is true, the reward will only be counted once per entity. + + + + + + + + + + + + + + + + + + + - - - - - - When this is included the agent's mission will end when they come in contact with a specified block type. - - - - - - + + + + + + + - - - - - - When this is included the agent's mission will end when they collect (or craft) a specified item. - - - - - - + + + + + + + - - - - - - - - - - - - - - - - Agent's mission will end when they corner a mob. If {{{global}}} is true then the agent doesn't have to be involved in catching the mob; otherwise they must be adjacent to the mob. - For our purposes, a mob is deemed "caught" if there are no unoccupied air blocks immediately north, south, east or west of them for them to move into. (An air block is considered occupied if there is an agent standing in it.) This does not necessarily correspond to Minecraft's definition of caught, in which mobs can escape by jumping or passing through agents. - - - - - - + + + + + When this is included the agent's mission will end when they reach a specified position. + + + + + + + + + + + + + When this is included the agent's mission will end when a certain amount of time has elapsed. + + + + + + + + + + + + When this is included the agent's mission will end when they come in contact with a specified block type. + + + + + + + + + + + + + When this is included the agent's mission will end when they collect (or craft) a specified item. + + + + + + + + + + + + + When this is included the agent's mission will end when they collect (or craft) a specified number of the item. + + + + + + + + + + + + + When this is included the agent's mission will end when they craft a specified item. + + + + + + + + + + + + + When this is included the agent's mission will end when they smelt a specified item. + + + + + + + + + + + + + When this is included the agent's mission will end when they possess the specified item in their inventory all at once. + + + + + + + + + + + + + + + + + - - - - - - Set up a quota for a group of commands. {{{AgentQuitFromReachingCommandQuota}}} will fire once the quota is exceeded. - - - - - - List of commands, separated by spaces, that will share this quota. Commands must be valid members of {{{ContinuousMovementCommand}}}, {{{AbsoluteMovementCommand}}}, {{{DiscreteMovementCommand}}}, {{{InventoryCommand}}}, or {{{ChatCommand}}}. - - For instance, if the command list contains {{{moveeast}}}, {{{movenorth}}}, {{{movesouth}}} and {{{movewest}}}, then the mission will end once the summed total usage of all four commands reaches the quota - even if the agent never used {{{movesouth}}}. - - - - - - - Total number of usages allocated for this command group. - - - - - - - String that will be returned from the {{{AgentQuitFromReachingCommandQuota}}} if this quota is reached. This can be used in {{{RewardForMissionEnd}}}, and is also returned in the {{{MissionEnded}}} message. - - - - - - - - - Count the commands acted on by the Mod, and signal the end of the mission when the defined quota of commands has been reached. - - A total number of commands can be specified, and/or groups of commands can be given their own quota. - - - - - - - - - - Total number of commands allowed before the mission ends. (Note that a command must be acted on to be counted - sending malformed commands won't affect the total.) - - The check for total command use takes precedence over the individual group quotas, if used. - - - - - - - String that will be returned from the {{{AgentQuitFromReachingCommandQuota}}} if the total allowed command usage is reached. This can be used in {{{RewardForMissionEnd}}}, and is also returned in the {{{MissionEnded}}} message. - - - + + + + + Agent's mission will end when they corner a mob. If {{{global}}} is true then the agent doesn't have to be involved in catching the mob; otherwise they must be adjacent to the mob. + For our purposes, a mob is deemed "caught" if there are no unoccupied air blocks immediately north, south, east or west of them for them to move into. (An air block is considered occupied if there is an agent standing in it.) This does not necessarily correspond to Minecraft's definition of caught, in which mobs can escape by jumping or passing through agents. + + + + + + + + + + + + + Set up a quota for a group of commands. {{{AgentQuitFromReachingCommandQuota}}} will fire once the quota is exceeded. + + + + + + List of commands, separated by spaces, that will share this quota. Commands must be valid members of {{{ContinuousMovementCommand}}}, {{{AbsoluteMovementCommand}}}, {{{DiscreteMovementCommand}}}, {{{InventoryCommand}}}, or {{{ChatCommand}}}. + + For instance, if the command list contains {{{moveeast}}}, {{{movenorth}}}, {{{movesouth}}} and {{{movewest}}}, then the mission will end once the summed total usage of all four commands reaches the quota - even if the agent never used {{{movesouth}}}. + + + + + + + Total number of usages allocated for this command group. + + + + + + + String that will be returned from the {{{AgentQuitFromReachingCommandQuota}}} if this quota is reached. This can be used in {{{RewardForMissionEnd}}}, and is also returned in the {{{MissionEnded}}} message. + + + - - + + + + + Count the commands acted on by the Mod, and signal the end of the mission when the defined quota of commands has been reached. + + A total number of commands can be specified, and/or groups of commands can be given their own quota. + + + + + + + + + + Total number of commands allowed before the mission ends. (Note that a command must be acted on to be counted - sending malformed commands won't affect the total.) + + The check for total command use takes precedence over the individual group quotas, if used. + + + + + + + String that will be returned from the {{{AgentQuitFromReachingCommandQuota}}} if the total allowed command usage is reached. This can be used in {{{RewardForMissionEnd}}}, and is also returned in the {{{MissionEnded}}} message. + + + + + + diff --git a/Schemas/MissionHandlers.xsd.in b/Schemas/MissionHandlers.xsd.in index 0fd44f840..7cc794821 100644 --- a/Schemas/MissionHandlers.xsd.in +++ b/Schemas/MissionHandlers.xsd.in @@ -8,2554 +8,2877 @@ jaxb:version="2.1" version="${MALMO_VERSION_MAJOR}.${MALMO_VERSION_MINOR}"> - - - - - - - An absolute position in the 3D Minecraft world. - - - - - - - - - - - An absolute position in the 3D Minecraft world that includes yaw and pitch. - - - - - - - - Defaults to facing South (0). North is 180. - - - - - - - - - - - - - Defaults to looking straight ahead (0). +90 = look down. -90 = look up. - - - - - - - - - - - - - - - - - - + + - - - - Generates a superflat world with a specified preset string - see e.g. [[http://www.minecraft101.net/superflat/]] - - - - + - - The superflat customization preset string. - + + An absolute position in the 3D Minecraft world. + - - + + + + + + - - Set this to true to force the world to be reloaded, otherwise the current world will be used (provided it matches the requested generator string). - Force reloading is slow, but will guarantee that no world changes will carry over between missions. - + + An absolute position in the 3D Minecraft world that includes yaw and pitch. + - - + + + + + + Defaults to facing South (0). North is 180. + + + + + + + + + + + + + Defaults to looking straight ahead (0). +90 = look down. -90 = look up. + + + + + + + + + + + + + + + + + + + + - - The world seed - leave blank (default) to get a random world. - + + Generates a superflat world with a specified preset string - see e.g. [[http://www.minecraft101.net/superflat/]] + - - + + + + + The superflat customization preset string. + + + + + + + Set this to true to force the world to be reloaded, otherwise the current world will be used (provided it matches the requested generator string). + Force reloading is slow, but will guarantee that no world changes will carry over between missions. + + + + + + + The world seed - leave blank (default) to get a random world. + + + + + + + Set this to true to force the world data files to be deleted after the mission is done. + Enabling this setting prevents the disk being filled with old worlds. + + + + + + + - - Set this to true to force the world data files to be deleted after the mission is done. - Enabling this setting prevents the disk being filled with old worlds. - + + Generates the default terrain. + - - - - - - - - Generates the default terrain. - - - - - - - The world seed - leave blank (default) to get a random world. - - - - - - - Set this to true to force the world to be reloaded, otherwise the current world will be used (provided it matches the requested seed). - Force reloading is slow, but will guarantee that no world changes will carry over between missions. - - - - - - - Set this to true to force the world data files to be deleted after the mission is done. - Enabling this setting prevents the disk being filled with old worlds. - - - - - - - - - - Loads a saved world from disk. You can find the saved worlds in "{{{Minecraft\run\saves}}}". Use the full path to one of those folders. - - If Minecraft is running on a different machine then copy the folder to a readable network location and update the path accordingly. Example: - - {{{<FileWorldGenerator src="\\\\machine-id\\shared\\ProjectMalmo\\saved_maps\\arena" />}}} - - - - - - - The path to the saved world folder. - - - - - - - Set this to true to force the world to be reloaded, otherwise the current world will be used (provided it matches the requested source filename). - Force reloading is slow, but will guarantee that no world changes will carry over between missions. - - - - - - - Set this to true to force the world data files to be deleted after the mission is done. - Enabling this setting prevents the disk being filled with old worlds. - - - - - - - - - - Generates a survival world with the specified biome. - - - - - - - The biome type for the world. Each chunk will be loaded with the biome specified. - - If left blank, the world will be a normal survival world. - - Biome ID #'s can be found here: https://minecraft.gamepedia.com/Biome#Biome_IDs - - - - - - - Set this to true to force the world to be reloaded, otherwise the current world will be used (provided it matches the requested seed). - Force reloading is slow, but will guarantee that no world changes will carry over between missions. - - - - - - - Set this to true to force the world data files to be deleted after the mission is done. - Enabling this setting prevents the disk being filled with old worlds. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Creates a moving two-block target which takes random moves within a specified arena. Can be linked to the turn scheduler. - This can be made more general in the future, but is currently tailored specifically for the Malmo collaborative challenge. - - - - - - - - Define the bounds of the arena within which the target can move. - - - - - - - - The master seed for the random number generator used to move the target. - - - - - - Either an integer number, or the string "random". - - - - - - - - - - - The length, in ticks, between each update, or the string "turnbased" to use the turn scheduler. - - - - - - Either an integer number, or the string "turnbased". - - - - - - - - - - - - - - - - - - Adds a snake made of blocks, that grows at one end and shrinks at the other. - - - - - - - - The master seed for the random number generator used to make the snake. - - - - - - Either an integer number, or the string "random". - - - - - - - - - - - Optional seed for determining block types. - - - - - - Either an integer number, or the string "random". - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Adds a maze into the world. - - - - - - - - The master seed for the random number generator used to make the maze. - - - - - - Either an integer number, or the string "random". - - - - - - - - - - - Seed for the random number generator for determining block types - omit to allow master seed to control block types. - - - - - - Either an integer number, or the string "random". - - - - - - - - - - - - - - - - - - - - - - - - - - - - Omit this element if you want the optimal path to be unmarked. - - - - - - - Omit this element if you want the subgoal points to be unmarked. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Base class for all draw objects. - - - - - - - - Base class for all block-based draw objects. - - - - - - - - - - - - - - - - Draws structures into the world. - - - - - - - - - - - - - Specify a block by location and type. - - - - - - - - - - - - - - - Specify an item by location and type. - - - - - - - - - - - - - - - - - - - - - - - - - - Specify a container item by location and type and contents. - - - - - - - - - - - - - - - - - - - - - - Draw a sign at the required location with the specified text. - - - - - - - - - - - - - - - - - - - - - - - - Specify an entity by location and type. - - - - - - - - - - - - - - - - - - - - - Specify a filled cuboid by inclusive coordinates and block type. - - - - - - - - - - - - - - - - - - Specify a filled sphere by centre coordinates and inclusive radius. - - - - - - - - - - - - - - - - Specify a line by start and end coordinates and thickness. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Adds a series of joined rooms into the world. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Basic animation created by repeatedly applying a DrawingDecorator at different positions. - - - - - - - - - Create an animation where the (x,y,z) position are determined by parametric equations. Recognised tokens are: + + + + + The world seed - leave blank (default) to get a random world. + + + + + + + Set this to true to force the world to be reloaded, otherwise the current world will be used (provided it matches the requested seed). + Force reloading is slow, but will guarantee that no world changes will carry over between missions. + + + + + + + Set this to true to force the world data files to be deleted after the mission is done. + Enabling this setting prevents the disk being filled with old worlds. + + + + + - * basic arithmetic operations: +, -, /, *, % (modulo), ^ (to the power of) - * basic trig: sin, cos, tan, asin, acos, atan - * abs (absolute value) - * rand - replaces with a random float between 0 and 1 - * t - the integer time variable, incremented with each time interval + + + + Loads a saved world from disk. You can find the saved worlds in "{{{Minecraft\run\saves}}}". Use the full path to one of those folders. - For example, to create a structure which orbits in the x-z plane about the point (100,100,100) at a radius of 20, use: - "100+20*cos(t)" and "100+20*sin(t)" for the x and z strings. + If Minecraft is running on a different machine then copy the folder to a readable network location and update the path accordingly. Example: - The parser is not robust to mismatched brackets, typos, unrecognised tokens etc, and will fail silently. - - - - - - - - - + {{{<FileWorldGenerator src="\\\\machine-id\\shared\\ProjectMalmo\\saved_maps\\arena" />}}} + + + + + + + The path to the saved world folder. + + + + - - The master seed for the random number generator used for any stochastic elements. - + + Set this to true to force the world to be reloaded, otherwise the current world will be used (provided it matches the requested source filename). + Force reloading is slow, but will guarantee that no world changes will carry over between missions. + - - + + + - Either an integer number, or the string "random". + Set this to true to force the world data files to be deleted after the mission is done. + Enabling this setting prevents the disk being filled with old worlds. - - - - - - - - - - - - Create an animation where the (x,y,z) position is determined by the starting position, a constant velocity, and a bounding box. + + + + - Each time step, the position is updated by adding the velocity values. If the object goes outside of the bounding box in one dimension, that dimension's velocity will be flipped to reverse the direction. - - - - - - + + + + Generates a survival world with the specified biome. + + + + + - Define the bounds of the canvas within which to move the object. + The biome type for the world. Each chunk will be loaded with the biome specified. + + If left blank, the world will be a normal survival world. + + Biome ID #'s can be found here: https://minecraft.gamepedia.com/Biome#Biome_IDs - - - - + + + + - Define the starting position of the drawing's origin. + Set this to true to force the world to be reloaded, otherwise the current world will be used (provided it matches the requested seed). + Force reloading is slow, but will guarantee that no world changes will carry over between missions. - - - - + + + + - Define the initial velocity of the drawing's origin. + Set this to true to force the world data files to be deleted after the mission is done. + Enabling this setting prevents the disk being filled with old worlds. - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Creates a moving two-block target which takes random moves within a specified arena. Can be linked to the turn scheduler. + This can be made more general in the future, but is currently tailored specifically for the Malmo collaborative challenge. + + + + + + + + Define the bounds of the arena within which the target can move. + + - - - - - - + + + + + The master seed for the random number generator used to move the target. + + + + + + Either an integer number, or the string "random". + + + + + + + + + + + The length, in ticks, between each update, or the string "turnbased" to use the turn scheduler. + + + + + + Either an integer number, or the string "turnbased". + + + + + + + + + + + + + + + + + + Adds a snake made of blocks, that grows at one end and shrinks at the other. + + + + + + + + The master seed for the random number generator used to make the snake. + + + + + + Either an integer number, or the string "random". + + + + + + + + + + + Optional seed for determining block types. + + + + + + Either an integer number, or the string "random". + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - Define the drawing, relative to (0,0,0), which will be drawn for each frame of the animation. + Adds a maze into the world. - - - - + + + + + + + The master seed for the random number generator used to make the maze. + + + + + + Either an integer number, or the string "random". + + + + + + + + + + + Seed for the random number generator for determining block types - omit to allow master seed to control block types. + + + + + + Either an integer number, or the string "random". + + + + + + + + + + + + + + + + + + + + + + + + + + + + Omit this element if you want the optimal path to be unmarked. + + + + + + + Omit this element if you want the subgoal points to be unmarked. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - The number of server ticks between each update of the animation. - + + Base class for all draw objects. + - - - - - - - - Sets up a build battle area, with a source structure that can't be edited, and a goal structure, with optional recolouring of blocks to indicate correct/incorrect placement. - - NOTE: Make sure to add a {{{RewardForStructureCopying}}} handler to the AgentHandlers if you want your agent to be rewarded for contributing to the build. - - - - - - - - Define the bounds of the structure to be copied. Anything in this volume when the mission starts will be used as the blueprint - eg anything drawn here using the {{{DrawingDecorator}}}, etc. - - - - - - - Define the bounds in which the agent should build their copy. - - - - - - - If present, correctly placed blocks (in the source and the copy) will be changed to this block type. - - - - - - - If present, incorrectly placed blocks (in the copy only) will be changed to this block type. - - - - - - - - - - - - Helps specify where random blocks should be placed in the world. - - - - - - Specifies the origin point or spawn point in the Minecraft world. - - If omitted, set to the world spawn point. - - - - - - - Specifies the radius of the circle around which the block may be randomly placed. - - - - - - - Specifies the block type of the block to be randomly placed. - - - - - - - - Specifies the placement behavior of the block. Default is "surface." - - String of "sphere" will place equally randomly in a sphere of radius specified. - - String of "circle" will place randomly in a circle at the y-coordinate specified. - - String of "surface" will place randomly in a circle, then raise to the highest available block on the surface. - - - - - - - - - - - - - - - - Adds a specified block to the world and sets compass to that block. - - Block is placed randomly along a radius around the origin specified. - - Can force the block to appear at the highest available y-value. - - - - - - - - Properties for placing a block in the world randomly. - - - - - - - Sets whether or not the compass location should be randomized within a certain radius. - - If false, will set the compass location to the block that was randomly placed. - - If true, will set the compass location to a random spot within the radius specified below. - - - - - - - The minimum distance a randomized compass location must be from the block that was randomly placed. - - If omitted, set to 0 blocks. - - - - - - - The maximum distance a randomized compass location must be from the block that was randomly placed. - - If omitted, set to 8 blocks. - - - - - - - - - - - - - Specifies a time limit that applies to all agents. - - - - - - - - - - - - Specifies that the mission ends when any of the agents finish. - - - - - - - - - - - - - - - - Requests that 32bpp depth frames be sent. - - - - - - - - - - - - - - Requests an 8bpp grayscale image. - - - - - - - - - - - - - - - - - - - - - - - - - Requests a 24bpp colour map - each object/entity represented by a solid block of colour. - - - - - - - - - - - + + + + + Base class for all block-based draw objects. + + + + + + + + + + - - - - - - Requests that video frames be sent. - - - - - - - - - - - - - - - - - - - - - - - - - - - - Set to false to specify the min and max depths manually. Default is true, where uses the min and max depths in the scene. - - - - - - - + + - - If true, returns depth in the fourth channel as RGBDRGBD.... Else as RGBRGB... - + + Draws structures into the world. + - - + + + + + + + + - - Sets the camera viewpoint. 0 = first-person, 1 = behind, 2 = facing. - + + Specify a block by location and type. + - - - - - - - + + + + + + + - - - - - - - - - - - - Commands for smooth movement. Some examples: - - "{{{move 0.5}}}" - start moving forward at 50% of the normal walking speed (-ve = backward, +ve = forward). - - "{{{strafe -1}}}" - start moving left at 100% of the normal walking speed (-ve = left, +ve = right). - - "{{{pitch 0.1}}}" - start tilting the agent's head down at 10% of the maximum speed (-ve = up, +ve = down). The maximum speed is set by {{{turnSpeedDegs}}} in {{{ContinuousMovementCommands}}}. - - "{{{turn 0.1}}}" - start turning right at 10% of the maximum speed (-ve = anti-clockwise/left, +ve = clockwise/right). The maximum speed is set by {{{turnSpeedDegs}}} in {{{ContinuousMovementCommands}}}. - - "{{{jump 1}}}" - start jumping (1 = start, 0 = stop). - - "{{{crouch 1}}}" - start crouching (1 = start, 0 = stop). - - "{{{attack 1}}}" - start attacking (1 = start, 0 = stop). The 'attack' command is for destroying blocks and attacking mobs. - - "{{{use 1}}}" - start 'use'ing (1 = start, 0 = stop). The 'use' command is for placing blocks and for other things too. - - - - - - - - - - - - - - - - - - Commands to set position and orientation directly. Some examples: - - "{{{tp 23.5 1 -34.5}}}" - teleport the agent to the absolute position x y z (space delimited). - - "{{{tpx 230}}}" - set the agent's x coordinate, without altering the y and z. - - "{{{tpy 103.2}}}" - set the agent's y coordinate, without altering the x and z. - - "{{{tpz -32.5}}}" - set the agent's z coordinate, without altering the x and y. - - "{{{setYaw 30}}}" - set the agent's body orientation to be 30 degrees clockwise from south. - - "{{{setPitch 20}}}" - set the agent's body orientation to be 20 degrees down from horizontal. - - - - - - - - - - - - - - - - Commands for moving and turning in discrete increments. Some examples: - - "{{{move 1}}}" - move the agent one block forwards (1 = forwards, -1 = backwards). - - "{{{jumpmove 1}}}" - move the agent one block up and forwards (1 = forwards, -1 = backwards). - - "{{{strafe 1}}}" - move the agent one block sideways (1 = right, -1 = left). - - "{{{jumpstrafe 1}}}" - move the agent one block up and sideways (1 = right, -1 = left). - - "{{{turn 1}}}" - rotate the agent 90 degrees right (1 = right, -1 = left). - - "{{{movenorth 1}}}" - move the agent one block north. - - "{{{moveeast 1}}}" - move the agent one block east. - - "{{{movesouth 1}}}" - move the agent one block south. - - "{{{movewest 1}}}" - move the agent one block west. - - "{{{jumpnorth 1}}}" - move the agent one block up and north. - - "{{{jumpeast 1}}}" - move the agent one block up and east. - - "{{{jumpsouth 1}}}" - move the agent one block up and south. - - "{{{jumpwest 1}}}" - move the agent one block up and west. - - "{{{jump 1}}}" - move the agent one block up. - - "{{{look 1}}}" - look down by 45 degrees (-ve = up, +ve = down). - - "{{{attack 1}}}" - destroy the block that has focus. - - "{{{use 1}}}" - place the held block item on the block face that has focus. - - "{{{jumpuse}}}" - simultaneously jump and place the held block on the block face that has focus. - - - - - - - - - - - - - - - - - - - - - - - - - - - - Commands for changing the contents of the inventory and hotbar. - - 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: - - "{{{swapInventoryItems 13 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: - - "{{{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: - - "{{{hotbar.1 1}}}" - "{{{hotbar.1 0}}}" - - Send both commands to select hotbar slot 1 as the currently-held tool. This affects the attack and use commands - - e.g. if the agent does 'use' while holding a block item it will place the block into the world. - - If the agent is currently pointed at a container item - eg a chest, shulker box, dispenser etc - then the swap and combine commands - can be extended to allow access to the container's inventory. To do this, simply prefix the slot number with the name of the foreign - inventory (which will be provided by the {{{ObservationFromFullInventory}}} observations). - - So to move an item out of the hotbar and into slot 0 of a chest, say: - - "{{{swapInventoryItems 3 Chest:0}}}" - - Note that this is the same as writing - - "{{{swapInventoryItems Inventory:3 Chest:0}}}" - - "Inventory" is the name of the player's inventory. - - See inventory_test.py for a working example of this. - - - - - - - - - - - - - - - - - - - - - - A command for simple crafting: - - Will look up all recipes that produce the requested object, and attempt each one in turn until one is successful or all have failed. This ignores all issues like requiring a crafting table / brewing stand etc, and the shape of the recipe (which items go in which slots on the crafting table). It will simply check to see whether the player has the necessary raw ingredients, and, if so, will remove them from the player's inventory and add the result of the recipe. - - For basic objects, use the ItemTypes or BlockTypes found in Types.xsd. Eg: - - "{{{craft diamond_pickaxe}}}" will remove three diamonds and two sticks from the player's inventory, and add a diamond pickaxe. - - For more control over colours, types etc, add a Variation or Colour. Eg: - - "{{{craft carpet PINK}}}" - - etc. - - - - - - - - - - - A command for broadcasting text messages to the other players. An example: - - "{{{chat I have found diamonds!}}}" - broadcasts the string "{{{I have found diamonds!}}}". - - Chat messages from other players can be observed using {{{ObservationFromChat}}}. - - - - - - - - - - - A command for ending the mission, example: - - "{{{quit}}}" - terminates the current mission. - - - - - - - - - - - Commands for controlling Minecraft at the level of keyboard and mouse events. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Allow-list/deny-list base class - restricted by each command handler to only allow that handler's commands. - - - - - - - - - - - - - - - - - - - - - When present, the Mod will accept commands that control smooth movement. - - Commands take the form of "verb <value>" e.g. "{{{move 1}}}" to move forwards with normal speed. - - - - - - - - - - - - - - - - - - - - This sets the maximum speed for both turning the agent and adjusting the camera pitch, in degrees per second. - - The turn and pitch command values are both scaled by this - eg "{{{turn -0.5}}}" to turn left (anti-clockwise) at half this maximum speed. - - - + + + + + Specify an item by location and type. + + + + + + + + + + + + + - - - - - - When present, the Mod will accept commands to set the player's position and orientation directly. - - Commands take the form of "verb <value>" e.g. "{{{tpx 13}}}" to set the x-coordinate to 13. - - - - - - - - - - - - - - - - + + + + + + - - - - - - When present, the Mod will accept commands that control movement in discrete jumps. - - Commands take the form of "verb <value>" e.g. "{{{move 1}}}" to move forwards one square. - - - - - - - - - - + + + + + Specify a container item by location and type and contents. + + + + + + - - - - - - - + + + + + + + + + - - - - - - When present, the Mod will accept commands that control the player's inventory. - - - - - - - - - - - - - - - - + + + + + Draw a sign at the required location with the specified text. + + + + + + + + + + + + + + + + + + - - - - - - When present, the Mod will accept simple commands that implement a basic form of crafting. - - - - - - - - - - - - - - - - + + + + + Specify an entity by location and type. + + + + + + + + + + + + + + + - - - - - - When present, the Mod will accept commands that send chat messages to the other players. - - - - - - - - - - - - - - - - + + + + + Specify a filled cuboid by inclusive coordinates and block type. + + + + + + + + + + + + - - - - - - When present, the Mod will accept a command that quits the mission. - - - - - - - - - - - - - - - - - + + + + + Specify a filled sphere by centre coordinates and inclusive radius. + + + + + + + + + + - - - - - - Allows a user to specify that certain commands must be sent on a turn-by-turn basis - ie, in a multi-agent mission, placing the {{{DiscreteMovementCommand}}} handler inside the TurnBasedCommands section will mean that each agent must take it in turns to send a discrete movement command. See turn_based_test.py in the Python Samples for a demonstration/explanation of this. - - - - - - - - - - - + + + + + Specify a line by start and end coordinates and thickness. + + + + + + + + + + + + + - - - - - - When present, the Mod will accept commands on the level of mouse and keyboard events. - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + Adds a series of joined rooms into the world. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - When present, the Mod will return observations that say what commands have been acted on since the last report, in the JSON element {{{CommandsSinceLastObservation}}}. - - Note that the commands returned might not yet have taken effect, depending on the command and the way in which Minecraft responds to it - - but they will have been processed by the command handling chain. - - - - - - - - - Automatically addd by Malmo when the user specifies the {{{TurnBasedCommands}}} handler. This provides vital observations back to the agent to allow them to make use of the turn scheduler. When it is the agent's turn, the JSON will contain {{{turn_number}}} - an integer which tracks the number of turns the agent has taken, and {{{turn_key}}} - a one-shot key which must be passed back to Malmo as a parameter in {{{sendCommand}}} in order for the command to be accepted. - - - - - - - - - When present, the Mod will return observations that indicate the direction to follow to the next subgoal. - The value to turn by is returned in the JSON element {{{yawDelta}}}. - - - - - - + + + + + + + - - - - - - When present, the Mod will return observations that say what is in the hotbar. - - Up to four values are returned for each slot, if not empty: e.g. {{{Hotbar_1_size}}} and {{{Hotbar_1_item}}} containing the number and - type of the item(s) in the slot, respectively, and {{{Hotbar_1_colour}}} and {{{Hotbar_1_variant}}} if the item has a colour/variation. Hotbar slots are numbered 0 to 8 inclusive. - - - - - - - - - When present, the Mod will return several observations: - - * Achievement statistics: {{{DistanceTravelled}}}, {{{TimeAlive}}}, {{{MobsKilled}}}, {{{PlayersKilled}}}, {{{DamageTaken}}}, {{{DamageDealt}}} - * Life statistics: {{{Life}}}, {{{Score}}}, {{{Food}}}, {{{Air}}}, {{{XP}}}, {{{IsAlive}}}, {{{Name}}} - * Position statistics: {{{XPos}}}, {{{YPos}}}, {{{ZPos}}}, {{{Pitch}}}, {{{Yaw}}} - * Environment statistics: {{{WorldTime}}} - current time in ticks, {{{TotalTime}}} - total world time, unaffected by ServerInitialConditions - - - - - - - - - When present, the Mod will return low-level keyboard and mouse events. - - - - - - - - - When present, the Mod will return information on the current performance of the Minecraft system - eg tick speeds, etc. - - - - - - - - - When present, the Mod will return a JSON object called "LineOfSight", containing observations about the block or entity which is currently in the centre of the screen: - - * Hit details: {{{hitType}}} - will be "block" for a block, "entity" for an entity (eg spider, rabbit etc) or "item" for a free-floating item that can be picked up. {{{inRange}}} will be true if the block/entity is within the agent's reach - ie attacking or using will have an effect on the object. {{{distance}}} gives the straight-line distance from the agent. - * Position: {{{x}}}, {{{y}}}, {{{z}}} - in the case of block hits, will be the precise point when the ray intercepts the block. {{{yaw}}}, {{{pitch}}} are also added for entities. - * Type information: {{{type}}}, {{{colour}}}, {{{variant}}}, {{{facing}}} - * Extra properties: in the case of block types, any extra properties will be returned by their minecraft name, prefixed with "prop_" (eg, for leaves, "prop_decayable" and "prop_check_decay") - this is the same data as can be seen by exploring Minecraft with the F3 debug information displayed. For floating items, the stack size is returned in {{{stackSize}}} - * NBTTagCompound: for tile entity blocks, optionally returns a json object called "NBTTagCompound" which contains the entity's entire NBTTagCompound - eg useful for reading the text off signs, etc. Set {{{includeNBT}}} to true to receive this data. - - - - + + + + + + + + + + + + + + + + + + + + + + + + + Basic animation created by repeatedly applying a DrawingDecorator at different positions. + + + + + + + + + Create an animation where the (x,y,z) position are determined by parametric equations. Recognised tokens are: + + * basic arithmetic operations: +, -, /, *, % (modulo), ^ (to the power of) + * basic trig: sin, cos, tan, asin, acos, atan + * abs (absolute value) + * rand - replaces with a random float between 0 and 1 + * t - the integer time variable, incremented with each time interval + + For example, to create a structure which orbits in the x-z plane about the point (100,100,100) at a radius of 20, use: + "100+20*cos(t)" and "100+20*sin(t)" for the x and z strings. + + The parser is not robust to mismatched brackets, typos, unrecognised tokens etc, and will fail silently. + + + + + + + + + + + + The master seed for the random number generator used for any stochastic elements. + + + + + + Either an integer number, or the string "random". + + + + + + + + + + + + + Create an animation where the (x,y,z) position is determined by the starting position, a constant velocity, and a bounding box. + + Each time step, the position is updated by adding the velocity values. If the object goes outside of the bounding box in one dimension, that dimension's velocity will be flipped to reverse the direction. + + + + + + + + Define the bounds of the canvas within which to move the object. + + + + + + + Define the starting position of the drawing's origin. + + + + + + + Define the initial velocity of the drawing's origin. + + + + + + + + + + + Define the drawing, relative to (0,0,0), which will be drawn for each frame of the animation. + + + + + + + + The number of server ticks between each update of the animation. + + + + + + + + + + Sets up a build battle area, with a source structure that can't be edited, and a goal structure, with optional recolouring of blocks to indicate correct/incorrect placement. + + NOTE: Make sure to add a {{{RewardForStructureCopying}}} handler to the AgentHandlers if you want your agent to be rewarded for contributing to the build. + + + + + + + + Define the bounds of the structure to be copied. Anything in this volume when the mission starts will be used as the blueprint - eg anything drawn here using the {{{DrawingDecorator}}}, etc. + + + + + + + Define the bounds in which the agent should build their copy. + + + + + + + If present, correctly placed blocks (in the source and the copy) will be changed to this block type. + + + + + + + If present, incorrectly placed blocks (in the copy only) will be changed to this block type. + + + + + + + + + + + + Helps specify where random blocks should be placed in the world. + + + + + + Specifies the origin point or spawn point in the Minecraft world. + + If omitted, set to the world spawn point. + + + + + + + Specifies the minumum radius of the circle around which the block may be randomly placed. + + + + + + + Specifies the maximum radius of the circle around which the block may be randomly placed. + + + + + + + Specifies the block type of the block to be randomly placed. + + + + + + + + Specifies the placement behavior of the block. Default is "surface." + + String of "sphere" will place equally randomly in a sphere of radius specified. + + String of "circle" will place randomly in a circle at the y-coordinate specified. + + String of "surface" will place randomly in a circle, then raise to the highest available block on the surface. + + + + + + + + + + + + + Sets whether or not the target block radius should be randomized within a certain radius. + If false, will set the default target radius to the given radius. + If true, will set the default target radius randomly within the bound specified below. + + + + + + + The minimum distance for a randomly placed target block. + + If omitted, and randomization of radius enabled, set to 10 blocks. + + + + + + + The maximum distance for a randomly placed target block. + + If omitted, and randomization of radius enabled, set to 64 blocks. + + + + + + + + + + Adds a specified block to the world and sets compass to that block. + + Block is placed randomly along a radius around the origin specified. + + Can force the block to appear at the highest available y-value. + + + + + + + + Properties for placing a block in the world randomly. + + + + + + + Sets whether or not the compass location should be randomized within a certain radius. + If false, will set the compass location to the block that was randomly placed. + If true, will set the compass location to a random spot within the radius specified below. + + + + + + + The minimum distance a randomized compass location must be from the block that was randomly placed. + + If omitted, set to 0 blocks. + + + + + + + The maximum distance a randomized compass location must be from the block that was randomly placed. + + If omitted, set to 8 blocks. + + + + + + + + + + + + + Specifies a time limit that applies to all agents. + + + + + + + + + + + + Specifies that the mission ends when any of the agents finish. + + + + + + + + + + + + + + + + + Requests that 32bpp depth frames be sent. + + + + + + + + + + + + + + Requests an 8bpp grayscale image. + + + + + + + + + + + + + + + + + + + + + + + + + Requests a 24bpp colour map - each object/entity represented by a solid block of colour. + + + + + + + + + + + + + + + + + + Requests that video frames be sent. + + + + + + + + + + + + + + + + + + + + + + + + + + + + Set to false to specify the min and max depths manually. Default is true, where uses the min and max depths in the scene. + + + + + + + + + + If true, returns depth in the fourth channel as RGBDRGBD.... Else as RGBRGB... + + + + + + + Sets the camera viewpoint. 0 = first-person, 1 = behind, 2 = facing. + + + + + + + + + + + + + + + + + + + + + + Commands for smooth movement. Some examples: + + "{{{move 0.5}}}" - start moving forward at 50% of the normal walking speed (-ve = backward, +ve = forward). + + "{{{strafe -1}}}" - start moving left at 100% of the normal walking speed (-ve = left, +ve = right). + + "{{{pitch 0.1}}}" - start tilting the agent's head down at 10% of the maximum speed (-ve = up, +ve = down). The maximum speed is set by {{{turnSpeedDegs}}} in {{{ContinuousMovementCommands}}}. + + "{{{turn 0.1}}}" - start turning right at 10% of the maximum speed (-ve = anti-clockwise/left, +ve = clockwise/right). The maximum speed is set by {{{turnSpeedDegs}}} in {{{ContinuousMovementCommands}}}. + + "{{{jump 1}}}" - start jumping (1 = start, 0 = stop). + + "{{{crouch 1}}}" - start crouching (1 = start, 0 = stop). + + "{{{attack 1}}}" - start attacking (1 = start, 0 = stop). The 'attack' command is for destroying blocks and attacking mobs. + + "{{{use 1}}}" - start 'use'ing (1 = start, 0 = stop). The 'use' command is for placing blocks and for other things too. + + + + + + + + + + + + + + + + + + Commands to set position and orientation directly. Some examples: + + "{{{tp 23.5 1 -34.5}}}" - teleport the agent to the absolute position x y z (space delimited). + + "{{{tpx 230}}}" - set the agent's x coordinate, without altering the y and z. + + "{{{tpy 103.2}}}" - set the agent's y coordinate, without altering the x and z. + + "{{{tpz -32.5}}}" - set the agent's z coordinate, without altering the x and y. + + "{{{setYaw 30}}}" - set the agent's body orientation to be 30 degrees clockwise from south. + + "{{{setPitch 20}}}" - set the agent's body orientation to be 20 degrees down from horizontal. + + + + + + + + + + + + + + + + Commands for moving and turning in discrete increments. Some examples: + + "{{{move 1}}}" - move the agent one block forwards (1 = forwards, -1 = backwards). + + "{{{jumpmove 1}}}" - move the agent one block up and forwards (1 = forwards, -1 = backwards). + + "{{{strafe 1}}}" - move the agent one block sideways (1 = right, -1 = left). + + "{{{jumpstrafe 1}}}" - move the agent one block up and sideways (1 = right, -1 = left). + + "{{{turn 1}}}" - rotate the agent 90 degrees right (1 = right, -1 = left). + + "{{{movenorth 1}}}" - move the agent one block north. + + "{{{moveeast 1}}}" - move the agent one block east. + + "{{{movesouth 1}}}" - move the agent one block south. + + "{{{movewest 1}}}" - move the agent one block west. + + "{{{jumpnorth 1}}}" - move the agent one block up and north. + + "{{{jumpeast 1}}}" - move the agent one block up and east. + + "{{{jumpsouth 1}}}" - move the agent one block up and south. + + "{{{jumpwest 1}}}" - move the agent one block up and west. + + "{{{jump 1}}}" - move the agent one block up. + + "{{{look 1}}}" - look down by 45 degrees (-ve = up, +ve = down). + + "{{{attack 1}}}" - destroy the block that has focus. + + "{{{use 1}}}" - place the held block item on the block face that has focus. + + "{{{jumpuse}}}" - simultaneously jump and place the held block on the block face that has focus. + + + + + + + + + + + + + + + + + + + + + + + + + + + + Commands for changing the contents of the inventory and hotbar. + + 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: + + "{{{swapInventoryItems 13 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: + + "{{{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: + + "{{{hotbar.1 1}}}" + "{{{hotbar.1 0}}}" + + Send both commands to select hotbar slot 1 as the currently-held tool. This affects the attack and use commands + - e.g. if the agent does 'use' while holding a block item it will place the block into the world. + + If the agent is currently pointed at a container item - eg a chest, shulker box, dispenser etc - then the swap and combine commands + can be extended to allow access to the container's inventory. To do this, simply prefix the slot number with the name of the foreign + inventory (which will be provided by the {{{ObservationFromFullInventory}}} observations). + + So to move an item out of the hotbar and into slot 0 of a chest, say: + + "{{{swapInventoryItems 3 Chest:0}}}" + + Note that this is the same as writing + + "{{{swapInventoryItems Inventory:3 Chest:0}}}" + + "Inventory" is the name of the player's inventory. + + See inventory_test.py for a working example of this. + + + + + + + + + + + + + + + + + + + + + + A command for simple crafting: + + "{{{craft carpet PINK}}}" + + etc. + + + + + + + + + + + A command for simple nearby crafting: + + "{{{craftNearby carpet PINK}}}" + + etc. + + + + + + + + + + + A command for simple smelting: + + "{{{smeltNearby carpet PINK}}}" + + etc. + + + + + + + + + + + A command for placing blocks in the agent's inventory: + + Will look in the agent's inventory. If the block exists, will try to place the block in the world. + + For basic objects, use BlockTypes found in Types.xsd. Eg: + + "{{{place diamond_block}}}" will attempt to place a diamond block from the agent's inventory. + + for more control over colours, types, etc, add a Variation or Colour. Eg: + + "{{{place carpet PINK}}}" + + + + + + + + + + + A command for broadcasting text messages to the other players. An example: + + "{{{chat I have found diamonds!}}}" - broadcasts the string "{{{I have found diamonds!}}}". + + Chat messages from other players can be observed using {{{ObservationFromChat}}}. + + + + + + + + + + + A command for ending the mission, example: + + "{{{quit}}}" - terminates the current mission. + + + + + + + + + + + Commands for controlling Minecraft at the level of keyboard and mouse events. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Allow-list/deny-list base class - restricted by each command handler to only allow that handler's commands. + + + + + + + + + + + + + - - - - - - When present, the Mod will return observations that describe the contents of the player's inventory. - There are two modes - "flat" (the default) is provided for backwards compatibility, and behaves like this: - - The inventory contents are returned in a flat format in the root of the json observations. - Up to four values are returned for each slot, if not empty: e.g. {{{InventorySlot_0_size}}} and {{{InventorySlot_0_item}}} containing the number and - type of the item(s) in the slot, respectively, and {{{InventorySlot_0_colour}}} and {{{InventorySlot_0_variant}}} if the item has a colour/variation. - Inventory slots are numbered 0 to 39 inclusive. - If there is a container item available (eg the player is pointed at a chest), this will be returned in the same way, but "InventorySlot" - will be replaced by "ContainerNameSlot" - eg {{{ShulkerBoxSlot_0_item}}} etc. - - If {{{flat}}} is false (recommended), the data is returned as an array of objects, one for each item in the inventory/inventories. - The JSON array is called "inventory", and each item in the array will contain: - * {{{type}}} - the type of the object in that ItemStack - * {{{colour}}} - the colour, if relevant - * {{{variant}}} - the variant, if relevant - * {{{quantity}}} - the number of objects in the ItemStack - * {{{index}}} - the slot number - * {{{inventory}}} - the name of the inventory - will be "Inventory" for the player, or, for example, "ShulkerBox", "Chest" etc, if a container is available. - This index and inventory information can be used to specify the item in the {{{InventoryCommands}}} - items are specified as inventory:index - - eg "ShulkerBox:12" - - In addition to this information, whether {{{flat}}} is true or false, an array called "inventoriesAvailable" will also be returned. - This will contain a list of all the inventories available (usually just the player's, but if the player is pointed at a container, this - will also be available.) - For each inventory, an object will be returned that specifies: - * {{{name}}} - the inventory name (same as will be returned in the {{{inventory}}} field for any items in that inventory) - * {{{size}}} - the number of slots the inventory provides. - - For a working example please see inventory_test.py in the Python samples folder. - - - - + + + + + + + When present, the Mod will accept commands that control smooth movement. + + Commands take the form of "verb <value>" e.g. "{{{move 1}}}" to move forwards with normal speed. + + + + + + + + + + + + + + + + + + + + This sets the maximum speed for both turning the agent and adjusting the camera pitch, in degrees per second. + + The turn and pitch command values are both scaled by this - eg "{{{turn -0.5}}}" to turn left (anti-clockwise) at half this maximum speed. + + + + + + + + + + When present, the Mod will accept commands to set the player's position and orientation directly. + + Commands take the form of "verb <value>" e.g. "{{{tpx 13}}}" to set the x-coordinate to 13. + + + + + + + + + + + + + + + + + + + + + + + When present, the Mod will accept commands that control movement in discrete jumps. + + Commands take the form of "verb <value>" e.g. "{{{move 1}}}" to move forwards one square. + + + + + + + + + + + + + + + + + + + + + + + + + When present, the Mod will accept commands that control the player's inventory. + + + + + + + + + + + + + + + + + + + + + + + When present, the Mod will accept simple commands that implement a basic form of crafting. + + + + + + + + + + + + + + + + + + + + + + + + When present, the Mod will accept simple commands that implement a basic form of crafting. Success of the craft command depends on the presence of a nearby crafting table previously placed by the agent and within reach and in field of view. + + + + + + + + + + + + + + + + + + + + + + + + When present, the Mod will accept simple commands that implement a basic form of smelting. Success of the smelt command depends on the presence of a nearby furnace previously placed by the agent and within reach and in field of view. Fails when a command corresponds to an item not able to be smelted. Each command takes fuel as if the agent had placed the items in a furnace. + + Eg: + + If called on 16 iron ore, requires 2 coal, 2 charcoal, 11 planks, etc. + + If called twice separately on 8 iron ore, requires 1 coal and then 1 coal, etc. + + + + + + + + + + + + + + + + + + + + + + + + When present, the Mod will accept commands that allow for the placement of blocks. If the specified block is contained in the agent's inventory, then the agent will attempt to place the block in the world. + + If a non-block is specified, the command fails. + + If a block is specified, the block will swap to the hotbar, be placed, and then swapped back, so no changes to the inventory are made besides losing a block. + + + + + + + + + + + + + + + + + + + + + + + When present, the Mod will accept commands that send chat messages to the other players. + + + + + + + + + + + + + + + + + + + + + + + When present, the Mod will accept a command that quits the mission. + + + + + + + + + + + + + + + + + + + + + + + + Allows a user to specify that certain commands must be sent on a turn-by-turn basis - ie, in a multi-agent mission, placing the {{{DiscreteMovementCommand}}} handler inside the TurnBasedCommands section will mean that each agent must take it in turns to send a discrete movement command. See turn_based_test.py in the Python Samples for a demonstration/explanation of this. + + + + + + + + + + + + + + + + + + When present, the Mod will accept commands on the level of mouse and keyboard events. + + + + + + + + + + + + + + + + + + + + + + + + + When present, the Mod will return observations that say what commands have been acted on since the last report, in the JSON element {{{CommandsSinceLastObservation}}}. + + Note that the commands returned might not yet have taken effect, depending on the command and the way in which Minecraft responds to it - + but they will have been processed by the command handling chain. + + + + + + + + + Automatically addd by Malmo when the user specifies the {{{TurnBasedCommands}}} handler. This provides vital observations back to the agent to allow them to make use of the turn scheduler. When it is the agent's turn, the JSON will contain {{{turn_number}}} - an integer which tracks the number of turns the agent has taken, and {{{turn_key}}} - a one-shot key which must be passed back to Malmo as a parameter in {{{sendCommand}}} in order for the command to be accepted. + + + + + + + + + When present, the Mod will return observations that indicate the direction to follow to the next subgoal. + The value to turn by is returned in the JSON element {{{yawDelta}}}. + + + + + + + + + + + + + When present, the Mod will return observations that say what is in the hotbar. + + Up to four values are returned for each slot, if not empty: e.g. {{{Hotbar_1_size}}} and {{{Hotbar_1_item}}} containing the number and + type of the item(s) in the slot, respectively, and {{{Hotbar_1_colour}}} and {{{Hotbar_1_variant}}} if the item has a colour/variation. Hotbar slots are numbered 0 to 8 inclusive. + + + + + + + + + When present, the Mod will return several observations: + + * Achievement statistics: {{{DistanceTravelled}}}, {{{TimeAlive}}}, {{{MobsKilled}}}, {{{PlayersKilled}}}, {{{DamageTaken}}}, {{{DamageDealt}}} + * Life statistics: {{{Life}}}, {{{Score}}}, {{{Food}}}, {{{Air}}}, {{{XP}}}, {{{IsAlive}}}, {{{Name}}} + * Position statistics: {{{XPos}}}, {{{YPos}}}, {{{ZPos}}}, {{{Pitch}}}, {{{Yaw}}} + * Environment statistics: {{{WorldTime}}} - current time in ticks, {{{TotalTime}}} - total world time, unaffected by ServerInitialConditions + + + + + + + + + When present, the Mod will return low-level keyboard and mouse events. + + + + + + + + + When present, the Mod will return information on the current performance of the Minecraft system - eg tick speeds, etc. + + + + + + + + + When present, the Mod will return a JSON object called "LineOfSight", containing observations about the block or entity which is currently in the centre of the screen: + + * Hit details: {{{hitType}}} - will be "block" for a block, "entity" for an entity (eg spider, rabbit etc) or "item" for a free-floating item that can be picked up. {{{inRange}}} will be true if the block/entity is within the agent's reach - ie attacking or using will have an effect on the object. {{{distance}}} gives the straight-line distance from the agent. + * Position: {{{x}}}, {{{y}}}, {{{z}}} - in the case of block hits, will be the precise point when the ray intercepts the block. {{{yaw}}}, {{{pitch}}} are also added for entities. + * Type information: {{{type}}}, {{{colour}}}, {{{variant}}}, {{{facing}}} + * Extra properties: in the case of block types, any extra properties will be returned by their minecraft name, prefixed with "prop_" (eg, for leaves, "prop_decayable" and "prop_check_decay") - this is the same data as can be seen by exploring Minecraft with the F3 debug information displayed. For floating items, the stack size is returned in {{{stackSize}}} + * NBTTagCompound: for tile entity blocks, optionally returns a json object called "NBTTagCompound" which contains the entity's entire NBTTagCompound - eg useful for reading the text off signs, etc. Set {{{includeNBT}}} to true to receive this data. + + + + + + + + + + + When present, the Mod will return observations that describe the contents of the player's inventory. + There are two modes - "flat" (the default) is provided for backwards compatibility, and behaves like this: + + The inventory contents are returned in a flat format in the root of the json observations. + Up to four values are returned for each slot, if not empty: e.g. {{{InventorySlot_0_size}}} and {{{InventorySlot_0_item}}} containing the number and + type of the item(s) in the slot, respectively, and {{{InventorySlot_0_colour}}} and {{{InventorySlot_0_variant}}} if the item has a colour/variation. + Inventory slots are numbered 0 to 39 inclusive. + If there is a container item available (eg the player is pointed at a chest), this will be returned in the same way, but "InventorySlot" + will be replaced by "ContainerNameSlot" - eg {{{ShulkerBoxSlot_0_item}}} etc. + + If {{{flat}}} is false (recommended), the data is returned as an array of objects, one for each item in the inventory/inventories. + The JSON array is called "inventory", and each item in the array will contain: + * {{{type}}} - the type of the object in that ItemStack + * {{{colour}}} - the colour, if relevant + * {{{variant}}} - the variant, if relevant + * {{{quantity}}} - the number of objects in the ItemStack + * {{{index}}} - the slot number + * {{{inventory}}} - the name of the inventory - will be "Inventory" for the player, or, for example, "ShulkerBox", "Chest" etc, if a container is available. + This index and inventory information can be used to specify the item in the {{{InventoryCommands}}} - items are specified as inventory:index - + eg "ShulkerBox:12" + + In addition to this information, whether {{{flat}}} is true or false, an array called "inventoriesAvailable" will also be returned. + This will contain a list of all the inventories available (usually just the player's, but if the player is pointed at a container, this + will also be available.) + For each inventory, an object will be returned that specifies: + * {{{name}}} - the inventory name (same as will be returned in the {{{inventory}}} field for any items in that inventory) + * {{{size}}} - the number of slots the inventory provides. + + For a working example please see inventory_test.py in the Python samples folder. + + + + + + + + + + + When present, the Mod will return an observation of the player's position that is unique for every cell on the x/z plane. + This is useful for discrete-movement missions where we need to uniquely identify their location but don't mind how. + + The observation will contain the JSON element {{{cell}}} containing e.g. {{{(2,4)}}} if the player is standing at any location where + x = 2 and z = 4. + + + + + + + + + + + + - - - - - - When present, the Mod will return an observation of the player's position that is unique for every cell on the x/z plane. - This is useful for discrete-movement missions where we need to uniquely identify their location but don't mind how. - - The observation will contain the JSON element {{{cell}}} containing e.g. {{{(2,4)}}} if the player is standing at any location where - x = 2 and z = 4. - - - - - - - - + + + + + When present, the Mod will return an observation that specifies the distance to a named location. + + A JSON element {{{distanceFromNAME}}} will be returned (where {{{NAME}}} is replaced with the name of the NamedPoint), + with a value that is the distance. + + + + + + + + + + + + + {{{name}}} - Each grid has a text label to identify it. + + {{{absoluteCoords}}} - If true, the min and max coordinates of the grid are interpreted as world coordinates. If false (the default) + then the coordinates are relative to the player. + + {{{min}}}, {{{max}}} - The corners of the grid. + + + + + + - - - - - - - - - When present, the Mod will return an observation that specifies the distance to a named location. - - A JSON element {{{distanceFromNAME}}} will be returned (where {{{NAME}}} is replaced with the name of the NamedPoint), - with a value that is the distance. - - - - - - + - - - - - - {{{name}}} - Each grid has a text label to identify it. - - {{{absoluteCoords}}} - If true, the min and max coordinates of the grid are interpreted as world coordinates. If false (the default) - then the coordinates are relative to the player. - - {{{min}}}, {{{max}}} - The corners of the grid. - - - - - - - - - - - - - - When present, the Mod will return observations that say what the nearby blocks are. - - For each {{{Grid}}} entry, a named JSON element will be returned with a 1D array of block types, in order along the x, then z, then y axes. - - - - - - + + + + + When present, the Mod will return observations that say what the nearby blocks are. + + For each {{{Grid}}} entry, a named JSON element will be returned with a 1D array of block types, in order along the x, then z, then y axes. + + + + + + + + + + + + + Used by {{{ObservationFromNearbyEntities}}}. Defines the range within which entities will be returned. Eg a range of 10,1,10 will return all entities within +/-10 blocks of the agent in the x and z axes, and within +/-1 block vertically. + + {{{update_frequency}}} is measured in Minecraft world ticks, and allows the user to specify how often they would like to receive each observation. A value of 20, under normal Minecraft running conditions, for example, would return the entity list once per second. + + + + + + + - - - - - - Used by {{{ObservationFromNearbyEntities}}}. Defines the range within which entities will be returned. Eg a range of 10,1,10 will return all entities within +/-10 blocks of the agent in the x and z axes, and within +/-1 block vertically. - - {{{update_frequency}}} is measured in Minecraft world ticks, and allows the user to specify how often they would like to receive each observation. A value of 20, under normal Minecraft running conditions, for example, would return the entity list once per second. - - - - - - - - - - - - - When present, the Mod will return observations that list the positions of all entities that fall within the given ranges of the agent. - - A JSON array will be returned for each range requested, named using the {{{name}}} attribute of the range. Within the array will be a series of elements, one for each entity, containing the following: - - - name: a string describing the entity (eg from Types.xsd) - - - x: the x position of the entity - - - y: the y position of the entity - - - z: the z position of the entity - - - quantity: if items have been grouped together by Minecraft, this indicates the number in the stack - - - colour: if the item is a tile entity, with a colour, this will be present to describe the colour - - - variation: optional string to describe the variation - eg the type of egg, or brick, etc (see Types.xsd) - - - - - - + + + + + When present, the Mod will return observations that list the positions of all entities that fall within the given ranges of the agent. + + A JSON array will be returned for each range requested, named using the {{{name}}} attribute of the range. Within the array will be a series of elements, one for each entity, containing the following: + + - name: a string describing the entity (eg from Types.xsd) + + - x: the x position of the entity + + - y: the y position of the entity + + - z: the z position of the entity + + - quantity: if items have been grouped together by Minecraft, this indicates the number in the stack + + - colour: if the item is a tile entity, with a colour, this will be present to describe the colour + + - variation: optional string to describe the variation - eg the type of egg, or brick, etc (see Types.xsd) + + + + + + + + + + + + + When present, the Mod will return observations that say what chat messages have occurred and from which player. + It will also return messages for any title or subtitle change (eg via Minecraft's title command) + + A JSON element {{{Chat}}} will be returned, with a list of chat strings. + In the same way, title changes and subtitle changes will be returned in {{{Title}}} and {{{Subtitle}}} respectively. + + Note that unless the AgentHost ObservationsPolicy is set to KEEP_ALL_OBSERVATIONS it is likely that chat messages will be missed. + The default policy is LATEST_OBSERVATION_ONLY. + + + + + + + + + + When present, the Mod will return observations that detail how the agent is facing and what position the agent is in with respect to a compass. + + A JSON element will be returned for the compass in the agent's inventory. The JSON will contain the following + + - set: boolean on whether the compass is set + + - compass-x: the x coordinate value of the set compass, null if not set + + - compass-y: the y coordinate value of the set compass, null if not set + + - compass-z: the z coordinate value of the set compass, null if not set + + - relative-x: the relative x coordinate value of the player to the compass, null if not set + + - relative-y: the relative y coordinate value of the player to the compass, null if not set + + - relative-z: the relative z coordinate value of the player to the compass, null if not set + + - offset: the number of degrees the agent is facing away from the direction the compass is pointing + + - normalized-offset: the number of degrees the agent is facing away, with the agent considered 0 degrees + + - distance: the distance from the agent's location to the compass's location + + + + + + + + + + + + + For multi-dimensional rewards, specifies the dimension to allocate this reward to. All rewards on this dimension will be summed. + + + + + + + + + + + + + + + + + + + + + - - - - - - When present, the Mod will return observations that say what chat messages have occurred and from which player. - It will also return messages for any title or subtitle change (eg via Minecraft's title command) - - A JSON element {{{Chat}}} will be returned, with a list of chat strings. - In the same way, title changes and subtitle changes will be returned in {{{Title}}} and {{{Subtitle}}} respectively. - - Note that unless the AgentHost ObservationsPolicy is set to KEEP_ALL_OBSERVATIONS it is likely that chat messages will be missed. - The default policy is LATEST_OBSERVATION_ONLY. - - - - - - - - - When present, the Mod will return observations that detail how the agent is facing and what position the agent is in with respect to a compass. - - A JSON element will be returned for the compass in the agent's inventory. The JSON will contain the following - - - set: boolean on whether the compass is set - - - compass-x: the x coordinate value of the set compass, null if not set - - - compass-y: the y coordinate value of the set compass, null if not set - - - compass-z: the z coordinate value of the set compass, null if not set - - - relative-x: the relative x coordinate value of the player to the compass, null if not set - - - relative-y: the relative y coordinate value of the player to the compass, null if not set - - - relative-z: the relative z coordinate value of the player to the compass, null if not set - - - offset: the number of degrees the agent is facing away from the direction the compass is pointing - - - normalized-offset: the number of degrees the agent is facing away, with the agent considered 0 degrees - - - distance: the distance from the agent's location to the compass's location - - - - - - - - - - - For multi-dimensional rewards, specifies the dimension to allocate this reward to. All rewards on this dimension will be summed. - - - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - Sends a reward when an entity is damaged. - - - - - - - - - - - - - Sends a reward when a specified position is reached by the agent. - - - - - - - + + + + + Sends a reward when an entity is damaged. + + + + + + + + + + + + + + Sends a reward when a specified position is reached by the agent. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sends a rewards when an agent comes in contact with a specific block type. + + + + + + + + + + + + + + Sends a reward when the agent issues a command. + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + Sends a reward when the agent sends a chat message that matches a given regular expression (supports Java regex syntax). + + + + + + + + + + + - - - - - - - - - + + + + + + + Sends a reward when the agent collects a specific item. + + + + + + + + + + + + + + Sends a reward when the agent collects a specific item. + + If Sparse is set to true, will only give full reward on collecting entire amount. + + Otherwise, will give the reward amount notated for each item collected up to the amount noted. + + + + + + + + + + + + + + + Sends a reward when the agent collects a specific item. + + If Sparse is set to true, will only give full reward on possessing the entire amount. + + Otherwise, will give the rewards amount notated for each item crafted up to the amount noted. + + + + + + + + + + + + + + + Sends a reward when the agent crafts a specific item. + + If Sparse is set to true, will only give full reward on crafting of entire amount. + + Otherwise, will give the reward amount notated for each item crafted up to the amount noted. + + + + + + + + + + + + + + + Sends a reward when the agent smelts a specific item. + + If Sparse is set to true, will only give full reward on crafting of entire amount. + + Otherwise, will give the reward amount notated for each item crafted up to the amount noted. + + + + + + + + + + + + + + + Sends a reward when an agent discards a specific item. + + + + + + + + + + + - - - - - - - - - - - - - - - - - Sends a rewards when an agent comes in contact with a specific block type. - - - - - - - + - - - - - - Sends a reward when the agent issues a command. - - - - - - - - - - - - - - - - - - - - Sends a reward when the agent sends a chat message that matches a given regular expression (supports Java regex syntax). - - - - - - - - - - - - - - - - - - - - Sends a reward when the agent collects a specific item. - - - - - - - - - - - - - - Sends a reward when an agent discards a specific item. - - - - - - - - - - - - - - - - - - - - Sends a reward when the mission ends for a specified reason. - - - - - - - - - + + + + + Sends a reward when the mission ends for a specified reason. + + + + + + + + + + + + + + + + Reward type for {{{RewardForStructureCopying}}}: + + - {{{PER_BLOCK}}} - reward will be given whenever a block is placed/destroyed in the goal volume, and will be determined by {{{RewardScale}}}. An additional reward of {{{RewardForCompletion}}} will be added the *first* time the copy is correctly completed. + + - {{{MISSION_END}}} - no reward will be given until the mission ends. Reward will be scaled from 0-1, where 0 means "no blocks correct" and 1 means "all blocks correct", and then multipled by {{{RewardScale}}}. + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - Reward type for {{{RewardForStructureCopying}}}: - - - {{{PER_BLOCK}}} - reward will be given whenever a block is placed/destroyed in the goal volume, and will be determined by {{{RewardScale}}}. An additional reward of {{{RewardForCompletion}}} will be added the *first* time the copy is correctly completed. - - - {{{MISSION_END}}} - no reward will be given until the mission ends. Reward will be scaled from 0-1, where 0 means "no blocks correct" and 1 means "all blocks correct", and then multipled by {{{RewardScale}}}. - - - - - - - - - - - + + + + + Sends a reward when the agent copies blocks from a given structure. + + NOTE: This will do nothing unless you have set up a {{{BuildBattleDecorator}}} on the server. + + - - - + + + + + + If present, the mission will end when the copy exactly matches the original. Set {{{description}}} to the quit code you'd like to receive when this happens. (See {{{RewardForMissionEnd}}}.) + + + + + + + + + + + + This is the reward to be added or deducted for each block event. + + The rewards are calculated as follows: + - For destroying a correct block: -1 * rewardScale + - For destroying an incorrect block: +1 * rewardScale + - For placing a correct block: +1 * rewardScale + - For placing an incorrect block: -1 * rewardScale + + If {{{RewardDensity}}} is set to {{{{PER_BLOCK}}}, this reward will arrive after each block transaction. If it's set to {{{MISSION_END}}}, the individual rewards will not be sent, but will be summed throughout the course of the mission, and the resulting total will be sent as the final reward. + + + + + + + + This is the extra reward to be added when the copy exactly matches the original. (Will only be applied once.) + + + - - + + + + + + Reward type for {{{RewardForTimeTaken}}}: + + - {{{PER_TICK}}} - only the reward delta will be sent as a reward, at each tick. The initial reward will be ignored. + + - {{{PER_TICK_ACCUMULATED}}} - the initial reward will be adjusted by the delta at each tick, and the current total sent. Eg: if the initial reward is 1000 and the delta is -1, the agent will receive rewards of 1000,999,998,997 for the first four ticks. + + - {{{MISSION_END}}} - the initial reward will be adjusted by the delta at each tick, but no reward will be sent until the mission ends. Eg: if the initial reward is 1000, the delta is -1, and the mission runs for 800 ticks, then final reward sent will be 200. + + + + + + + + + + + + + Reward that is dependent on time. Can be received per tick, or just once at the end. + + - - - + + + + + - - - - - - - - Sends a reward when the agent copies blocks from a given structure. - - NOTE: This will do nothing unless you have set up a {{{BuildBattleDecorator}}} on the server. - - - - - - - - - If present, the mission will end when the copy exactly matches the original. Set {{{description}}} to the quit code you'd like to receive when this happens. (See {{{RewardForMissionEnd}}}.) - - - - - - - - - - - - This is the reward to be added or deducted for each block event. - - The rewards are calculated as follows: - - For destroying a correct block: -1 * rewardScale - - For destroying an incorrect block: +1 * rewardScale - - For placing a correct block: +1 * rewardScale - - For placing an incorrect block: -1 * rewardScale - - If {{{RewardDensity}}} is set to {{{{PER_BLOCK}}}, this reward will arrive after each block transaction. If it's set to {{{MISSION_END}}}, the individual rewards will not be sent, but will be summed throughout the course of the mission, and the resulting total will be sent as the final reward. - - - - - - + + + + - This is the extra reward to be added when the copy exactly matches the original. (Will only be applied once.) + Reward type for {{{RewardForDistanceTraveledToCompassTarget}}}: + + - {{{PER_TICK}}} - reward the distance traveled twards compass target each tick + + - {{{PER_TICK_ACCUMULATED}}} - the reward will be adjusted by the delta at each tick, and the current total sent. E.G. If the agent moves forward to the target and then back to start the reward will be 0 + + - {{{MISSION_END}}} - the reward will be adjusted by the delta at each tick, but no reward will be sent until the mission ends. Eg: if the initial reward is 1000, the delta is -1, and the mission runs for 800 ticks, then final reward sent will be 200. - - - - - - - - - Reward type for {{{RewardForTimeTaken}}}: - - - {{{PER_TICK}}} - only the reward delta will be sent as a reward, at each tick. The initial reward will be ignored. - - - {{{PER_TICK_ACCUMULATED}}} - the initial reward will be adjusted by the delta at each tick, and the current total sent. Eg: if the initial reward is 1000 and the delta is -1, the agent will receive rewards of 1000,999,998,997 for the first four ticks. - - - {{{MISSION_END}}} - the initial reward will be adjusted by the delta at each tick, but no reward will be sent until the mission ends. Eg: if the initial reward is 1000, the delta is -1, and the mission runs for 800 ticks, then final reward sent will be 200. - - - - - - - - - - - - - Reward that is dependent on time. Can be received per tick, or just once at the end. - - - - - - - - - - + + + + + + + - - - - - - - - - - - - - - Reward for cornering a mob, such that it cannot move from its current square without passing through an agent. - If {{{global}}} is true then the agent doesn't have to be involved in catching the mob; otherwise they must be adjacent to the mob. - For our purposes, a mob is deemed "caught" if there are no unoccupied air blocks immediately north, south, east or west of them for them to move into. (An air block is considered occupied if there is an agent standing in it.) This does not necessarily correspond to Minecraft's definition of caught, in which mobs can escape by jumping or passing through agents. - If {{{oneshot}}} is true, the reward will only be counted once per entity. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - When this is included the agent's mission will end when they reach a specified position. - - - - - - + + + + Reward that is dependent on distance to compass target. Can be received per tick, or just once at the end. + + + + + + + + + + + + + + + + + + - - - - - - When this is included the agent's mission will end when a certain amount of time has elapsed. - - - - - + + + + + Reward for cornering a mob, such that it cannot move from its current square without passing through an agent. + If {{{global}}} is true then the agent doesn't have to be involved in catching the mob; otherwise they must be adjacent to the mob. + For our purposes, a mob is deemed "caught" if there are no unoccupied air blocks immediately north, south, east or west of them for them to move into. (An air block is considered occupied if there is an agent standing in it.) This does not necessarily correspond to Minecraft's definition of caught, in which mobs can escape by jumping or passing through agents. + If {{{oneshot}}} is true, the reward will only be counted once per entity. + + + + + + + + + + + + + + + + + + + - - - - - - When this is included the agent's mission will end when they come in contact with a specified block type. - - - - - - + + + + + + + - - - - - - When this is included the agent's mission will end when they collect (or craft) a specified item. - - - - - - + + + + + + + - - - - - - - - - - - - - - - - Agent's mission will end when they corner a mob. If {{{global}}} is true then the agent doesn't have to be involved in catching the mob; otherwise they must be adjacent to the mob. - For our purposes, a mob is deemed "caught" if there are no unoccupied air blocks immediately north, south, east or west of them for them to move into. (An air block is considered occupied if there is an agent standing in it.) This does not necessarily correspond to Minecraft's definition of caught, in which mobs can escape by jumping or passing through agents. - - - - - - + + + + + When this is included the agent's mission will end when they reach a specified position. + + + + + + + + + + + + + When this is included the agent's mission will end when a certain amount of time has elapsed. + + + + + + + + + + + + When this is included the agent's mission will end when they come in contact with a specified block type. + + + + + + + + + + + + + When this is included the agent's mission will end when they collect (or craft) a specified item. + + + + + + + + + + + + + When this is included the agent's mission will end when they collect (or craft) a specified number of the item. + + + + + + + + + + + + + When this is included the agent's mission will end when they craft a specified item. + + + + + + + + + + + + + When this is included the agent's mission will end when they smelt a specified item. + + + + + + + + + + + + + When this is included the agent's mission will end when they possess the specified item in their inventory all at once. + + + + + + + + + + + + + + + + + - - - - - - Set up a quota for a group of commands. {{{AgentQuitFromReachingCommandQuota}}} will fire once the quota is exceeded. - - - - - - List of commands, separated by spaces, that will share this quota. Commands must be valid members of {{{ContinuousMovementCommand}}}, {{{AbsoluteMovementCommand}}}, {{{DiscreteMovementCommand}}}, {{{InventoryCommand}}}, or {{{ChatCommand}}}. - - For instance, if the command list contains {{{moveeast}}}, {{{movenorth}}}, {{{movesouth}}} and {{{movewest}}}, then the mission will end once the summed total usage of all four commands reaches the quota - even if the agent never used {{{movesouth}}}. - - - - - - - Total number of usages allocated for this command group. - - - - - - - String that will be returned from the {{{AgentQuitFromReachingCommandQuota}}} if this quota is reached. This can be used in {{{RewardForMissionEnd}}}, and is also returned in the {{{MissionEnded}}} message. - - - - - - - - - Count the commands acted on by the Mod, and signal the end of the mission when the defined quota of commands has been reached. - - A total number of commands can be specified, and/or groups of commands can be given their own quota. - - - - - - - - - - Total number of commands allowed before the mission ends. (Note that a command must be acted on to be counted - sending malformed commands won't affect the total.) - - The check for total command use takes precedence over the individual group quotas, if used. - - - - - - - String that will be returned from the {{{AgentQuitFromReachingCommandQuota}}} if the total allowed command usage is reached. This can be used in {{{RewardForMissionEnd}}}, and is also returned in the {{{MissionEnded}}} message. - - - + + + + + Agent's mission will end when they corner a mob. If {{{global}}} is true then the agent doesn't have to be involved in catching the mob; otherwise they must be adjacent to the mob. + For our purposes, a mob is deemed "caught" if there are no unoccupied air blocks immediately north, south, east or west of them for them to move into. (An air block is considered occupied if there is an agent standing in it.) This does not necessarily correspond to Minecraft's definition of caught, in which mobs can escape by jumping or passing through agents. + + + + + + + + + + + + + Set up a quota for a group of commands. {{{AgentQuitFromReachingCommandQuota}}} will fire once the quota is exceeded. + + + + + + List of commands, separated by spaces, that will share this quota. Commands must be valid members of {{{ContinuousMovementCommand}}}, {{{AbsoluteMovementCommand}}}, {{{DiscreteMovementCommand}}}, {{{InventoryCommand}}}, or {{{ChatCommand}}}. + + For instance, if the command list contains {{{moveeast}}}, {{{movenorth}}}, {{{movesouth}}} and {{{movewest}}}, then the mission will end once the summed total usage of all four commands reaches the quota - even if the agent never used {{{movesouth}}}. + + + + + + + Total number of usages allocated for this command group. + + + + + + + String that will be returned from the {{{AgentQuitFromReachingCommandQuota}}} if this quota is reached. This can be used in {{{RewardForMissionEnd}}}, and is also returned in the {{{MissionEnded}}} message. + + + - - + + + + + Count the commands acted on by the Mod, and signal the end of the mission when the defined quota of commands has been reached. + + A total number of commands can be specified, and/or groups of commands can be given their own quota. + + + + + + + + + + Total number of commands allowed before the mission ends. (Note that a command must be acted on to be counted - sending malformed commands won't affect the total.) + + The check for total command use takes precedence over the individual group quotas, if used. + + + + + + + String that will be returned from the {{{AgentQuitFromReachingCommandQuota}}} if the total allowed command usage is reached. This can be used in {{{RewardForMissionEnd}}}, and is also returned in the {{{MissionEnded}}} message. + + + + + + diff --git a/sample_missions/inventory_handlers.xml b/sample_missions/inventory_handlers.xml new file mode 100644 index 000000000..9747b14f7 --- /dev/null +++ b/sample_missions/inventory_handlers.xml @@ -0,0 +1,75 @@ + + + + A tour of rewards through collecting, possessing, crafting, and smelting + + + + + clear + false + + + + + + + + + + Magpie + + + + + + + + + + + + + 640 + 480 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file