View Javadoc

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