diff --git a/.gitignore b/.gitignore index c36968006..14818ce5f 100755 --- a/.gitignore +++ b/.gitignore @@ -167,6 +167,7 @@ ClientBin/ *.pfx *.publishsettings node_modules/ +.DS_Store # RIA/Silverlight projects Generated_Code/ diff --git a/CMakeLists.txt b/CMakeLists.txt index f3bdd4165..d8fb4e997 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,9 +51,10 @@ set( STATIC_BOOST ON CACHE BOOL ${STATIC_BOOST_DESC} ) if( INCLUDE_PYTHON ) set( USE_PYTHON_VERSIONS_DESC "Specifies which version of Python to build Malmo with Python bindings" ) + set( MACOS_USE_PYTHON_MODULE_DESC "Specifies which Python module to build Malmo on Apple MacOS" ) set( USE_PYTHON_VERSIONS 3.6 CACHE STRING ${USE_PYTHON_VERSIONS_DESC} ) - set( BOOST_PYTHON_NAME_DESC "Specifies which Boost Python module to build Malmo with" ) - set( BOOST_PYTHON_NAME "python3" CACHE STRING ${BOOST_PYTHON_NAME_DESC} ) + # Boost has switched to using a 2 digit naming convention for python on MacOS. + set( MACOS_USE_PYTHON_MODULE "python37" CACHE STRING ${MACOS_USE_PYTHON_MODULE_DESC} ) endif() set( WARNINGS_AS_ERRORS OFF ) @@ -105,18 +106,21 @@ SET(Boost_ADDITIONAL_VERSIONS "1.67" "1.67.0") SET(BOOST_PYTHON_MODULE_NAME "python") if ( USE_PYTHON_VERSIONS VERSION_GREATER 3 ) - SET( BOOST_PYTHON_MODULE_NAME ${BOOST_PYTHON_NAME} ) + if ( APPLE ) + SET( BOOST_PYTHON_MODULE_NAME ${MACOS_USE_PYTHON_MODULE} ) + else() + SET( BOOST_PYTHON_MODULE_NAME "python3" ) + endif() endif() if( WIN32 ) SET(Boost_USE_STATIC_LIBS ON) - find_package( Boost COMPONENTS chrono date_time filesystem iostreams program_options ${BOOST_PYTHON_MODULE_NAME} regex system thread REQUIRED ) - add_definitions(-DBOOST_ALL_NO_LIB=1) # Turn off auto-linking, creates problems when linking boost statically + find_package( Boost COMPONENTS chrono date_time filesystem iostreams program_options ${BOOST_PYTHON_MODULE_NAME} regex system thread zlib REQUIRED ) else() - SET(Boost_USE_STATIC_LIBS ${STATIC_BOOST}) find_package( Boost COMPONENTS chrono date_time filesystem iostreams program_options ${BOOST_PYTHON_MODULE_NAME} regex system thread REQUIRED ) set(CMAKE_THREAD_PREFER_PTHREAD TRUE) find_package( Threads REQUIRED ) + SET(Boost_USE_STATIC_LIBS ${STATIC_BOOST}) endif() # suggested install paths - see readme.md diff --git a/Malmo/samples/Python_examples/Untitled-1 b/Malmo/samples/Python_examples/Untitled-1 new file mode 100644 index 000000000..84df10b44 --- /dev/null +++ b/Malmo/samples/Python_examples/Untitled-1 @@ -0,0 +1,63 @@ +agent_host = MalmoPython.AgentHost() +try: + agent_host.parse( sys.argv ) +except RuntimeError as e: + print('ERROR:',e) + print(agent_host.getUsage()) + exit(1) +if agent_host.receivedArgument("help"): + print(agent_host.getUsage()) + exit(0) + +# -- set up the mission -- # +mission_file = './tutorial_6.xml' +with open(mission_file, 'r') as f: + print("Loading mission from %s" % mission_file) + mission_xml = f.read() + my_mission = MalmoPython.MissionSpec(mission_xml, True) +# add 20% holes for interest +for x in range(1,4): + for z in range(1,13): + if random.random()<0.1: + my_mission.drawBlock( x,45,z,"lava") + +max_retries = 3 + +if agent_host.receivedArgument("test"): + num_repeats = 1 +else: + num_repeats = 150 + +cumulative_rewards = [] +for i in range(num_repeats): + + print() + print('Repeat %d of %d' % ( i+1, num_repeats )) + + my_mission_record = MalmoPython.MissionRecordSpec() + + for retry in range(max_retries): + try: + agent_host.startMission( my_mission, my_mission_record ) + break + except RuntimeError as e: + if retry == max_retries - 1: + print("Error starting mission:",e) + exit(1) + else: + time.sleep(2.5) + + print("Waiting for the mission to start", end=' ') + world_state = agent_host.getWorldState() + while not world_state.has_mission_begun: + print(".", end="") + time.sleep(0.1) + world_state = agent_host.getWorldState() + for error in world_state.errors: + print("Error:",error.text) + print() + + # -- run the agent in the world -- # + cumulative_reward = agent.run(agent_host) + print('Cumulative reward: %d' % cumulative_reward) + cumulative_rewards += [ cumulative_reward ] diff --git a/Malmo/samples/Python_examples/navigation.xml b/Malmo/samples/Python_examples/navigation.xml new file mode 100644 index 000000000..c8bf71c12 --- /dev/null +++ b/Malmo/samples/Python_examples/navigation.xml @@ -0,0 +1,61 @@ + + + + Navigation through survival world. + + + + 50 + + + + + + clear + false + + + + + + 64 + 64 + diamond_block + surface + + true + 0 + 1 + + + + + + + + Columbus + + + + + + + + + + + + + + + + + + + + + + diff --git a/Malmo/samples/Python_examples/run_mission.py b/Malmo/samples/Python_examples/run_mission.py index ac20b5c77..247adab08 100755 --- a/Malmo/samples/Python_examples/run_mission.py +++ b/Malmo/samples/Python_examples/run_mission.py @@ -58,11 +58,15 @@ def run(argv=['']): agent_host = MalmoPython.AgentHost() malmoutils.parse_command_line(agent_host, argv) + with open('navigation.xml', 'r') as f: + print("Loading mission from %s" % 'navigation.xml') + mission_xml = f.read() + my_mission = MalmoPython.MissionSpec(mission_xml, True) - my_mission = MalmoPython.MissionSpec() - my_mission.timeLimitInSeconds( 10 ) - my_mission.requestVideo( 320, 240 ) - my_mission.rewardForReachingPosition( 19.5, 0.0, 19.5, 100.0, 1.1 ) + # my_mission = MalmoPython.MissionSpec() + # my_mission.timeLimitInSeconds( 10 ) + # my_mission.requestVideo( 320, 240 ) + # my_mission.rewardForReachingPosition( 19.5, 0.0, 19.5, 100.0, 1.1 ) my_mission_record = malmoutils.get_default_recording_object(agent_host, "saved_data") @@ -103,6 +107,7 @@ def run(argv=['']): print() last_delta = time.time() + net_reward = 00 # main loop: while world_state.is_mission_running: agent_host.sendCommand( "move 1" ) @@ -119,14 +124,17 @@ def run(argv=['']): print("Max delay exceeded for world state change") restart_minecraft(world_state, agent_host, client_info, "world state change") for reward in world_state.rewards: - print("Summed reward:",reward.getValue()) + cur_r = reward.getValue() + print("Summed reward:",cur_r) + net_reward += cur_r + print(net_reward) for error in world_state.errors: print("Error:",error.text) for frame in world_state.video_frames: print("Frame:",frame.width,'x',frame.height,':',frame.channels,'channels') #image = Image.frombytes('RGB', (frame.width, frame.height), bytes(frame.pixels) ) # to convert to a PIL image print("Mission has stopped.") - + print("FINAL REWARD: {}".format(net_reward)) if __name__ == "__main__": run(sys.argv) diff --git a/Malmo/samples/samples.iml b/Malmo/samples/samples.iml new file mode 100644 index 000000000..722c9789b --- /dev/null +++ b/Malmo/samples/samples.iml @@ -0,0 +1,22 @@ + + + + + + + MIXIN + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Malmo/src/AgentHost.cpp b/Malmo/src/AgentHost.cpp index 3553a804d..853e6a784 100755 --- a/Malmo/src/AgentHost.cpp +++ b/Malmo/src/AgentHost.cpp @@ -897,9 +897,10 @@ namespace malmo if( !this->commands_connection ) { TimestampedString error_message( boost::posix_time::microsec_clock::universal_time(), - "AgentHost::sendCommand : commands connection is not open. Is the mission running?" + "AgentHost::sendCommand : commands connection is not open. Attempting to start a new commands connection." ); this->world_state.errors.push_back( boost::make_shared( error_message ) ); + this->openCommandsConnection(); return; } diff --git a/Malmo/src/MissionSpec.cpp b/Malmo/src/MissionSpec.cpp index 89270e4a7..3b01d884c 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,11 @@ 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) @@ -376,6 +384,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"); } @@ -420,11 +431,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 --------------------------------------------------- @@ -522,6 +538,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"); @@ -582,6 +607,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 5804f0b0e..2595495b4 100755 --- a/Malmo/src/MissionSpec.h +++ b/Malmo/src/MissionSpec.h @@ -299,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. @@ -382,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/Client/ClientStateMachine.java b/Minecraft/src/main/java/com/microsoft/Malmo/Client/ClientStateMachine.java index c3c659f84..6d4b1c473 100755 --- a/Minecraft/src/main/java/com/microsoft/Malmo/Client/ClientStateMachine.java +++ b/Minecraft/src/main/java/com/microsoft/Malmo/Client/ClientStateMachine.java @@ -955,6 +955,7 @@ protected void execute() throws Exception { // A specific port has been requested, and it's not the one we are currently using, // so we need to recreate our poller. + System.out.println("Requested command port is not the same as the input poller port; the port was not free. Stopping server."); ClientStateMachine.this.controlInputPoller.stopServer(); ClientStateMachine.this.controlInputPoller = null; } @@ -964,6 +965,7 @@ protected void execute() throws Exception ClientStateMachine.this.controlInputPoller = new TCPInputPoller(AddressHelper.MIN_FREE_PORT, AddressHelper.MAX_FREE_PORT, true, "com"); else ClientStateMachine.this.controlInputPoller = new TCPInputPoller(requestedPort, "com"); + System.out.println("Starting command server."); ClientStateMachine.this.controlInputPoller.start(); } // Make sure the cac is up-to-date: 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/AgentQuitFromCollectingItemImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/AgentQuitFromCollectingItemImplementation.java index 42b37c887..ab8f95b4e 100644 --- a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/AgentQuitFromCollectingItemImplementation.java +++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/AgentQuitFromCollectingItemImplementation.java @@ -1,6 +1,7 @@ package com.microsoft.Malmo.MissionHandlers; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import net.minecraft.item.ItemStack; @@ -14,36 +15,36 @@ import com.microsoft.Malmo.Schemas.BlockOrItemSpecWithDescription; import com.microsoft.Malmo.Schemas.MissionInit; -public class AgentQuitFromCollectingItemImplementation extends HandlerBase implements IWantToQuit -{ - AgentQuitFromCollectingItem params; - List matchers; - String quitCode = ""; - boolean wantToQuit = false; +/** + * Quits the mission when the agent has collected the right amount of items. The count on the item collection is absolute. + */ +public class AgentQuitFromCollectingItemImplementation extends HandlerBase implements IWantToQuit { - public static class ItemQuitMatcher extends RewardForItemBase.ItemMatcher - { + 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) - { + ItemQuitMatcher(BlockOrItemSpecWithDescription spec) { super(spec); this.description = spec.getDescription(); } - String description() - { + String description() { return this.description; } } @Override - public boolean parseParameters(Object params) - { - if (params == null || !(params instanceof AgentQuitFromCollectingItem)) + public boolean parseParameters(Object params) { + if (!(params instanceof AgentQuitFromCollectingItem)) return false; - - this.params = (AgentQuitFromCollectingItem)params; + + this.params = (AgentQuitFromCollectingItem) params; this.matchers = new ArrayList(); for (BlockOrItemSpecWithDescription bs : this.params.getItem()) this.matchers.add(new ItemQuitMatcher(bs)); @@ -51,57 +52,99 @@ public boolean parseParameters(Object params) } @Override - public boolean doIWantToQuit(MissionInit missionInit) - { + public boolean doIWantToQuit(MissionInit missionInit) { return this.wantToQuit; } @Override - public String getOutcome() - { + public String getOutcome() { return this.quitCode; } @Override - public void prepare(MissionInit missionInit) - { + public void prepare(MissionInit missionInit) { MinecraftForge.EVENT_BUS.register(this); + collectedItems = new HashMap(); } @Override - public void cleanup() - { + public void cleanup() { MinecraftForge.EVENT_BUS.unregister(this); } @SubscribeEvent - public void onGainItem(GainItemEvent event) - { + public void onGainItem(GainItemEvent event) { checkForMatch(event.stack); } @SubscribeEvent - public void onPickupItem(EntityItemPickupEvent event) - { - if (event.getItem() != null && event.getItem().getEntityItem() != null) - { + public void onPickupItem(EntityItemPickupEvent event) { + if (event.getItem() != null) { ItemStack stack = event.getItem().getEntityItem(); checkForMatch(stack); } } - private void checkForMatch(ItemStack is) - { - if (is != null) - { - for (ItemQuitMatcher matcher : this.matchers) - { - if (matcher.matches(is)) - { - this.quitCode = matcher.description(); - this.wantToQuit = true; + /** + * 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); + + int prev = (collectedItems.get(is.getUnlocalizedName()) == null ? 0 + : collectedItems.get(is.getUnlocalizedName())); + if (variant) + collectedItems.put(is.getUnlocalizedName(), prev + is.getCount()); + else + 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); } } -} +} \ No newline at end of file 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..8b1b66af7 --- /dev/null +++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/AgentQuitFromCraftingItemImplementation.java @@ -0,0 +1,147 @@ +package com.microsoft.Malmo.MissionHandlers; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +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; + +/** + * @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; + private boolean callCraft = true; + + 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 + public void onItemCraft(PlayerEvent.ItemCraftedEvent event) { + if (callCraft) + checkForMatch(event.crafting); + + callCraft = !callCraft; + } + + /** + * 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); + + int prev = (craftedItems.get(is.getUnlocalizedName()) == null ? 0 + : craftedItems.get(is.getUnlocalizedName())); + if (variant) + craftedItems.put(is.getUnlocalizedName(), prev + is.getCount()); + else + 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..717164a33 --- /dev/null +++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/AgentQuitFromPossessingItemImplementation.java @@ -0,0 +1,192 @@ +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.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; + +/** + * @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 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 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); + collectedItems = new HashMap(); + } + + @Override + public void cleanup() { + MinecraftForge.EVENT_BUS.unregister(this); + } + + @SubscribeEvent + public void onGainItem(GainItemEvent event) { + checkForMatch(event.stack); + } + + @SubscribeEvent + public void onPickupItem(EntityItemPickupEvent event) { + if (event.getItem() != null) + checkForMatch(event.getItem().getEntityItem()); + } + + @SubscribeEvent + public void onLoseItem(LoseItemEvent event) { + if (event.stack != null) + removeCollectedItemCount(event.stack); + } + + @SubscribeEvent + public void onDropItem(ItemTossEvent event) { + removeCollectedItemCount(event.getEntityItem().getEntityItem()); + } + + @SubscribeEvent + public void onDestroyItem(PlayerDestroyItemEvent event) { + removeCollectedItemCount(event.getOriginal()); + } + + @SubscribeEvent + public void onBlockPlace(PlaceEvent event) { + if (!event.isCanceled() && event.getPlacedBlock() != null) { + ItemStack stack = new ItemStack(event.getPlacedBlock().getBlock()); + removeCollectedItemCount(stack); + } + } + + /** + * 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); + + int prev = (collectedItems.get(is.getUnlocalizedName()) == null ? 0 + : collectedItems.get(is.getUnlocalizedName())); + if (variant) + collectedItems.put(is.getUnlocalizedName(), prev + is.getCount()); + else + collectedItems.put(is.getItem().getUnlocalizedName(), prev + is.getCount()); + + } + + private void removeCollectedItemCount(ItemStack is) { + boolean variant = getVariant(is); + + int prev = (collectedItems.get(is.getUnlocalizedName()) == null ? 0 + : collectedItems.get(is.getUnlocalizedName())); + if (variant) + collectedItems.put(is.getUnlocalizedName(), prev - is.getCount()); + else + 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); + } + } +} \ No newline at end of file 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..269317a92 --- /dev/null +++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/AgentQuitFromSmeltingItemImplementation.java @@ -0,0 +1,150 @@ +package com.microsoft.Malmo.MissionHandlers; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +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; + +/** + * @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; + private int callSmelt = 0; + + 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 + public void onItemSmelt(PlayerEvent.ItemSmeltedEvent event) { + if (callSmelt % 4 == 0) + checkForMatch(event.smelting); + + callSmelt = (callSmelt + 1) % 4; + } + + /** + * 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/AgentQuitFromTouchingBlockTypeImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/AgentQuitFromTouchingBlockTypeImplementation.java index 73b2dab14..c42e15cc8 100755 --- a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/AgentQuitFromTouchingBlockTypeImplementation.java +++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/AgentQuitFromTouchingBlockTypeImplementation.java @@ -32,7 +32,6 @@ 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.AgentQuitFromTouchingBlockType; import com.microsoft.Malmo.Schemas.BlockSpec; import com.microsoft.Malmo.Schemas.BlockSpecWithDescription; 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..a7a08d951 --- /dev/null +++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/NearbyCraftCommandsImplementation.java @@ -0,0 +1,201 @@ +// -------------------------------------------------------------------------------------------------- +// 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 java.util.List; + +import net.minecraft.block.BlockWorkbench; +import net.minecraft.client.Minecraft; +import net.minecraft.client.entity.EntityPlayerSP; +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.entity.living.LivingEvent; +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.NearbyCraftCommand; +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; + + @SubscribeEvent + public void onJump(LivingEvent.LivingJumpEvent event) { + if (event.getEntity() instanceof EntityPlayerSP) { + System.out.println("Jump! Attempting to craft sticks."); + onExecute("craftNearby", "stick", null); + } + } + + 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) { + System.out.println("We are close enough to the table"); + // We are close enough, try crafting recipes + List matching_recipes = CraftingHelper.getRecipesForRequestedOutput(message.parameters); + for (IRecipe recipe : matching_recipes) + if (CraftingHelper.attemptCrafting(player, recipe)) { + System.out.println("Crafted " + recipe.getRecipeOutput().toString()); + return null; + } + } else + System.out.println("We are not close enough to a table"); + + System.out.println("Either didn't have the requisite materials or weren't close enough to a table"); + 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; + } +} 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..cff9f6a2a --- /dev/null +++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/NearbySmeltCommandsImplementation.java @@ -0,0 +1,181 @@ +// -------------------------------------------------------------------------------------------------- +// 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.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(); + } + + @Override + public void deinstall(MissionInit missionInit) { + } + + @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..aa23a8ce4 --- /dev/null +++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/PlaceCommandsImplementation.java @@ -0,0 +1,170 @@ +// -------------------------------------------------------------------------------------------------- +// 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); + } + } + + /* + + InventoryPlayer inv = player.inventory; + Block b = Block.getBlockFromName(parameter); + if (b == null || b.getRegistryName() == null) + return false; + + boolean foundBlock = false; + for (int i = 0; i < 41 && !foundBlock; i++) { + ItemStack s = inv.getStackInSlot(i).copy(); + if (b.getRegistryName().equals(s.getItem().getRegistryName())) { + int selectedHotBarItem = inv.currentItem; +// ItemStack selected = inv.getStackInSlot(selectedHotBarItem).copy(); + inv.setInventorySlotContents(selectedHotBarItem, s); + + 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 = b.getDefaultState().getCollisionBoundingBox(player.world, pos); + if (axisalignedbb == null) { + PlayerInteractEvent event = new PlayerInteractEvent.RightClickBlock(player, EnumHand.MAIN_HAND, mop.getBlockPos(), mop.sideHit, mop.hitVec); + MinecraftForge.EVENT_BUS.post(event); + if (!event.isCanceled()) { + BlockPos newPos = mop.getBlockPos().add(mop.sideHit.getDirectionVec()); + Block newB = Block.getBlockFromItem(inv.getCurrentItem().getItem()); + IBlockState blockType = newB.getStateFromMeta(inv.getCurrentItem().getMetadata()); + if (player.world.setBlockState(newPos, blockType)) { + BlockSnapshot snapshot = new BlockSnapshot(player.world, newPos, blockType); + BlockEvent.PlaceEvent placeEvent = new BlockEvent.PlaceEvent(snapshot, player.world.getBlockState(mop.getBlockPos()), player, EnumHand.MAIN_HAND); + MinecraftForge.EVENT_BUS.post(placeEvent); + // We set the block, so remove it from the inventory. + if (!player.isCreative()) { + if (player.inventory.getCurrentItem().getCount() > 1) + player.inventory.getCurrentItem().setCount(player.inventory.getCurrentItem().getCount() - 1); + else + player.inventory.mainInventory.get(player.inventory.currentItem).setCount(0); + } + } + } + } + } + foundBlock = true; + + inv.setInventorySlotContents(i, inv.getCurrentItem()); +// inv.setInventorySlotContents(selectedHotBarItem, selected); + } + } + */ + + 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; + } +} \ No newline at end of file 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..ef775b916 100755 --- a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForCollectingItemImplementation.java +++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForCollectingItemImplementation.java @@ -1,31 +1,19 @@ -// -------------------------------------------------------------------------------------------------- -// 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 io.netty.buffer.Unpooled; - +import java.util.ArrayList; +import java.util.HashMap; import java.util.Map; -import javax.xml.bind.DatatypeConverter; +import com.microsoft.Malmo.MalmoMod; +import com.microsoft.Malmo.MalmoMod.IMalmoMessageListener; +import com.microsoft.Malmo.MalmoMod.MalmoMessageType; +import com.microsoft.Malmo.MissionHandlerInterfaces.IRewardProducer; +import com.microsoft.Malmo.Schemas.BlockOrItemSpecWithReward; +import com.microsoft.Malmo.Schemas.MissionInit; +import com.microsoft.Malmo.Schemas.RewardForCollectingItem; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.item.ItemStack; import net.minecraftforge.common.MinecraftForge; @@ -34,95 +22,171 @@ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.network.ByteBufUtils; -import com.microsoft.Malmo.MalmoMod; -import com.microsoft.Malmo.MalmoMod.IMalmoMessageListener; -import com.microsoft.Malmo.MalmoMod.MalmoMessageType; -import com.microsoft.Malmo.MissionHandlerInterfaces.IRewardProducer; -import com.microsoft.Malmo.Schemas.BlockOrItemSpecWithReward; -import com.microsoft.Malmo.Schemas.MissionInit; -import com.microsoft.Malmo.Schemas.RewardForCollectingItem; +import javax.xml.bind.DatatypeConverter; + +/** + * @author Cayden Codel, Carnegie Mellon University + *

