View Javadoc

1   package cz.cuni.amis.pogamut.ut2004.agent.navigation;
2   
3   import java.util.ArrayList;
4   import java.util.List;
5   import java.util.logging.Logger;
6   
7   import cz.cuni.amis.pogamut.base.agent.navigation.IPathFuture;
8   import cz.cuni.amis.pogamut.base.agent.navigation.impl.PathFuture;
9   import cz.cuni.amis.pogamut.base.communication.worldview.react.EventReact;
10  import cz.cuni.amis.pogamut.base.communication.worldview.react.EventReactOnce;
11  import cz.cuni.amis.pogamut.base.utils.math.DistanceUtils;
12  import cz.cuni.amis.pogamut.base3d.worldview.IVisionWorldView;
13  import cz.cuni.amis.pogamut.base3d.worldview.object.ILocated;
14  import cz.cuni.amis.pogamut.base3d.worldview.object.Location;
15  import cz.cuni.amis.pogamut.unreal.communication.messages.UnrealId;
16  import cz.cuni.amis.pogamut.ut2004.agent.navigation.floydwarshall.FloydWarshallMap;
17  import cz.cuni.amis.pogamut.ut2004.bot.impl.UT2004Bot;
18  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.GetPath;
19  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.EndMessage;
20  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Item;
21  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.NavPoint;
22  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.NavPointNeighbourLink;
23  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Self;
24  import cz.cuni.amis.pogamut.ut2004.communication.translator.shared.events.Path;
25  import cz.cuni.amis.utils.future.FutureStatus;
26  
27  /**
28   * {@link IPathFuture} implementation that is using UT2004 inner AStar algorithm for finding the path inside UT2004
29   * environment. 
30   * <p><p>
31   * <b>WARNING:</b> UT2004 has a limition set on the path length. It will return only the first
32   * 16 navpoints that are leading to the path's target. Whenever path executor happens to tell you, that
33   * the target is reached, you should compare your bot current location with {@link UT2004AStarPathFuture#getPathTo()}.
34   * <p><p>
35   * Note that the path that is produced by this future contains mix of {@link NavPoint} and {@link Location} objects.
36   * Usually {@link Location} objects are only the first and last elements of the path and the rest are {@link NavPoint}s.
37   * 
38   * @author Jimmy
39   */
40  public class UT2004AStarPathFuture extends PathFuture<ILocated> {
41  
42  	private static final int PATH_TIMEOUT = 10;
43  
44  	private static Object idMutex = new Object();
45  	
46  	private static long lastId = 0;
47  	
48  	private String pathId;
49  	
50  	private EventReactOnce<Path> pathReaction;
51  	
52  	private EventReact<EndMessage> endReaction;
53  
54  	private IVisionWorldView worldView;
55  	
56  	private Logger log;
57  	
58  	private Double startTime;
59  
60  	public UT2004AStarPathFuture(UT2004Bot bot, ILocated pathFrom, ILocated pathTo) {
61  		super(pathFrom, pathTo, bot.getEventBus(), bot.getWorldView());
62  		log = bot.getLogger().getCategory(this.getClass().getSimpleName());
63  		synchronized(idMutex) {
64  			pathId = "UT2004AStarPathFuture_" + (++lastId);
65  		}
66  		pathReaction = new EventReactOnce<Path>(Path.class, bot.getWorldView()){
67  			@Override
68  			protected void react(Path event) {
69  				if (pathId.equals(event.getPathId())) {
70  					eventPath(event);
71  				}
72  			}			
73  		};
74  		endReaction = new EventReact<EndMessage>(EndMessage.class, bot.getWorldView()) {
75  			@Override
76  			protected void react(EndMessage event) {
77  				eventEndMessage(event);
78  			}		
79  		};
80  		log.finer("Requesting path from '" + pathFrom + "' to '" + pathTo + "' under id '" + pathId + "'.");
81  		bot.getAct().act(new GetPath().setLocation(pathTo.getLocation()).setId(pathId));
82  		log.fine("Path requested, listening for the result (timeout " + PATH_TIMEOUT + "s)");		
83  		worldView = bot.getWorldView();
84  	}
85  
86  	@Override
87  	protected boolean cancelComputation(boolean mayInterruptIfRunning) {
88  		pathReaction.disable();
89  		endReaction.disable();
90  		return getStatus() == FutureStatus.FUTURE_IS_BEING_COMPUTED;
91  	}
92  	
93  	protected void eventEndMessage(EndMessage event) {
94  		if (startTime == null) startTime = event.getTime();
95  		if (event.getTime() - startTime > PATH_TIMEOUT) {
96  			pathReaction.disable();
97  			endReaction.disable();
98  			if (getStatus() == FutureStatus.FUTURE_IS_BEING_COMPUTED) {
99  				computationException(new UT2004AStarPathTimeoutException("Path did not came from GB2004 in " + PATH_TIMEOUT + "s.", log, this));
100 			}
101 		}
102 	}
103 
104 	protected void eventPath(Path event) {
105 		endReaction.disable();
106 		List<ILocated> result = new ArrayList<ILocated>(event.getPath().size());
107 		ILocated last = null;
108 		for (int i = 0; i < event.getPath().size(); ++i) {			
109 			UnrealId routeId = event.getPath().get(i).getRouteId();
110 			NavPoint nav = (NavPoint) worldView.get(routeId);
111 			if (nav == null) {
112 				result.add(last = event.getPath().get(i).getLocation());
113 			} else {
114 				result.add(last = nav);
115 			}			
116 		}
117 		
118 		double distance;
119 		if (last != null) {
120 			distance = getPathTo().getLocation().getDistance(last.getLocation());
121 			if (distance < 40) {
122 				// COMPLETE PATH, OK!
123 				setResult(result);
124 				return;
125 			}
126 		} else {
127 			last = worldView.getSingle(Self.class).getLocation();
128 			distance = getPathTo().getLocation().getDistance(last.getLocation());
129 		}
130 		
131 		double distance2D = getPathTo().getLocation().getDistance2D(last.getLocation());
132 		double distanceZ = getPathTo().getLocation().getDistanceZ(last.getLocation());
133 		
134 		// INCOMPLETE PATH !!! ... Path Planner inside UT2004 is SICK! 
135 		
136 		ILocated pathTo = getPathTo();
137 		NavPoint pathToNav = 
138 			pathTo instanceof NavPoint 
139 				? (NavPoint)getPathTo() 
140 				: DistanceUtils.getNearest(worldView.getAll(NavPoint.class).values(), pathTo, 20);
141 		if (pathToNav == null) {
142 			// check whether pathTo is not an Item
143 			Item pathToItem = DistanceUtils.getNearest(worldView.getAll(Item.class).values(), pathTo, 20);
144 			if (pathToItem != null) {
145 				pathToNav = pathToItem.getNavPoint();
146 			}
147 		}
148 		NavPoint lastNav = last instanceof NavPoint ? (NavPoint) last : DistanceUtils.getNearest(worldView.getAll(NavPoint.class).values(), last, 20);
149 		
150 		if (pathToNav != null) {
151 			// WE'RE RUNNING TO SOME NAVPOINT!
152 			if (lastNav != null) {
153 				// AND LAST POINT ON PATH IS NAVPOINT!
154 				NavPointNeighbourLink link = lastNav.getOutgoingEdges().get(pathToNav.getId());
155 				if (link != null) {
156 					// THERE EXIST LINK BETWEEN last AND pathTo !!!
157 					if (FloydWarshallMap.isWalkable(link.getFlags())) {
158 						// LINK IS WALKABLE !!!
159 						result.add(pathTo);
160 						setResult(result);
161 						return;
162 					} else {
163 						// LINK IS NOT WALKABLE
164 						// => just return the path we've got, it is probably because of GETPATH limit (max 16. navpoints returned)
165 						setResult(result);
166 						// MAY BE CALL: computationException(...); ?
167 						return;
168 					}
169 				} else {
170 					// LINK DOES NOT EXIST BETWEEN THESE TWO NAVPOINTS, THERE IS PROBABLY A GOOD REASON WHY NOT...
171 					// => just return the path we've got
172 					setResult(result);
173 					return;
174 				}
175 			}
176 		}
177 		
178 		// EITHER pathTo IS NOT NavPoint, OR last IS NOT A NAVPOINT
179 				
180 		if (distance2D < 100 &&
181 			distanceZ  < 200) {
182 			// SEEMS THAT WE CAN SIDE STEP TO pathTo
183 			result.add(pathTo);
184 			setResult(result);
185 			return;
186 		}
187 		
188 		// => just return the path we've got
189 		// MAY BE CALL: computationException(...); ?
190 		setResult(result);
191 	}
192 
193 }