1 package cz.cuni.amis.pogamut.ut2004.teamcomm.server;
2
3 import java.net.InetSocketAddress;
4 import java.util.HashSet;
5 import java.util.Map;
6 import java.util.Set;
7 import java.util.logging.Level;
8
9 import com.google.inject.Inject;
10
11 import cz.cuni.amis.pogamut.base.communication.command.IAct;
12 import cz.cuni.amis.pogamut.base.communication.connection.impl.socket.SocketConnection;
13 import cz.cuni.amis.pogamut.base.communication.worldview.event.IWorldEventListener;
14 import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectEvent;
15 import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectEventListener;
16 import cz.cuni.amis.pogamut.base.communication.worldview.object.event.WorldObjectUpdatedEvent;
17 import cz.cuni.amis.pogamut.base.component.bus.IComponentBus;
18 import cz.cuni.amis.pogamut.base.utils.logging.IAgentLogger;
19 import cz.cuni.amis.pogamut.unreal.communication.messages.UnrealId;
20 import cz.cuni.amis.pogamut.ut2004.agent.params.UT2004AgentParameters;
21 import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.SendControlMessage;
22 import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.StartPlayers;
23 import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.BeginMessage;
24 import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.EndMessage;
25 import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.PlayerJoinsGame;
26 import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.PlayerLeft;
27 import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.PlayerMessage;
28 import cz.cuni.amis.pogamut.ut2004.communication.worldview.UT2004WorldView;
29 import cz.cuni.amis.pogamut.ut2004.factory.guice.remoteagent.UT2004ServerFactory;
30 import cz.cuni.amis.pogamut.ut2004.server.IUT2004Server;
31 import cz.cuni.amis.pogamut.ut2004.server.impl.UT2004Server;
32 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.server.TCMinaServer;
33 import cz.cuni.amis.pogamut.ut2004.teamcomm.server.protocol.TCControlMessages;
34 import cz.cuni.amis.pogamut.ut2004.teamcomm.server.protocol.messages.TCControlServerAlive;
35 import cz.cuni.amis.pogamut.ut2004.utils.UT2004ServerRunner;
36 import cz.cuni.amis.utils.maps.LazyMap;
37
38 public class UT2004TCServer extends UT2004Server implements IUT2004Server {
39
40 public static final UnrealId SERVER_UNREAL_ID = UnrealId.get("TC_CONTROL_SERVER");
41
42 private static final double ALIVE_MESSAGE_INTERVAL_SECS = 30;
43
44
45
46
47 private IWorldEventListener<BeginMessage> myBeginMessageListener = new IWorldEventListener<BeginMessage>() {
48 public void notify(BeginMessage event) {
49 timeUpdate(event);
50 }
51 };
52
53
54
55
56 private IWorldEventListener<EndMessage> myEndMessageListener = new IWorldEventListener<EndMessage>() {
57 public void notify(EndMessage event) {
58 batchEnd(event);
59 }
60 };
61
62
63
64
65 private IWorldEventListener<PlayerJoinsGame> myPlayerJoinsGameMessageListener = new IWorldEventListener<PlayerJoinsGame>() {
66 public void notify(PlayerJoinsGame event) {
67 playerJoinsGame(event);
68 }
69 };
70
71
72
73
74 private IWorldEventListener<PlayerLeft> myPlayerLeftMessageListener = new IWorldEventListener<PlayerLeft>() {
75 public void notify(PlayerLeft event) {
76 playerLeft(event);
77 }
78 };
79
80
81
82
83 private IWorldObjectEventListener<PlayerMessage, WorldObjectUpdatedEvent<PlayerMessage>> myPlayerListener = new IWorldObjectEventListener<PlayerMessage, WorldObjectUpdatedEvent<PlayerMessage>>() {
84 public void notify(WorldObjectUpdatedEvent<PlayerMessage> event) {
85 playerUpdate(event);
86 }
87 };
88
89 private Object mutex = new Object();
90
91 private TCControlMessages messages = new TCControlMessages();
92
93 private TCMinaServer minaServer;
94
95 private long utSimTime = -1;
96
97 private double utTimeLast = -1;
98
99 private double utTimeCurrent = -1;
100
101 private double utTimeDelta = -1;
102
103 private Map<UnrealId, BotTCRecord<PlayerMessage>> records = new LazyMap<UnrealId, BotTCRecord<PlayerMessage>>() {
104
105 @Override
106 protected BotTCRecord<PlayerMessage> create(UnrealId key) {
107 return new BotTCRecord<PlayerMessage>(key);
108 }
109
110 };
111
112 private Set<UnrealId> leftPlayers = new HashSet<UnrealId>();
113
114 @Inject
115 public UT2004TCServer(UT2004AgentParameters params, IAgentLogger agentLogger, IComponentBus bus, SocketConnection connection, UT2004WorldView worldView, IAct act) {
116 super(params, agentLogger, bus, connection, worldView, act);
117
118 minaServer = new TCMinaServer(this, new InetSocketAddress(getParams().getBindHost(), getParams().getBindPort()), getLogger().getCategory("TCMinaServer"));
119
120 getWorldView().addEventListener(BeginMessage.class, myBeginMessageListener);
121 getWorldView().addEventListener(EndMessage.class, myEndMessageListener);
122 getWorldView().addEventListener(PlayerJoinsGame.class, myPlayerJoinsGameMessageListener);
123 getWorldView().addEventListener(PlayerLeft.class, myPlayerLeftMessageListener);
124 getWorldView().addObjectListener(PlayerMessage.class, WorldObjectUpdatedEvent.class, myPlayerListener);
125 }
126
127 @Override
128 public UT2004TCServerParams getParams() {
129 UT2004AgentParameters params = super.getParams();
130 if (!(params instanceof UT2004TCServerParams)) throw new RuntimeException("Invalid parameters passed, expecting UT2004TCServerParams, got " + params);
131 return (UT2004TCServerParams) params;
132 }
133
134 @Override
135 protected void init() {
136 super.init();
137
138
139
140
141 log.setLevel(Level.INFO);
142
143 synchronized(mutex) {
144 getAct().act(new StartPlayers(true, true, false));
145 }
146 }
147
148 @Override
149 protected void stopAgent() {
150 synchronized(mutex) {
151 if (minaServer != null) {
152 minaServer.stop();
153 minaServer = null;
154 }
155 }
156 super.stopAgent();
157 }
158
159
160
161
162
163 public long getSimTime() {
164 return utSimTime;
165 }
166
167 public PlayerMessage getPlayer(UnrealId source) {
168
169 if (!records.containsKey(source)) return null;
170
171 BotTCRecord<PlayerMessage> record = records.get(source);
172
173 if (record == null) return null;
174
175 return record.getPlayer();
176 }
177
178
179
180
181
182
183 private void deleteRecord(UnrealId id) {
184 BotTCRecord<PlayerMessage> record = records.remove(id);
185
186 if (record != null) {
187 minaServer.botLeft(id);
188 }
189
190 leftPlayers.add(id);
191 }
192
193 private void ensureRecord(PlayerMessage object) {
194 records.put(object.getId(), new BotTCRecord<PlayerMessage>(object.getId(), object));
195 }
196
197
198
199
200
201 protected void playerUpdate(IWorldObjectEvent<PlayerMessage> event) {
202 if (!records.containsKey(event.getId())) {
203 if (leftPlayers.contains(event.getId())) {
204
205 leftPlayers.remove(event.getId());
206 return;
207 }
208 log.info(event.getObject().getId().getStringId() + ": First PlayerMessage received.");
209 sendAlive();
210 }
211 ensureRecord(event.getObject());
212 }
213
214 protected void playerLeft(PlayerLeft event) {
215 log.info(event.getId().getStringId() + ": Player has LEFT the game.");
216 deleteRecord(event.getId());
217 minaServer.botLeft(event.getId());
218 }
219
220 protected void playerJoinsGame(PlayerJoinsGame event) {
221 log.info(event.getId().getStringId() + ": Player has JOINED the game.");
222 }
223
224 protected void batchEnd(EndMessage event) {
225 }
226
227 protected void timeUpdate(BeginMessage event) {
228 utSimTime = event.getSimTime();
229
230 utTimeLast = utTimeCurrent;
231 utTimeCurrent = event.getTime();
232 utTimeDelta = utTimeCurrent - utTimeLast;
233
234 if (utSimTime > 0 && utTimeCurrent > 0 && (lastAlive < 0 || utTimeCurrent - lastAlive > ALIVE_MESSAGE_INTERVAL_SECS)) {
235
236 if (!minaServer.getRunning().getFlag()) {
237 log.info("Starting MINA SERVER!");
238 minaServer.start();
239 }
240
241 sendAlive();
242 }
243 }
244
245
246
247
248
249 private double lastAlive = -1;
250
251 private void sendAlive() {
252 log.fine("Sending ALIVE message");
253
254 lastAlive = utTimeCurrent;
255
256 TCControlServerAlive message = new TCControlServerAlive();
257
258 message.setHost(getParams().getBindHost());
259 message.setPort(getParams().getBindPort());
260
261 SendControlMessage command = messages.write(message);
262 command.setSendAll(true);
263 getAct().act(command);
264 }
265
266 public static UT2004TCServer startTCServer() {
267 return startTCServer("localhost", 3010);
268 }
269
270 public static UT2004TCServer startTCServer(String bindHost, int bindPort) {
271
272 UT2004TCServerModule module = new UT2004TCServerModule();
273 UT2004ServerFactory factory = new UT2004ServerFactory(module);
274 UT2004ServerRunner<UT2004Server, UT2004TCServerParams> serverRunner = new UT2004ServerRunner(factory);
275
276 UT2004TCServerParams params = new UT2004TCServerParams();
277 params.setBindHost("localhost");
278 params.setBindPort(3010);
279
280 return (UT2004TCServer) serverRunner.setLogLevel(Level.INFO).setMain(false).startAgents(params).get(0);
281 }
282
283 public static void main(String[] args) {
284 startTCServer();
285 }
286
287 }