diff --git a/Malmo/samples/Python_examples/MazeRunner.py b/Malmo/samples/Python_examples/MazeRunner.py
index 10700c66c..56f2f288e 100755
--- a/Malmo/samples/Python_examples/MazeRunner.py
+++ b/Malmo/samples/Python_examples/MazeRunner.py
@@ -23,61 +23,113 @@
import sys
import time
import json
+import errno
-def GetMissionXML( current_seed ):
+maze1 = '''
+
+
+ 0.5
+ random
+ random
+ false
+
+
+
+
+
+
+
+
+'''
+
+maze2 = '''
+
+
+ 0.5
+ random
+ random
+ false
+
+
+
+
+
+
+
+
+'''
+
+maze3 = '''
+
+
+ 0.2
+ random
+ random
+ false
+
+
+
+
+
+
+
+
+
+'''
+
+maze4 = '''
+
+
+ 0.5
+ random
+ random
+ false
+
+
+
+
+
+
+
+
+
+
+'''
+
+def GetMissionXML( mazeblock ):
return '''
Run the maze!
+
+
+ ''' + str(TICK_LENGTH) + '''
+
-
+ false
-
-
- 0.5
- ''' + str(current_seed) + '''
- random
- false
-
-
-
-
-
-
-
-
-
+ ''' + mazeblock + '''
+
-
+
James Bond
-
-
-
-
-
+
-
strafe
-
-
-
@@ -86,9 +138,10 @@ def GetMissionXML( current_seed ):
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0) # flush print output immediately
validate = True
-my_mission = MalmoPython.MissionSpec(GetMissionXML("random"),validate)
+mazeblocks = [maze1, maze2, maze3, maze4]
agent_host = MalmoPython.AgentHost()
+agent_host.addOptionalIntArgument( "speed,s", "Length of tick, in ms.", 50)
try:
agent_host.parse( sys.argv )
except RuntimeError as e:
@@ -106,10 +159,25 @@ def GetMissionXML( current_seed ):
else:
num_reps = 30000
-for iRepeat in range(num_reps):
+recordingsDirectory="MazeRecordings"
+TICK_LENGTH = agent_host.getIntArgument("speed")
+
+try:
+ os.makedirs(recordingsDirectory)
+except OSError as exception:
+ if exception.errno != errno.EEXIST: # ignore error if already existed
+ raise
+
+for iRepeat in xrange(num_reps):
+
+ mazeblock = random.choice(mazeblocks)
+ my_mission = MalmoPython.MissionSpec(GetMissionXML(mazeblock),validate)
+ # Set up a recording - MUST be done once for each mission - don't do this outside the loop!
+ my_mission_record = MalmoPython.MissionRecordSpec(recordingsDirectory + "//" + "Mission_" + str(iRepeat) + ".tgz")
+ my_mission_record.recordRewards()
+ my_mission_record.recordObservations()
try:
- my_mission_record = MalmoPython.MissionRecordSpec()
agent_host.startMission( my_mission, my_mission_record )
except RuntimeError as e:
print "Error starting mission:",e
@@ -130,22 +198,21 @@ def GetMissionXML( current_seed ):
# main loop:
while world_state.is_mission_running:
- world_state = agent_host.getWorldState()
- while world_state.number_of_observations_since_last_state < 1 and world_state.is_mission_running:
- print "Waiting for observations..."
- time.sleep(0.05)
- world_state = agent_host.getWorldState()
-
- if world_state.is_mission_running:
+ if world_state.number_of_observations_since_last_state > 0:
print "Got " + str(world_state.number_of_observations_since_last_state) + " observations since last state."
- msg = world_state.observations[0].text
+ msg = world_state.observations[-1].text
ob = json.loads(msg)
current_yaw_delta = ob.get(u'yawDelta', 0)
- current_speed = 1-abs(current_yaw_delta)
+ current_speed = (1-abs(current_yaw_delta))
print "Got observation: " + str(current_yaw_delta)
- agent_host.sendCommand( "move " + str(current_speed) )
- agent_host.sendCommand( "turn " + str(current_yaw_delta) )
-
+ try:
+ agent_host.sendCommand( "move " + str(current_speed) )
+ agent_host.sendCommand( "turn " + str(current_yaw_delta) )
+ except RuntimeError as e:
+ print "Failed to send command:",e
+ pass
+ world_state = agent_host.getWorldState()
+
print "Mission has stopped."
time.sleep(0.5) # Give mod a little time to get back to dormant state.
diff --git a/Malmo/samples/Python_examples/MultiMaze.py b/Malmo/samples/Python_examples/MultiMaze.py
index 95b5943d8..d74269f0a 100755
--- a/Malmo/samples/Python_examples/MultiMaze.py
+++ b/Malmo/samples/Python_examples/MultiMaze.py
@@ -57,6 +57,7 @@ def GetMissionXML( current_seed, xorg, yorg, zorg ):
cookie
+
@@ -69,7 +70,6 @@ def GetMissionXML( current_seed, xorg, yorg, zorg ):
-
@@ -89,7 +89,6 @@ def GetMissionXML( current_seed, xorg, yorg, zorg ):
-
diff --git a/Malmo/samples/Python_examples/patchwork_quilt.py b/Malmo/samples/Python_examples/patchwork_quilt.py
index 1ae002731..33462f804 100755
--- a/Malmo/samples/Python_examples/patchwork_quilt.py
+++ b/Malmo/samples/Python_examples/patchwork_quilt.py
@@ -61,6 +61,7 @@ def GetMissionXML( current_seed, xorg, yorg, zorg, iteration ):
cookie
+
@@ -71,7 +72,6 @@ def GetMissionXML( current_seed, xorg, yorg, zorg, iteration ):
-
strafe
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 ba92b3147..1affa51ee 100755
--- a/Minecraft/src/main/java/com/microsoft/Malmo/Client/ClientStateMachine.java
+++ b/Minecraft/src/main/java/com/microsoft/Malmo/Client/ClientStateMachine.java
@@ -66,6 +66,7 @@
import com.microsoft.Malmo.Client.MalmoModClient.InputType;
import com.microsoft.Malmo.MissionHandlerInterfaces.IWantToQuit;
import com.microsoft.Malmo.MissionHandlers.MissionBehaviour;
+import com.microsoft.Malmo.Schemas.AgentHandlers;
import com.microsoft.Malmo.Schemas.AgentSection;
import com.microsoft.Malmo.Schemas.ClientAgentConnection;
import com.microsoft.Malmo.Schemas.MinecraftServerConnection;
@@ -830,6 +831,10 @@ public void onMessage(MalmoMessageType messageType, Map data)
if (messageType != MalmoMessageType.SERVER_ALLPLAYERSJOINED)
return;
+ String extraHandlers = data.get("extra_handlers");
+ if (extraHandlers != null && extraHandlers.length() > 0)
+ attemptToAddExtraHandlers(extraHandlers);
+
// The server is ready, so send our MissionInit back to the agent and go!
// We launch the agent by sending it the MissionInit message we were sent (but with the Launcher's IP address included)
String xml = null;
@@ -858,6 +863,19 @@ public void onMessage(MalmoMessageType messageType, Map data)
}
}
+ private void attemptToAddExtraHandlers(String extraHandlers)
+ {
+ try
+ {
+ AgentHandlers handlers = (AgentHandlers)SchemaHelper.deserialiseObject(extraHandlers, "MissionInit.xsd", AgentHandlers.class);
+ currentMissionBehaviour().addExtraHandlers(handlers);
+ }
+ catch (Exception e)
+ {
+ // Do something... like episodeHasCompletedWithErrors(nextState, error)?
+ }
+ }
+
@Override
public void cleanup()
{
diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MalmoMod.java b/Minecraft/src/main/java/com/microsoft/Malmo/MalmoMod.java
index c51315b8a..8f997e047 100755
--- a/Minecraft/src/main/java/com/microsoft/Malmo/MalmoMod.java
+++ b/Minecraft/src/main/java/com/microsoft/Malmo/MalmoMod.java
@@ -55,7 +55,6 @@
import com.microsoft.Malmo.MissionHandlers.AbsoluteMovementCommandsImplementation;
import com.microsoft.Malmo.MissionHandlers.ObservationFromFullStatsImplementation;
import com.microsoft.Malmo.MissionHandlers.ObservationFromGridImplementation;
-import com.microsoft.Malmo.MissionHandlers.ObservationFromMazeOptimalPathImplementation;
import com.microsoft.Malmo.Schemas.MissionInit;
import com.microsoft.Malmo.Server.MalmoModServer;
import com.microsoft.Malmo.Utils.AddressHelper;
@@ -113,7 +112,6 @@ public void preInit(FMLPreInitializationEvent event)
network.registerMessage(ObservationFromFullStatsImplementation.FullStatsRequestMessageHandler.class, ObservationFromFullStatsImplementation.FullStatsRequestMessage.class, 1, Side.SERVER);
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(ObservationFromMazeOptimalPathImplementation.OptimalPathRequestMessageHandler.class, ObservationFromMazeOptimalPathImplementation.OptimalPathRequestMessage.class, 4, Side.SERVER);
network.registerMessage(AbsoluteMovementCommandsImplementation.TeleportMessageHandler.class, AbsoluteMovementCommandsImplementation.TeleportMessage.class, 5, Side.SERVER);
network.registerMessage(MalmoMessageHandler.class, MalmoMessage.class, 6, Side.SERVER); // Malmo messages from client to server
}
diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlerInterfaces/IWorldDecorator.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlerInterfaces/IWorldDecorator.java
index 9e223cf62..913a1f2b6 100755
--- a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlerInterfaces/IWorldDecorator.java
+++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlerInterfaces/IWorldDecorator.java
@@ -21,6 +21,7 @@
import net.minecraft.world.World;
+import com.microsoft.Malmo.Schemas.AgentHandlers;
import com.microsoft.Malmo.Schemas.MissionInit;
/** Interface for objects which can determine the world structure for the Minecraft mission.
@@ -40,6 +41,12 @@ public DecoratorException(String message)
* @param missionInit the MissionInit object for the currently running mission, which may contain parameters for the observation requirements.
*/
public void buildOnWorld(MissionInit missionInit) throws DecoratorException;
+
+ /** Gives the decorator a chance to add any client-side mission handlers that might be required - eg end-points for the maze generator, etc.
+ * @param handlers A list of handlers to which the decorator can add
+ * @return true if new decorators were added
+ */
+ public boolean getExtraAgentHandlers(AgentHandlers handlers);
/** Called periodically by the server, during the mission run. Use to provide dynamic behaviour.
* @param world the World we are controlling.
diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/ClassroomDecoratorImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/ClassroomDecoratorImplementation.java
index 6d6a7684d..bf65e8547 100755
--- a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/ClassroomDecoratorImplementation.java
+++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/ClassroomDecoratorImplementation.java
@@ -31,6 +31,7 @@
import net.minecraft.world.World;
import com.microsoft.Malmo.MissionHandlerInterfaces.IWorldDecorator;
+import com.microsoft.Malmo.Schemas.AgentHandlers;
import com.microsoft.Malmo.Schemas.AgentSection;
import com.microsoft.Malmo.Schemas.PaletteEnum;
import com.microsoft.Malmo.Schemas.ClassroomDecorator;
@@ -1496,4 +1497,10 @@ private void setIgloo()
this.hint = Blocks.redstone_ore.getDefaultState();
}
}
+
+ @Override
+ public boolean getExtraAgentHandlers(AgentHandlers handlers)
+ {
+ return false;
+ }
}
diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/DrawingDecoratorImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/DrawingDecoratorImplementation.java
index 3b0001ca1..38eac85b8 100755
--- a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/DrawingDecoratorImplementation.java
+++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/DrawingDecoratorImplementation.java
@@ -23,6 +23,7 @@
import net.minecraft.world.World;
import com.microsoft.Malmo.MissionHandlerInterfaces.IWorldDecorator;
+import com.microsoft.Malmo.Schemas.AgentHandlers;
import com.microsoft.Malmo.Schemas.DrawingDecorator;
import com.microsoft.Malmo.Schemas.Mission;
import com.microsoft.Malmo.Schemas.MissionInit;
@@ -63,4 +64,10 @@ public void buildOnWorld(MissionInit missionInit)
@Override
public void update(World world) {}
+
+ @Override
+ public boolean getExtraAgentHandlers(AgentHandlers handlers)
+ {
+ return false;
+ }
}
\ No newline at end of file
diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/MazeDecoratorImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/MazeDecoratorImplementation.java
index 54fcb1d27..9adccf339 100755
--- a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/MazeDecoratorImplementation.java
+++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/MazeDecoratorImplementation.java
@@ -34,6 +34,8 @@
import com.microsoft.Malmo.MalmoMod;
import com.microsoft.Malmo.MissionHandlerInterfaces.IWorldDecorator;
+import com.microsoft.Malmo.Schemas.AgentHandlers;
+import com.microsoft.Malmo.Schemas.AgentQuitFromReachingPosition;
import com.microsoft.Malmo.Schemas.AgentSection;
import com.microsoft.Malmo.Schemas.BlockType;
import com.microsoft.Malmo.Schemas.BlockVariant;
@@ -42,7 +44,8 @@
import com.microsoft.Malmo.Schemas.MazeBlock;
import com.microsoft.Malmo.Schemas.MazeDecorator;
import com.microsoft.Malmo.Schemas.MissionInit;
-import com.microsoft.Malmo.Schemas.Pos;
+import com.microsoft.Malmo.Schemas.ObservationFromSubgoalPositionList;
+import com.microsoft.Malmo.Schemas.PointWithToleranceAndDescription;
import com.microsoft.Malmo.Schemas.PosAndDirection;
import com.microsoft.Malmo.Utils.BlockDrawingHelper;
import com.microsoft.Malmo.Utils.MinecraftTypeHelper;
@@ -72,6 +75,8 @@ public class MazeDecoratorImplementation extends HandlerBase implements IWorldDe
private int optimalPathHeight;
private int subgoalHeight;
private int gapHeight;
+ private AgentQuitFromReachingPosition quitter = null;
+ private ObservationFromSubgoalPositionList navigator = null;
int width;
int length;
@@ -432,7 +437,7 @@ private void populateNeighbours(Cell[] grid, Cell[] neighbours, int x, int z, bo
neighbours[7] = (allowDiags && x < this.width-1 && z > 0) ? grid[(x+1) + (z-1)*this.width] : null;
}
- private void findSubgoals(Cell[] grid, Cell start, Cell end, Hashtable savelocation)
+ private void findSubgoals(Cell[] grid, Cell start, Cell end)
{
System.out.println("Attempting to find subgoals...");
@@ -526,23 +531,30 @@ private void findSubgoals(Cell[] grid, Cell start, Cell end, Hashtable types, Random r)
{
if (types == null || types.size() == 0)
return "air";
return types.get(r.nextInt(types.size())).value();
}
-
+
private Colour chooseColour(List colours, Random r)
{
if (colours == null || colours.size() == 0)
@@ -738,4 +759,21 @@ private int distBetweenPoints(int x1, int z1, int x2, int z2, boolean bAllowDiag
@Override
public void update(World world) {}
+
+ @Override
+ public boolean getExtraAgentHandlers(AgentHandlers handlers)
+ {
+ boolean added = false;
+ if (this.quitter != null)
+ {
+ handlers.getAgentMissionHandlers().add(this.quitter);
+ added = true;
+ }
+ if (this.navigator != null)
+ {
+ handlers.getAgentMissionHandlers().add(this.navigator);
+ added = true;
+ }
+ return added;
+ }
}
diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/MissionBehaviour.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/MissionBehaviour.java
index daaec4e2d..9de2e23b3 100755
--- a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/MissionBehaviour.java
+++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/MissionBehaviour.java
@@ -101,6 +101,13 @@ private void initAgent(MissionInit missionInit)
createAndAddHandler(handler);
}
+ public boolean addExtraHandlers(AgentHandlers handlers)
+ {
+ for (Object handler : handlers.getAgentMissionHandlers())
+ createAndAddHandler(handler);
+ return true;
+ }
+
private void initServer(MissionInit missionInit)
{
reset();
diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/ObservationFromMazeOptimalPathImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/ObservationFromMazeOptimalPathImplementation.java
deleted file mode 100755
index e11c2585e..000000000
--- a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/ObservationFromMazeOptimalPathImplementation.java
+++ /dev/null
@@ -1,172 +0,0 @@
-// --------------------------------------------------------------------------------------------------
-// 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.Hashtable;
-import java.util.Map;
-
-import net.minecraft.entity.player.EntityPlayerMP;
-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.google.gson.JsonObject;
-import com.microsoft.Malmo.MalmoMod;
-import com.microsoft.Malmo.Schemas.MissionInit;
-
-public class ObservationFromMazeOptimalPathImplementation extends ObservationFromServer
-{
- // TODO - maze was created by Server so subgoals are stored in the server properties... make this a server-side observation producer...
- private int subgoalIndex = 0;
-
- @Override
- public void prepare(MissionInit missionInit)
- {
- super.prepare(missionInit);
- }
-
- @Override
- public void cleanup()
- {
- super.cleanup();
- }
-
- @Override
- public ObservationRequestMessage createObservationRequestMessage()
- {
- return new OptimalPathRequestMessage(this.subgoalIndex);
- }
-
- @Override
- protected void onReturnedData(Map data)
- {
- String sgi = data.get("subgoalIndex");
- if (sgi != null)
- {
- Integer i = Integer.valueOf(sgi);
- if (i != null)
- this.subgoalIndex = i;
- }
- }
-
- public static class OptimalPathRequestMessage extends ObservationRequestMessage
- {
- private int subgoalIndex = 0;
-
- public OptimalPathRequestMessage()
- {
- }
-
- public OptimalPathRequestMessage(int sgi)
- {
- this.subgoalIndex = sgi;
- }
-
- @Override
- void restoreState(ByteBuf buf)
- {
- this.subgoalIndex = buf.readInt();
- }
-
- @Override
- void persistState(ByteBuf buf)
- {
- buf.writeInt(this.subgoalIndex);
- }
-
- @Override
- public void addReturnData(Map returnData)
- {
- if (returnData != null)
- returnData.put("subgoalIndex", String.valueOf(this.subgoalIndex));
- }
- }
-
- public static class OptimalPathRequestMessageHandler extends ObservationRequestMessageHandler implements IMessageHandler
- {
- public OptimalPathRequestMessageHandler()
- {
-
- }
- @Override
- void buildJson(JsonObject json, EntityPlayerMP player, ObservationRequestMessage message, MessageContext ctx)
- {
- if (!(message instanceof OptimalPathRequestMessage))
- return;
-
- OptimalPathRequestMessage oprm = (OptimalPathRequestMessage)message;
- Hashtable properties = null;
- try
- {
- properties = MalmoMod.getPropertiesForCurrentThread();
- }
- catch (Exception e)
- {
- // Can't get the properties - no data.
- return;
- }
-
- double[] xTargets = (double[])properties.get("OptPathXCoords");
- double[] zTargets = (double[])properties.get("OptPathZCoords");
- if (xTargets == null || zTargets == null)
- return; // No data.
-
- int numTargets = Math.min(xTargets.length, zTargets.length); // Should be no difference between them but check anyway.
- if (oprm.subgoalIndex >= numTargets)
- return; // Finished.
-
- double targetx = xTargets[oprm.subgoalIndex];
- double targetz = zTargets[oprm.subgoalIndex];
- double sourcex = player.posX;
- double sourcez = player.posZ;
-
- if (Math.abs(targetx-sourcex) + Math.abs(targetz-sourcez) < 1)
- oprm.subgoalIndex++;
-
- if (oprm.subgoalIndex >= numTargets)
- return; // Finished.
-
- // Calculate which way we need to turn in order to point towards the target:
- double dx = (targetx - sourcex);
- double dz = (targetz - sourcez);
- double targetYaw = (Math.atan2(dz, dx) * 180.0/Math.PI) - 90;
- // System.out.println("I:" + oprm.subgoalIndex + "; D:(" + (targetx-sourcex) + "," + (targetz-sourcez) + "); Y:" + targetYaw);
- double sourceYaw = player.rotationYaw;
- // Find shortest angular distance between the two yaws, preserving sign:
- double difference = targetYaw - sourceYaw;
- while (difference < -180)
- difference += 360;
- while (difference > 180)
- difference -= 360;
- // Normalise:
- difference /= 180.0;
- json.addProperty("yawDelta", difference);
- }
-
- @Override
- public IMessage onMessage(OptimalPathRequestMessage message, MessageContext ctx)
- {
- processMessage(message, ctx);
- return null;
- }
- }
-}
diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/ObservationFromSubgoalPositionListImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/ObservationFromSubgoalPositionListImplementation.java
new file mode 100755
index 000000000..ca2c57e9c
--- /dev/null
+++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/ObservationFromSubgoalPositionListImplementation.java
@@ -0,0 +1,99 @@
+// --------------------------------------------------------------------------------------------------
+// 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 net.minecraft.client.Minecraft;
+import net.minecraft.client.entity.EntityPlayerSP;
+
+import com.google.gson.JsonObject;
+import com.microsoft.Malmo.MissionHandlerInterfaces.IObservationProducer;
+import com.microsoft.Malmo.Schemas.MissionInit;
+import com.microsoft.Malmo.Schemas.ObservationFromSubgoalPositionList;
+
+public class ObservationFromSubgoalPositionListImplementation extends HandlerBase implements IObservationProducer
+{
+ private int subgoalIndex = 0;
+ private ObservationFromSubgoalPositionList positions;
+
+ @Override
+ public boolean parseParameters(Object params)
+ {
+ if (params == null || !(params instanceof ObservationFromSubgoalPositionList))
+ return false;
+
+ this.positions = (ObservationFromSubgoalPositionList)params;
+ return true;
+ }
+
+ @Override
+ public void writeObservationsToJSON(JsonObject json, MissionInit missionInit)
+ {
+ int nTargets = this.positions.getPoint().size();
+ boolean foundNextPoint = false;
+ double targetx = 0;
+ double targetz = 0;
+ EntityPlayerSP player = Minecraft.getMinecraft().thePlayer;
+ if (player == null)
+ return; // Nothing we can do.
+
+ double sourcex = player.posX;
+ double sourcez = player.posZ;
+
+ while (this.subgoalIndex < nTargets && !foundNextPoint)
+ {
+ targetx = this.positions.getPoint().get(this.subgoalIndex).getX().doubleValue();
+ targetz = this.positions.getPoint().get(this.subgoalIndex).getZ().doubleValue();
+ double tol = this.positions.getPoint().get(this.subgoalIndex).getTolerance().doubleValue();
+
+ if (Math.abs(targetx-sourcex) + Math.abs(targetz-sourcez) < tol)
+ this.subgoalIndex++;
+ else
+ foundNextPoint = true;
+ }
+
+ if (!foundNextPoint)
+ return; // Finished.
+
+ // Calculate which way we need to turn in order to point towards the target:
+ double dx = (targetx - sourcex);
+ double dz = (targetz - sourcez);
+ double targetYaw = (Math.atan2(dz, dx) * 180.0/Math.PI) - 90;
+ double sourceYaw = player.rotationYaw;
+ // Find shortest angular distance between the two yaws, preserving sign:
+ double difference = targetYaw - sourceYaw;
+ while (difference < -180)
+ difference += 360;
+ while (difference > 180)
+ difference -= 360;
+ // Normalise:
+ difference /= 180.0;
+ json.addProperty("yawDelta", difference);
+ }
+
+ @Override
+ public void prepare(MissionInit missionInit)
+ {
+ }
+
+ @Override
+ public void cleanup()
+ {
+ }
+}
diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/SnakeDecoratorImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/SnakeDecoratorImplementation.java
index 93c6bfa2f..a46dd1457 100755
--- a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/SnakeDecoratorImplementation.java
+++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/SnakeDecoratorImplementation.java
@@ -32,6 +32,7 @@
import net.minecraft.world.World;
import com.microsoft.Malmo.MissionHandlerInterfaces.IWorldDecorator;
+import com.microsoft.Malmo.Schemas.AgentHandlers;
import com.microsoft.Malmo.Schemas.AgentSection;
import com.microsoft.Malmo.Schemas.BlockType;
import com.microsoft.Malmo.Schemas.BlockVariant;
@@ -288,4 +289,10 @@ private void initDimensionsAndBehaviour()
this.maxNumberOfStairs = this.snakeParams.getMaxStairLength();
this.maxPathLength = this.snakeParams.getMaxLength();
}
+
+ @Override
+ public boolean getExtraAgentHandlers(AgentHandlers handlers)
+ {
+ return false;
+ }
}
\ No newline at end of file
diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/WorldFromComposite.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/WorldFromComposite.java
index 018295cec..72c25a409 100755
--- a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/WorldFromComposite.java
+++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/WorldFromComposite.java
@@ -24,6 +24,7 @@
import net.minecraft.world.World;
import com.microsoft.Malmo.MissionHandlerInterfaces.IWorldDecorator;
+import com.microsoft.Malmo.Schemas.AgentHandlers;
import com.microsoft.Malmo.Schemas.MissionInit;
/** Composite class that manages a set of world builders
@@ -36,7 +37,7 @@ public void addBuilder(IWorldDecorator builder)
{
this.builders.add(builder);
}
-
+
@Override
public void buildOnWorld(MissionInit missionInit) throws DecoratorException
{
@@ -45,7 +46,7 @@ public void buildOnWorld(MissionInit missionInit) throws DecoratorException
builder.buildOnWorld(missionInit);
}
}
-
+
@Override
public void update(World world)
{
@@ -54,4 +55,15 @@ public void update(World world)
builder.update(world);
}
}
+
+ @Override
+ public boolean getExtraAgentHandlers(AgentHandlers handlers)
+ {
+ boolean added = false;
+ for (IWorldDecorator builder : this.builders)
+ {
+ added |= builder.getExtraAgentHandlers(handlers);
+ }
+ return added;
+ }
}
diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/Server/ServerStateMachine.java b/Minecraft/src/main/java/com/microsoft/Malmo/Server/ServerStateMachine.java
index 282f64c0f..74b3cd51b 100755
--- a/Minecraft/src/main/java/com/microsoft/Malmo/Server/ServerStateMachine.java
+++ b/Minecraft/src/main/java/com/microsoft/Malmo/Server/ServerStateMachine.java
@@ -24,6 +24,8 @@
import java.util.List;
import java.util.Map;
+import javax.xml.bind.JAXBException;
+
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.item.Item;
@@ -52,6 +54,7 @@
import com.microsoft.Malmo.StateMachine;
import com.microsoft.Malmo.MissionHandlerInterfaces.IWorldDecorator.DecoratorException;
import com.microsoft.Malmo.MissionHandlers.MissionBehaviour;
+import com.microsoft.Malmo.Schemas.AgentHandlers;
import com.microsoft.Malmo.Schemas.AgentSection;
import com.microsoft.Malmo.Schemas.AgentStart.Inventory;
import com.microsoft.Malmo.Schemas.InventoryBlock;
@@ -62,6 +65,7 @@
import com.microsoft.Malmo.Schemas.ServerInitialConditions;
import com.microsoft.Malmo.Schemas.ServerSection;
import com.microsoft.Malmo.Utils.MinecraftTypeHelper;
+import com.microsoft.Malmo.Utils.SchemaHelper;
import com.microsoft.Malmo.Utils.ScreenHelper;
import com.microsoft.Malmo.Utils.TimeHelper;
@@ -736,8 +740,25 @@ private void onCastAssembled()
{
// Ready the players:
resetPlayerGameTypes();
+ // Build up any extra mission handlers required:
+ MissionBehaviour handlers = getHandlers();
+ AgentHandlers extraHandlers = new AgentHandlers();
+ Map data = new HashMap();
+ if (handlers.worldDecorator.getExtraAgentHandlers(extraHandlers))
+ {
+ String xml;
+ try
+ {
+ xml = SchemaHelper.serialiseObject(extraHandlers, MissionInit.class);
+ data.put("extra_handlers", xml);
+ }
+ catch (JAXBException e)
+ {
+ // TODO - is this worth aborting the mission for?
+ }
+ }
// And tell them all they can proceed:
- MalmoMod.safeSendToAll(MalmoMessageType.SERVER_ALLPLAYERSJOINED);
+ MalmoMod.safeSendToAll(MalmoMessageType.SERVER_ALLPLAYERSJOINED, data);
}
@Override
diff --git a/Schemas/Mission.xsd b/Schemas/Mission.xsd
index 5188b68fb..d8ba5a563 100755
--- a/Schemas/Mission.xsd
+++ b/Schemas/Mission.xsd
@@ -208,7 +208,7 @@
-
+
diff --git a/Schemas/MissionHandlers.xsd b/Schemas/MissionHandlers.xsd
index 7133e944a..bf3c671a9 100755
--- a/Schemas/MissionHandlers.xsd
+++ b/Schemas/MissionHandlers.xsd
@@ -321,6 +321,14 @@
+
+
+
+
+
+
+
+
@@ -878,16 +886,18 @@
-
+
- When present, the Mod will return observations that say the direction to follow to the next subgoal on the maze's optimal path,
- if MazeDecorator has been used.
-
+ When present, the Mod will return observations that indicate the direction to follow to the next subgoal.
The value to turn by is returned in the JSON element {{{yawDelta}}}.
-
+
+
+
+
+
diff --git a/changelog.txt b/changelog.txt
index 7ebafd444..b96ee5635 100755
--- a/changelog.txt
+++ b/changelog.txt
@@ -1,5 +1,7 @@
0.14.0
-------------------
+New: ObservationsFromMazeOptimalPath has been turned into general purpose ObservationFromSubgoalPositionList
+New: Maze generator can now take care of quitting mission when agent reaches goal (issue #103)
New: AgentQuitFromReachingCommandQuota (issue #109)
0.13.0 (2016-07-01)