+ * Sends a reward when the agent collected the specified item with + * specified amounts. Counter is absolute. + */ +public class RewardForCollectingItemImplementation extends RewardForItemBase + implements IRewardProducer, IMalmoMessageListener { -public class RewardForCollectingItemImplementation extends RewardForItemBase implements IRewardProducer, IMalmoMessageListener -{ private RewardForCollectingItem params; + private ArrayList matchers; + private HashMap craftedItems; - @Override - public void onMessage(MalmoMessageType messageType, Map data) - { - String bufstring = data.get("message"); - ByteBuf buf = Unpooled.copiedBuffer(DatatypeConverter.parseBase64Binary(bufstring)); - ItemStack itemStack = ByteBufUtils.readItemStack(buf); - if (itemStack != null && itemStack.getItem() != null) - { - accumulateReward(this.params.getDimension(), itemStack); + @SubscribeEvent + public void onPickupItem(EntityItemPickupEvent event) { + if (event.getItem() != null) { + checkForMatch(event.getItem().getEntityItem()); + if (event.getEntityPlayer() instanceof EntityPlayerMP) + sendItemStackToClient((EntityPlayerMP) event.getEntityPlayer(), MalmoMessageType.SERVER_COLLECTITEM, event.getItem().getEntityItem()); } - else - { - System.out.println("Error - couldn't understand the itemstack we received."); + } + + @SubscribeEvent + public void onGainItem(GainItemEvent event) { + if (event.stack != null) { + accumulateReward(this.params.getDimension(), event.stack); } } - - public static class GainItemEvent extends Event - { - public final ItemStack stack; - public GainItemEvent(ItemStack stack) - { - this.stack = stack; + /** + * 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; } - @Override - public boolean parseParameters(Object params) { - if (params == null || !(params instanceof RewardForCollectingItem)) - return false; + private int getCollectedItemCount(ItemStack is) { + boolean variant = getVariant(is); - // Build up a map of rewards per item: - this.params = (RewardForCollectingItem) params; - for (BlockOrItemSpecWithReward is : this.params.getItem()) - addItemSpecToRewardStructure(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()); + } - return true; + private void addCollectedItemCount(ItemStack is) { + boolean variant = getVariant(is); + + int prev = (craftedItems.get(is.getUnlocalizedName()) == null ? 0 + : craftedItems.get(is.getUnlocalizedName())); + if (variant) + craftedItems.put(is.getUnlocalizedName(), prev + is.getCount()); + else + craftedItems.put(is.getItem().getUnlocalizedName(), prev + is.getCount()); } - @SubscribeEvent - public void onGainItem(GainItemEvent event) - { - if (event.stack != null) - { - accumulateReward(this.params.getDimension(), event.stack); + private void checkForMatch(ItemStack is) { + int savedCollected = getCollectedItemCount(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++) { + this.adjustAndDistributeReward( + ((BlockOrItemSpecWithReward) matcher.matchSpec).getReward().floatValue(), + params.getDimension(), + ((BlockOrItemSpecWithReward) matcher.matchSpec).getDistribution()); + } + + } else if (savedCollected != 0 && savedCollected >= matcher.matchSpec.getAmount()) { + // Do nothing + } else { + for (int i = 0; i < is.getCount() && i < matcher.matchSpec.getAmount(); i++) { + this.adjustAndDistributeReward( + ((BlockOrItemSpecWithReward) matcher.matchSpec).getReward().floatValue(), + params.getDimension(), + ((BlockOrItemSpecWithReward) matcher.matchSpec).getDistribution()); + } + } + } else { + if (savedCollected < matcher.matchSpec.getAmount() + && savedCollected + is.getCount() >= matcher.matchSpec.getAmount()) { + this.adjustAndDistributeReward( + ((BlockOrItemSpecWithReward) matcher.matchSpec).getReward().floatValue(), + params.getDimension(), + ((BlockOrItemSpecWithReward) matcher.matchSpec).getDistribution()); + } + } + } + } + + addCollectedItemCount(is); } } - @SubscribeEvent - public void onPickupItem(EntityItemPickupEvent event) - { - if (event.getItem() != null && event.getEntityPlayer() instanceof EntityPlayerMP ) - { - // This event is received on the server side, so we need to pass it to the client. - sendItemStackToClient((EntityPlayerMP)event.getEntityPlayer(), MalmoMessageType.SERVER_COLLECTITEM, event.getItem().getEntityItem()); - } + @Override + public boolean parseParameters(Object params) { + if (!(params instanceof RewardForCollectingItem)) + return false; + + matchers = new ArrayList(); + + this.params = (RewardForCollectingItem) params; + for (BlockOrItemSpecWithReward spec : this.params.getItem()) + this.matchers.add(new ItemMatcher(spec)); + + return true; } @Override - public void prepare(MissionInit missionInit) - { + public void prepare(MissionInit missionInit) { super.prepare(missionInit); MinecraftForge.EVENT_BUS.register(this); MalmoMod.MalmoMessageHandler.registerForMessage(this, MalmoMessageType.SERVER_COLLECTITEM); + craftedItems = new HashMap(); } @Override - public void getReward(MissionInit missionInit, MultidimensionalReward reward) - { + public void getReward(MissionInit missionInit, MultidimensionalReward reward) { super.getReward(missionInit, reward); } @Override - public void cleanup() - { + public void cleanup() { super.cleanup(); MinecraftForge.EVENT_BUS.unregister(this); MalmoMod.MalmoMessageHandler.deregisterForMessage(this, MalmoMessageType.SERVER_COLLECTITEM); } -} + + @Override + public void onMessage(MalmoMessageType messageType, Map data) { + String buffString = data.get("message"); + ByteBuf buf = Unpooled.copiedBuffer(DatatypeConverter.parseBase64Binary(buffString)); + ItemStack itemStack = ByteBufUtils.readItemStack(buf); + if (itemStack != null) { + accumulateReward(this.params.getDimension(), itemStack); + } else { + System.out.println("Error - couldn't understand the itemstack we received."); + } + } + + public static class GainItemEvent extends Event { + public final ItemStack stack; + + public GainItemEvent(ItemStack stack) { + this.stack = stack; + } + } +} \ No newline at end of file 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..0b6ed8030 --- /dev/null +++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForCraftingItemImplementation.java @@ -0,0 +1,162 @@ +package com.microsoft.Malmo.MissionHandlers; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +import com.microsoft.Malmo.MalmoMod; +import com.microsoft.Malmo.MalmoMod.IMalmoMessageListener; +import com.microsoft.Malmo.MalmoMod.MalmoMessageType; +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.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, IMalmoMessageListener { + + private RewardForCraftingItem params; + private ArrayList matchers; + private HashMap craftedItems; + private boolean callCraft = true; + + @SubscribeEvent + public void onItemCraft(PlayerEvent.ItemCraftedEvent event) { + if (callCraft) + checkForMatch(event.crafting); + + callCraft = !callCraft; + } + + /** + * 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); + + int prev = (craftedItems.get(is.getUnlocalizedName()) == null ? 0 + : craftedItems.get(is.getUnlocalizedName())); + if (variant) + craftedItems.put(is.getUnlocalizedName(), prev + is.getCount()); + else + 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++) { + this.adjustAndDistributeReward( + ((BlockOrItemSpecWithReward) matcher.matchSpec).getReward().floatValue(), + params.getDimension(), + ((BlockOrItemSpecWithReward) matcher.matchSpec).getDistribution()); + } + + } else if (savedCrafted != 0 && savedCrafted >= matcher.matchSpec.getAmount()) { + // Do nothing + } else { + for (int i = 0; i < is.getCount() && i < matcher.matchSpec.getAmount(); i++) { + this.adjustAndDistributeReward( + ((BlockOrItemSpecWithReward) matcher.matchSpec).getReward().floatValue(), + params.getDimension(), + ((BlockOrItemSpecWithReward) matcher.matchSpec).getDistribution()); + } + } + } else { + if (savedCrafted < matcher.matchSpec.getAmount() + && savedCrafted + is.getCount() >= matcher.matchSpec.getAmount()) { + this.adjustAndDistributeReward( + ((BlockOrItemSpecWithReward) matcher.matchSpec).getReward().floatValue(), + params.getDimension(), + ((BlockOrItemSpecWithReward) matcher.matchSpec).getDistribution()); + } + } + } + } + + 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); + MalmoMod.MalmoMessageHandler.registerForMessage(this, MalmoMessageType.SERVER_COLLECTITEM); + 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); + MalmoMod.MalmoMessageHandler.deregisterForMessage(this, MalmoMessageType.SERVER_COLLECTITEM); + } + + @Override + public void onMessage(MalmoMessageType messageType, Map data) { + } +} \ No newline at end of file diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForDistanceTraveledToCompassTargetImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForDistanceTraveledToCompassTargetImplementation.java index d0ee7ec3e..9f9e584db 100644 --- a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForDistanceTraveledToCompassTargetImplementation.java +++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForDistanceTraveledToCompassTargetImplementation.java @@ -1,5 +1,7 @@ package com.microsoft.Malmo.MissionHandlers; +import java.lang.*; + import com.microsoft.Malmo.Schemas.MissionInit; import com.microsoft.Malmo.Schemas.RewardForDistanceTraveledToCompassTarget; @@ -7,6 +9,7 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.entity.EntityPlayerSP; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; public class RewardForDistanceTraveledToCompassTargetImplementation extends RewardBase { @@ -14,6 +17,7 @@ public class RewardForDistanceTraveledToCompassTargetImplementation extends Rewa double previousDistance; float totalReward; boolean positionInitialized; + BlockPos prevSpawn; @Override public boolean parseParameters(Object params) @@ -25,9 +29,9 @@ public boolean parseParameters(Object params) this.params = (RewardForDistanceTraveledToCompassTarget)params; EntityPlayerSP player = Minecraft.getMinecraft().player; - BlockPos spawn = player.world.getSpawnPoint(); + prevSpawn = player.world.getSpawnPoint(); BlockPos playerLoc = player.getPosition(); - this.previousDistance = playerLoc.getDistance(spawn.getX(), spawn.getY(), spawn.getZ()); + this.previousDistance = playerLoc.getDistance(prevSpawn.getX(), prevSpawn.getY(), prevSpawn.getZ()); this.totalReward = 0; this.positionInitialized = false; @@ -42,10 +46,11 @@ public void getReward(MissionInit missionInit, MultidimensionalReward reward) EntityPlayerSP player = Minecraft.getMinecraft().player; BlockPos spawn = player.world.getSpawnPoint(); - BlockPos playerLoc = player.getPosition(); + Vec3d playerLoc = player.getPositionVector(); + Vec3d spawnPos = new Vec3d(spawn.getX(), spawn.getY(), spawn.getZ()); - double currentDistance = playerLoc.getDistance(spawn.getX(), spawn.getY(), spawn.getZ()); - float delta = -1 * (float)(currentDistance - previousDistance); + double currentDistance = playerLoc.distanceTo(spawnPos); + float delta = (float)(this.previousDistance - currentDistance); switch (this.params.getDensity()) { case MISSION_END: @@ -65,12 +70,14 @@ public void getReward(MissionInit missionInit, MultidimensionalReward reward) } // Avoid sending large rewards as the result of an initial teleport event - if(!this.positionInitialized && (delta < -0.0001 || 0.0001 < delta)){ - this.positionInitialized = true; + if (this.prevSpawn.getX() != spawn.getX() || + this.prevSpawn.getY() != spawn.getY() || + this.prevSpawn.getZ() != spawn.getZ()) { this.totalReward = 0; } - this.previousDistance = playerLoc.getDistance(spawn.getX(), spawn.getY(), spawn.getZ()); + this.previousDistance = currentDistance; + this.prevSpawn = spawn; super.getReward(missionInit, reward); if (sendReward) 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..33473a80b --- /dev/null +++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForPossessingItemImplementation.java @@ -0,0 +1,232 @@ +package com.microsoft.Malmo.MissionHandlers; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +import com.microsoft.Malmo.MalmoMod; +import com.microsoft.Malmo.MalmoMod.IMalmoMessageListener; +import com.microsoft.Malmo.MalmoMod.MalmoMessageType; +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 io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; + +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.Event; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.network.ByteBufUtils; + +import javax.xml.bind.DatatypeConverter; + +/** + * @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, IMalmoMessageListener { + + private RewardForPossessingItem params; + private ArrayList matchers; + private HashMap collectedItems; + + @SubscribeEvent + public void onPickupItem(EntityItemPickupEvent event) { + if (event.getItem() != null) { + checkForMatch(event.getItem().getEntityItem()); + if (event.getEntityPlayer() instanceof EntityPlayerMP) + sendItemStackToClient((EntityPlayerMP) event.getEntityPlayer(), MalmoMessageType.SERVER_COLLECTITEM, event.getItem().getEntityItem()); + } + } + + @SubscribeEvent + public void onGainItem(GainItemEvent event) { + if (event.stack != null) { + accumulateReward(this.params.getDimension(), event.stack); + } + } + + @SubscribeEvent + public void onLoseItem(LoseItemEvent event) { + if (event.stack != null) + removeCollectedItemCount(event.stack); + } + + @SubscribeEvent + public void onDropItem(ItemTossEvent event) { + removeCollectedItemCount(event.getEntityItem().getEntityItem()); + } + + @SubscribeEvent + public void onDestroyItem(PlayerDestroyItemEvent event) { + removeCollectedItemCount(event.getOriginal()); + } + + @SubscribeEvent + public void onBlockPlace(PlaceEvent event) { + if (!event.isCanceled() && event.getPlacedBlock() != null) { + ItemStack stack = new ItemStack(event.getPlacedBlock().getBlock()); + removeCollectedItemCount(stack); + } + } + + /** + * 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); + + int prev = (collectedItems.get(is.getUnlocalizedName()) == null ? 0 + : collectedItems.get(is.getUnlocalizedName())); + if (variant) + collectedItems.put(is.getUnlocalizedName(), prev + is.getCount()); + else + collectedItems.put(is.getItem().getUnlocalizedName(), prev + is.getCount()); + } + + private void removeCollectedItemCount(ItemStack is) { + boolean variant = getVariant(is); + + int prev = (collectedItems.get(is.getUnlocalizedName()) == null ? 0 + : collectedItems.get(is.getUnlocalizedName())); + if (variant) + collectedItems.put(is.getUnlocalizedName(), prev - is.getCount()); + else + collectedItems.put(is.getItem().getUnlocalizedName(), prev - is.getCount()); + } + + private void checkForMatch(ItemStack is) { + int savedCollected = getCollectedItemCount(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++) { + this.adjustAndDistributeReward( + ((BlockOrItemSpecWithReward) matcher.matchSpec).getReward().floatValue(), + params.getDimension(), + ((BlockOrItemSpecWithReward) matcher.matchSpec).getDistribution()); + } + + } else if (savedCollected != 0 && savedCollected >= matcher.matchSpec.getAmount()) { + // Do nothing + } else { + for (int i = 0; i < is.getCount() && i < matcher.matchSpec.getAmount(); i++) { + this.adjustAndDistributeReward( + ((BlockOrItemSpecWithReward) matcher.matchSpec).getReward().floatValue(), + params.getDimension(), + ((BlockOrItemSpecWithReward) matcher.matchSpec).getDistribution()); + } + } + } else { + if (savedCollected < matcher.matchSpec.getAmount() + && savedCollected + is.getCount() >= matcher.matchSpec.getAmount()) { + this.adjustAndDistributeReward( + ((BlockOrItemSpecWithReward) matcher.matchSpec).getReward().floatValue(), + params.getDimension(), + ((BlockOrItemSpecWithReward) matcher.matchSpec).getDistribution()); + } + } + } + } + + 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); + MalmoMod.MalmoMessageHandler.registerForMessage(this, MalmoMessageType.SERVER_COLLECTITEM); + 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); + MalmoMod.MalmoMessageHandler.deregisterForMessage(this, MalmoMessageType.SERVER_COLLECTITEM); + } + + @Override + public void onMessage(MalmoMessageType messageType, Map data) { + String buffString = data.get("message"); + ByteBuf buf = Unpooled.copiedBuffer(DatatypeConverter.parseBase64Binary(buffString)); + ItemStack itemStack = ByteBufUtils.readItemStack(buf); + if (itemStack != null) { + accumulateReward(this.params.getDimension(), itemStack); + } else { + System.out.println("Error - couldn't understand the itemstack we received."); + } + } + + public static class GainItemEvent extends Event { + public final ItemStack stack; + + public GainItemEvent(ItemStack stack) { + this.stack = stack; + } + } +} \ No newline at end of file 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..9f3ccc761 --- /dev/null +++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForSmeltingItemImplementation.java @@ -0,0 +1,164 @@ +package com.microsoft.Malmo.MissionHandlers; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +import com.microsoft.Malmo.MalmoMod; +import com.microsoft.Malmo.MalmoMod.IMalmoMessageListener; +import com.microsoft.Malmo.MalmoMod.MalmoMessageType; +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.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, IMalmoMessageListener { + + private RewardForSmeltingItem params; + private ArrayList matchers; + private HashMap craftedItems; + private int callSmelt = 0; + + @SubscribeEvent + public void onItemSmelt(PlayerEvent.ItemSmeltedEvent event) { + if (callSmelt % 4 == 0) + checkForMatch(event.smelting); + + callSmelt = (callSmelt + 1) % 4; + } + + /** + * 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 (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 addSmeltedItemCount(ItemStack is) { + boolean variant = getVariant(is); + + int prev = (craftedItems.get(is.getUnlocalizedName()) == null ? 0 + : craftedItems.get(is.getUnlocalizedName())); + if (variant) + craftedItems.put(is.getUnlocalizedName(), prev + is.getCount()); + else + craftedItems.put(is.getItem().getUnlocalizedName(), prev + is.getCount()); + } + + private void checkForMatch(ItemStack is) { + System.out.println("[REWARD] Checking stack " + is.getUnlocalizedName() + " " + is.getCount()); + int savedSmelted = getSmeltedItemCount(is); + System.out.println("[REWARD] Previous saved amount is " + savedSmelted); + 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++) { + System.out.println("Giving reward"); + this.adjustAndDistributeReward( + ((BlockOrItemSpecWithReward) matcher.matchSpec).getReward().floatValue(), + params.getDimension(), + ((BlockOrItemSpecWithReward) matcher.matchSpec).getDistribution()); + } + } else if (savedSmelted != 0 && savedSmelted >= matcher.matchSpec.getAmount()) { + // Do nothing + } else { + for (int i = 0; i < is.getCount() && i < matcher.matchSpec.getAmount(); i++) { + System.out.println("Giving reward"); + this.adjustAndDistributeReward( + ((BlockOrItemSpecWithReward) matcher.matchSpec).getReward().floatValue(), + params.getDimension(), + ((BlockOrItemSpecWithReward) matcher.matchSpec).getDistribution()); + } + } + } else { + if (savedSmelted < matcher.matchSpec.getAmount() + && savedSmelted + is.getCount() >= matcher.matchSpec.getAmount()) { + System.out.println("Giving reward"); + this.adjustAndDistributeReward( + ((BlockOrItemSpecWithReward) matcher.matchSpec).getReward().floatValue(), + params.getDimension(), + ((BlockOrItemSpecWithReward) matcher.matchSpec).getDistribution()); + } + } + } + } + + 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); + MalmoMod.MalmoMessageHandler.registerForMessage(this, MalmoMessageType.SERVER_COLLECTITEM); + 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); + MalmoMod.MalmoMessageHandler.deregisterForMessage(this, MalmoMessageType.SERVER_COLLECTITEM); + } + + @Override + public void onMessage(MalmoMessageType messageType, Map data) { + } +} \ No newline at end of file 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..90d943f5b 100755 --- a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/SimpleCraftCommandsImplementation.java +++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/SimpleCraftCommandsImplementation.java @@ -26,6 +26,8 @@ import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.item.ItemStack; import net.minecraft.item.crafting.IRecipe; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.fml.common.gameevent.PlayerEvent; import net.minecraftforge.fml.common.network.ByteBufUtils; import net.minecraftforge.fml.common.network.simpleimpl.IMessage; import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; @@ -104,7 +106,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..3f22ecf5a 100755 --- a/Minecraft/src/main/java/com/microsoft/Malmo/Utils/CraftingHelper.java +++ b/Minecraft/src/main/java/com/microsoft/Malmo/Utils/CraftingHelper.java @@ -41,6 +41,7 @@ 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; @@ -202,6 +203,32 @@ public static boolean playerHasIngredients(EntityPlayerMP player, List + * 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(EntityPlayerSP 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; + } + /** Compare two ItemStacks and see if their items match - take wildcards into account, don't take stacksize into account. * @param A ItemStack A * @param B ItemStack B @@ -338,7 +365,7 @@ public static List getRecipesForRequestedOutput(String output) if (obj instanceof IRecipe) { ItemStack is = ((IRecipe)obj).getRecipeOutput(); - if (is == null) + if (is == null || target == null) continue; if (ItemStack.areItemsEqual(is, target)) { diff --git a/Minecraft/src/main/resources/schemas.index b/Minecraft/src/main/resources/schemas.index index 70b4a99d8..55dcedb71 100644 --- a/Minecraft/src/main/resources/schemas.index +++ b/Minecraft/src/main/resources/schemas.index @@ -1,5 +1,5 @@ -Mission.xsd -MissionEnded.xsd MissionHandlers.xsd MissionInit.xsd +MissionEnded.xsd Types.xsd +Mission.xsd diff --git a/Schemas/Mission.xsd b/Schemas/Mission.xsd old mode 100755 new mode 100644 index 12261c8e0..0dd229ae3 --- a/Schemas/Mission.xsd +++ b/Schemas/Mission.xsd @@ -353,9 +353,12 @@ + + + @@ -368,6 +371,9 @@ + + + @@ -378,6 +384,9 @@ + + + @@ -398,6 +407,8 @@ + + diff --git a/Schemas/Mission.xsd.in b/Schemas/Mission.xsd.in index c04f7b6f0..056b460ca 100644 --- a/Schemas/Mission.xsd.in +++ b/Schemas/Mission.xsd.in @@ -353,9 +353,12 @@ + + + @@ -368,6 +371,9 @@ + + + @@ -378,6 +384,9 @@ + + + @@ -398,6 +407,8 @@ + + diff --git a/Schemas/MissionEnded.xsd b/Schemas/MissionEnded.xsd old mode 100755 new mode 100644 diff --git a/Schemas/MissionHandlers.xsd b/Schemas/MissionHandlers.xsd old mode 100755 new mode 100644 index 14593c411..4a91670d0 --- a/Schemas/MissionHandlers.xsd +++ b/Schemas/MissionHandlers.xsd @@ -9,7 +9,7 @@ version="0.37"> - + @@ -139,28 +139,28 @@ - + - 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" />}}} + Generates a survival world with the specified biome. - + - The path to the saved world folder. + 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 source filename). + 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. @@ -176,28 +176,28 @@ - + - Generates a survival world with the specified biome. + 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 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 + 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 seed). + 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. @@ -1257,9 +1257,9 @@ "{{{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}}}. + "{{{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}}}. + "{{{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). @@ -1267,7 +1267,7 @@ "{{{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. + "{{{use 1}}}" - start 'use'ing (1 = start, 0 = stop). The 'use' command is for placing blocks and for other things too. @@ -1401,10 +1401,10 @@ To select a hotbar slot: - "{{{hotbar.1 1}}}" - "{{{hotbar.1 0}}}" + "{{{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 + 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 @@ -1415,7 +1415,7 @@ "{{{swapInventoryItems 3 Chest:0}}}" - Note that this is the same as writing + Note that this is the same as writing "{{{swapInventoryItems Inventory:3 Chest:0}}}" @@ -1445,21 +1445,64 @@ 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. + "{{{craft carpet PINK}}}" - For basic objects, use the ItemTypes or BlockTypes found in Types.xsd. Eg: + etc. + + + + + + - "{{{craft diamond_pickaxe}}}" will remove three diamonds and two sticks from the player's inventory, and add a diamond pickaxe. + + + + A command for simple nearby crafting: - For more control over colours, types etc, add a Variation or Colour. Eg: + "{{{craftNearby carpet PINK}}}" - "{{{craft 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}}}" + + + + @@ -1525,7 +1568,7 @@ - + @@ -1670,6 +1713,7 @@ + @@ -1685,6 +1729,87 @@ + + + + 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. + + + + + + + + + + + + + + + + + + + @@ -2152,12 +2277,14 @@ + + @@ -2249,16 +2376,78 @@ 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. + + + + + + + + + + + @@ -2551,6 +2740,45 @@ + + + + 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. + + + + + + + + + diff --git a/Schemas/MissionHandlers.xsd.in b/Schemas/MissionHandlers.xsd.in index af106e8be..4a91670d0 100644 --- a/Schemas/MissionHandlers.xsd.in +++ b/Schemas/MissionHandlers.xsd.in @@ -6,10 +6,10 @@ xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" elementFormDefault="qualified" jaxb:version="2.1" - version="${MALMO_VERSION_MAJOR}.${MALMO_VERSION_MINOR}"> + version="0.37"> - + @@ -139,28 +139,28 @@ - + - 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" />}}} + Generates a survival world with the specified biome. - + - The path to the saved world folder. + 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 source filename). + 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. @@ -176,28 +176,28 @@ - + - Generates a survival world with the specified biome. + 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 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 + 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 seed). + 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. @@ -1257,9 +1257,9 @@ "{{{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}}}. + "{{{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}}}. + "{{{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). @@ -1267,7 +1267,7 @@ "{{{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. + "{{{use 1}}}" - start 'use'ing (1 = start, 0 = stop). The 'use' command is for placing blocks and for other things too. @@ -1401,10 +1401,10 @@ To select a hotbar slot: - "{{{hotbar.1 1}}}" - "{{{hotbar.1 0}}}" + "{{{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 + 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 @@ -1415,7 +1415,7 @@ "{{{swapInventoryItems 3 Chest:0}}}" - Note that this is the same as writing + Note that this is the same as writing "{{{swapInventoryItems Inventory:3 Chest:0}}}" @@ -1445,21 +1445,64 @@ 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. + "{{{craft carpet PINK}}}" - For basic objects, use the ItemTypes or BlockTypes found in Types.xsd. Eg: + etc. + + + + + + - "{{{craft diamond_pickaxe}}}" will remove three diamonds and two sticks from the player's inventory, and add a diamond pickaxe. + + + + A command for simple nearby crafting: - For more control over colours, types etc, add a Variation or Colour. Eg: + "{{{craftNearby carpet PINK}}}" - "{{{craft 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}}}" + + + + @@ -1525,7 +1568,7 @@ - + @@ -1670,6 +1713,7 @@ + @@ -1685,6 +1729,87 @@ + + + + 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. + + + + + + + + + + + + + + + + + + + @@ -2152,12 +2277,14 @@ + + @@ -2249,16 +2376,78 @@ 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. + + + + + + + + + + + @@ -2551,6 +2740,45 @@ + + + + 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. + + + + + + + + + diff --git a/Schemas/MissionInit.xsd b/Schemas/MissionInit.xsd old mode 100755 new mode 100644 diff --git a/Schemas/Schemas.iml b/Schemas/Schemas.iml new file mode 100644 index 000000000..8021953ed --- /dev/null +++ b/Schemas/Schemas.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/Schemas/Types.xsd b/Schemas/Types.xsd old mode 100755 new mode 100644