View Javadoc

1   package cz.cuni.amis.pogamut.ut2004.observer.impl;
2   
3   import java.util.concurrent.TimeUnit;
4   import java.util.logging.Level;
5   
6   import com.google.inject.Inject;
7   
8   import cz.cuni.amis.pogamut.base.agent.IAgentId;
9   import cz.cuni.amis.pogamut.base.agent.impl.AbstractGhostAgent;
10  import cz.cuni.amis.pogamut.base.agent.state.impl.AgentStateStarting;
11  import cz.cuni.amis.pogamut.base.communication.command.IAct;
12  import cz.cuni.amis.pogamut.base.communication.worldview.event.IWorldEventListener;
13  import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectEvent;
14  import cz.cuni.amis.pogamut.base.communication.worldview.react.ObjectEventReactOnce;
15  import cz.cuni.amis.pogamut.base.component.bus.IComponentBus;
16  import cz.cuni.amis.pogamut.base.component.bus.event.BusAwareCountDownLatch;
17  import cz.cuni.amis.pogamut.base.component.exception.ComponentCantStartException;
18  import cz.cuni.amis.pogamut.base.utils.guice.AgentScoped;
19  import cz.cuni.amis.pogamut.base.utils.logging.IAgentLogger;
20  import cz.cuni.amis.pogamut.base3d.worldview.IVisionWorldView;
21  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.PasswordReply;
22  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.Ready;
23  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.GameInfo;
24  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Password;
25  import cz.cuni.amis.pogamut.ut2004.communication.translator.shared.events.ReadyCommandRequest;
26  import cz.cuni.amis.pogamut.ut2004.observer.IUT2004Observer;
27  import cz.cuni.amis.utils.exception.PogamutInterruptedException;
28  
29  /**
30   * Abstract class - ancestor of all UT2004 observer controls.
31   * <p>
32   * <p>
33   * It counts with GameBots2004 protocol therefore taking care of:
34   * <ol>
35   * <li>ReadyCommandRequest - sending automatically ready(), override
36   * readyCommandRequested() if you're not comfortable with this</li>
37   * <li>Password - when password is requested it calls method
38   * createPasswordReply()</li>
39   * </ol>
40   * <p>
41   * <p>
42   * You may use setPassword() method to specify the password before starting the
43   * agent.
44   * 
45   * @author Jimmy
46   */
47  @AgentScoped
48  public abstract class AbstractUT2004Observer<WORLD_VIEW extends IVisionWorldView, ACT extends IAct> extends AbstractGhostAgent<WORLD_VIEW, ACT> implements IUT2004Observer {
49  	
50  	/**
51  	 * If specified - used for the construction of the PasswordReply in
52  	 * createPasswordReply() method.
53  	 */
54  	private String desiredPassword = null;
55  	
56  	private ObjectEventReactOnce<GameInfo, IWorldObjectEvent<GameInfo>> latchRaiseReact;
57  	
58  	private BusAwareCountDownLatch latch;
59  	
60  	@Inject
61  	public AbstractUT2004Observer(IAgentId agentId, IComponentBus bus, IAgentLogger agentLogger, WORLD_VIEW worldView, ACT act) {
62  		super(agentId, bus, agentLogger, worldView, act);
63  
64  		getWorldView().addEventListener(ReadyCommandRequest.class, readyCommandRequestListener);
65  		getWorldView().addEventListener(Password.class, passwordRequestedListener);
66  		
67  		latch = new BusAwareCountDownLatch(1, bus, getWorldView());
68  		
69  		latchRaiseReact = new ObjectEventReactOnce<GameInfo, IWorldObjectEvent<GameInfo>>(
70  				GameInfo.class, getWorldView()
71  		) {
72  			@Override
73  			protected void react(IWorldObjectEvent<GameInfo> event) {
74  				latch.countDown();
75  			}
76  		};
77  
78  		
79  	}
80  
81  	/**
82  	 * Specify the password that should be used if required by the world.
83  	 * 
84  	 * @param password
85  	 */
86  	public void setPassword(String password) {
87  		this.desiredPassword = password;
88  	}
89  
90  	// --------------
91  	// -=-=-=-=-=-=-=
92  	// READY LISTENER
93  	// -=-=-=-=-=-=-=
94  	// --------------
95  	/**
96  	 * This method is called whenever HelloBot message is parsed - the
97  	 * GameBots2004 is awaiting the bot to reply with Ready command to begin the
98  	 * handshake.
99  	 */
100 	protected void readyCommandRequested() {
101 		getAct().act(new Ready());
102 	}
103 
104 	/**
105 	 * Listener that is hooked to WorldView awaiting event ReadyCommandRequest
106 	 * calling setupWorldViewListeners() and then readyCommandRequested() method
107 	 * upon receiving the event.
108 	 */
109 	private IWorldEventListener<ReadyCommandRequest> readyCommandRequestListener = new IWorldEventListener<ReadyCommandRequest>() {
110 
111 		@Override
112 		public void notify(ReadyCommandRequest event) {
113 			setState(new AgentStateStarting("GameBots2004 greeted us, sending READY."));
114 			readyCommandRequested();
115 			setState(new AgentStateStarting("READY sent."));
116 		}
117 	};
118 	// -----------------
119 	// -=-=-=-=-=-=-=-=-
120 	// PASSWORD LISTENER
121 	// -=-=-=-=-=-=-=-=-
122 	// -----------------
123 	/**
124 	 * Instance of the password reply command that was sent upon receivieng
125 	 * request for the password (the world is locked).
126 	 * <p>
127 	 * <p>
128 	 * If null the password was not required by the time the bot connected to
129 	 * the world.
130 	 */
131 	private PasswordReply passwordReply = null;
132 
133 	/**
134 	 * Instance of the password reply command that was sent upon receivieng
135 	 * request for the password (the world is locked).
136 	 * <p>
137 	 * <p>
138 	 * If null the password was not required by the time the bot connected to
139 	 * the world.
140 	 * 
141 	 * @return
142 	 */
143 	public PasswordReply getPasswordReply() {
144 		return passwordReply;
145 	}
146 
147 	/**
148 	 * This method is called whenever the Password event is caught telling us
149 	 * the world is locked and is requiring a password.
150 	 * <p>
151 	 * <p>
152 	 * May return null - in that case an empty password is sent to the server
153 	 * (which will probably result in closing the connection and termination of
154 	 * the agent).
155 	 * <p>
156 	 * <p>
157 	 * This message is then saved to private field passwordReply and is
158 	 * accessible via getPasswordReply() method if required to be probed during
159 	 * the bot's runtime.
160 	 * <p>
161 	 * <p>
162 	 * Note that if setPassword() method is called before this one it will use
163 	 * provided password via that method.
164 	 */
165 	protected PasswordReply createPasswordReply() {
166 		return desiredPassword != null ? new PasswordReply(desiredPassword)
167 				: null;
168 	}
169 
170 	/**
171 	 * Listener that is hooked to WorldView awaiting event InitCommandRequest
172 	 * calling initCommandRequested() method upon receiving the event.
173 	 */
174 	private IWorldEventListener<Password> passwordRequestedListener = new IWorldEventListener<Password>() {
175 
176 		@Override
177 		public void notify(Password event) {
178 			setState(new AgentStateStarting("Password requested by the world."));
179 			passwordReply = createPasswordReply();
180 			if (passwordReply == null) {
181 				passwordReply = new PasswordReply("");
182 			}
183 			if (log.isLoggable(Level.INFO)) log.info("Password required for the world, replying with '"
184 					+ passwordReply.getPassword() + "'.");
185 			getAct().act(passwordReply);
186 		}
187 	};
188 
189 	@Override
190 	public WORLD_VIEW getWorldView() {
191 		return super.getWorldView();
192 	}
193 	
194 	/////
195 	//
196 	// LIFECYCLE METHODS (starting/stopping observer)
197 	//
198 	/////
199 	
200 	/**
201 	 * Called during stop/kill/reset events.
202 	 */
203 	protected void reset() {
204 		latch.countDown();
205 		latch = new BusAwareCountDownLatch(1, getEventBus(), getWorldView());
206 		latchRaiseReact.enable();
207 	}
208 	
209 	@Override
210 	protected void startAgent() {
211 		super.startAgent();
212 		if (!latch.await(60000, TimeUnit.MILLISECONDS)) {
213 			throw new ComponentCantStartException("GameInfo message was not received in 60 secs.", this);
214 		}
215 		try {
216 			Thread.sleep(1000);
217 		} catch (InterruptedException e) {
218 			throw new PogamutInterruptedException(e, this);
219 		}
220 	}
221 	
222 	@Override
223 	protected void startPausedAgent() {
224 		super.startPausedAgent();
225 		if (!latch.await(60000, TimeUnit.MILLISECONDS)) {
226 			throw new ComponentCantStartException("GameInfo message was not received in 60 secs.", this);
227 		}
228 		try {
229 			Thread.sleep(1000);
230 		} catch (InterruptedException e) {
231 			throw new PogamutInterruptedException(e, this);
232 		}
233 	}
234 	
235 	@Override
236 	protected void resetAgent() {
237 		super.resetAgent();
238 		reset();
239 	}
240 	
241 	@Override
242 	protected void stopAgent() {
243 		super.stopAgent();
244 		reset();
245 	}
246 	
247 	@Override
248 	protected void killAgent() {
249 		super.killAgent();
250 		reset();
251 	}
252 }