Source code: marauroa/game/GameServerManager.java
1 /* $Id: GameServerManager.java,v 1.23 2003/12/12 16:33:30 arianne_rpg Exp $ */
2 /***************************************************************************
3 * (C) Copyright 2003 - Marauroa *
4 ***************************************************************************
5 ***************************************************************************
6 * *
7 * This program is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation; either version 2 of the License, or *
10 * (at your option) any later version. *
11 * *
12 ***************************************************************************/
13 package marauroa.game;
14
15 import java.util.*;
16 import java.io.*;
17
18 import marauroa.net.*;
19 import marauroa.*;
20
21 /** The GameServerManager is a active entity of the marauroa.game package,
22 * it is in charge of processing all the messages and modify PlayerEntry Container accordingly. */
23 public class GameServerManager extends Thread
24 {
25 private NetworkServerManager netMan;
26 private RPServerManager rpMan;
27 private PlayerEntryContainer playerContainer;
28
29 /** The thread will be running while keepRunning is true */
30 private boolean keepRunning;
31 /** isFinished is true when the thread has really exited. */
32 private boolean isfinished;
33
34 /** Constructor that initialize also the RPManager
35 * @param netMan a NetworkServerManager instance. */
36 public GameServerManager(NetworkServerManager netMan)
37 {
38 super("GameServerManager");
39 marauroad.trace("GameServerManager",">");
40
41 keepRunning=true;
42 this.netMan=netMan;
43 playerContainer=PlayerEntryContainer.getContainer();
44 rpMan=new RPServerManager(netMan);
45
46 start();
47
48 marauroad.trace("GameServerManager","<");
49 }
50
51 /** Constructor that initialize also the RPManager
52 * @param netMan a NetworkServerManager instance.
53 * @param rpMan a RPManager instance */
54 public GameServerManager(NetworkServerManager netMan, RPServerManager rpMan)
55 {
56 super("GameServerManager");
57 marauroad.trace("GameServerManager",">");
58
59 keepRunning=true;
60 this.netMan=netMan;
61 this.rpMan=rpMan;
62 playerContainer=PlayerEntryContainer.getContainer();
63
64 start();
65
66 marauroad.trace("GameServerManager","<");
67 }
68
69 public void finish()
70 {
71 marauroad.trace("GameServerManager::finish",">");
72
73 rpMan.finish();
74 keepRunning=false;
75
76 while(isfinished==false)
77 {
78 try
79 {
80 Thread.sleep(1000);
81 }
82 catch(java.lang.InterruptedException e)
83 {
84 }
85 }
86
87 marauroad.trace("GameServerManager::finish","<");
88 }
89
90 public void run()
91 {
92 marauroad.trace("GameServerManager::run",">");
93
94 try
95 {
96 while(keepRunning)
97 {
98 Message msg=netMan.getMessage(TimeoutConf.GAMESERVER_MESSAGE_GET_TIMEOUT);
99
100 if(msg!=null)
101 {
102 playerContainer.getLock().requestWriteLock();
103
104 switch(msg.getType())
105 {
106 case Message.TYPE_C2S_LOGIN:
107 marauroad.trace("GameServerManager::run","D","Processing C2S Login Message");
108 processLoginEvent((MessageC2SLogin)msg);
109 break;
110 case Message.TYPE_C2S_CHOOSECHARACTER:
111 marauroad.trace("GameServerManager::run","D","Processing C2S Choose Character Message");
112 processChooseCharacterEvent((MessageC2SChooseCharacter)msg);
113 break;
114 case Message.TYPE_C2S_LOGOUT:
115 marauroad.trace("GameServerManager::run","D","Processing C2S Logout Message");
116 processLogoutEvent((MessageC2SLogout)msg);
117 break;
118 case Message.TYPE_C2S_ACTION:
119 marauroad.trace("GameServerManager::run","D","Processing C2S Action Message");
120 processActionEvent((MessageC2SAction)msg);
121 break;
122 case Message.TYPE_C2S_PERCEPTION_ACK:
123 marauroad.trace("GameServerManager::run","D","Processing C2S Perception ACK Message");
124 processPerceptionACKEvent((MessageC2SPerceptionACK)msg);
125 break;
126 default:
127 marauroad.trace("GameServerManager::run","W","Unknown Message["+msg.getType()+"]");
128 break;
129 }
130
131 playerContainer.getLock().releaseLock();
132 }
133 }
134 }
135 finally
136 {
137 isfinished=true;
138 marauroad.trace("GameServerManager::run","<");
139 }
140 }
141
142 private static class ServerInfo
143 {
144 static private String typeGame;
145 static private String name;
146 static private String version;
147 static private String contact;
148
149 static
150 {
151 marauroad.trace("GameServerManager::ServerInfo::(static)",">");
152
153 try
154 {
155 Configuration conf=Configuration.getConfiguration();
156
157 typeGame=conf.get("server_typeGame");
158 name=conf.get("server_name");
159 version=conf.get("server_version");
160 contact=conf.get("server_contact");
161 }
162 catch(Exception e)
163 {
164 marauroad.trace("GameServerManager::ServerInfo::(static)","X",e.getMessage());
165 marauroad.trace("GameServerManager::ServerInfo::(static)","!","ABORT: Unable to load Server info");
166 System.exit(-1);
167 }
168 finally
169 {
170 marauroad.trace("GameServerManager::ServerInfo::(static)","<");
171 }
172 }
173
174 public static String[] get()
175 {
176 String[] result=new String[4];
177
178 result[0]=typeGame;
179 result[1]=name;
180 result[2]=version;
181 result[3]=contact;
182
183 return result;
184 }
185 }
186
187 private void processLoginEvent(MessageC2SLogin msg)
188 {
189 marauroad.trace("GameServerManager::processLoginEvent",">");
190 try
191 {
192 if(playerContainer.hasRuntimePlayer(msg.getClientID()) || playerContainer.hasPlayer(msg.getUsername()))
193 {
194 /* Error: Player is already logged. */
195 marauroad.trace("GameServerManager::processLoginEvent","W","Client("+msg.getAddress().toString()+") trying to login twice");
196
197 /* Notify player of the event. */
198 MessageS2CLoginNACK msgLoginNACK=new MessageS2CLoginNACK(msg.getAddress(),MessageS2CLoginNACK.UNKNOWN_REASON);
199 netMan.addMessage(msgLoginNACK);
200 return;
201 }
202
203 if(playerContainer.size()==GameConst.MAX_NUMBER_PLAYERS)
204 {
205 /* Error: Too many clients logged on the server. */
206 marauroad.trace("GameServerManager::processLoginEvent","W","Server is full, Client("+msg.getAddress().toString()+") can't login");
207
208 /* Notify player of the event. */
209 MessageS2CLoginNACK msgLoginNACK=new MessageS2CLoginNACK(msg.getAddress(),MessageS2CLoginNACK.SERVER_IS_FULL);
210 netMan.addMessage(msgLoginNACK);
211 return;
212 }
213
214 if(playerContainer.verifyAccount(msg.getUsername(),msg.getPassword()))
215 {
216 marauroad.trace("GameServerManager::processLoginEvent","D","Correct username/password");
217
218 /* Correct: The login is correct */
219 int clientid=playerContainer.addRuntimePlayer(msg.getUsername(),msg.getAddress());
220 playerContainer.addLoginEvent(msg.getUsername(),msg.getAddress(),true);
221
222 /* Send player the Login ACK message */
223 MessageS2CLoginACK msgLoginACK=new MessageS2CLoginACK(msg.getAddress());
224 msgLoginACK.setClientID(clientid);
225 netMan.addMessage(msgLoginACK);
226
227 /* Send player the ServerInfo */
228 MessageS2CServerInfo msgServerInfo=new MessageS2CServerInfo(msg.getAddress(),ServerInfo.get());
229 msgServerInfo.setClientID(clientid);
230 netMan.addMessage(msgServerInfo);
231
232 /* Build player character list and send it to client */
233 String[] characters=playerContainer.getCharacterList(clientid);
234 MessageS2CCharacterList msgCharacters=new MessageS2CCharacterList(msg.getAddress(),characters);
235 msgCharacters.setClientID(clientid);
236 netMan.addMessage(msgCharacters);
237
238 playerContainer.changeRuntimeState(clientid,PlayerEntryContainer.STATE_LOGIN_COMPLETE);
239 }
240 else
241 {
242 marauroad.trace("GameServerManager::processLoginEvent","W","Incorrect username/password");
243 if(playerContainer.hasPlayer(msg.getUsername()))
244 {
245 playerContainer.addLoginEvent(msg.getUsername(),msg.getAddress(),false);
246 }
247 else
248 {
249 marauroad.trace("GameServerManager::processLoginEvent","W","Incorrect username: Can't add login event.");
250 }
251
252 /* Send player the Login NACK message */
253 MessageS2CLoginNACK msgLoginNACK=new MessageS2CLoginNACK(msg.getAddress(),MessageS2CLoginNACK.USERNAME_WRONG);
254 netMan.addMessage(msgLoginNACK);
255 }
256 }
257 catch(Exception e)
258 {
259 marauroad.trace("GameServerManager::processLoginEvent","X",e.getMessage());
260 }
261 finally
262 {
263 marauroad.trace("GameServerManager::processLoginEvent","<");
264 }
265 }
266
267 private void processChooseCharacterEvent(MessageC2SChooseCharacter msg)
268 {
269 marauroad.trace("GameServerManager::processChooseCharacterEvent",">");
270
271 try
272 {
273 int clientid=msg.getClientID();
274
275 if(!playerContainer.hasRuntimePlayer(clientid))
276 {
277 /* Error: Player didn't login. */
278 marauroad.trace("GameServerManager::processChooseCharacterEvent","W","Client("+msg.getAddress().toString()+") has not login yet");
279 return;
280 }
281
282 if(playerContainer.getRuntimeState(clientid)!=playerContainer.STATE_LOGIN_COMPLETE)
283 {
284 /* Error: Player has not completed login yet, or he/she has logout already. */
285 marauroad.trace("GameServerManager::processChooseCharacterEvent","W","Client("+msg.getAddress().toString()+") has not login yet");
286 return;
287 }
288
289 if(!playerContainer.verifyRuntimePlayer(clientid,msg.getAddress()))
290 {
291 /* Error: Player has not correct IP<->clientid relation */
292 marauroad.trace("GameServerManager::processChooseCharacterEvent","E","Client("+msg.getAddress().toString()+") has not correct IP<->clientid relation");
293 return;
294 }
295
296 if(playerContainer.hasCharacter(clientid,msg.getCharacter()))
297 {
298 marauroad.trace("GameServerManager::processChooseCharacterEvent","D","Client("+msg.getAddress().toString()+") has character("+msg.getCharacter()+")");
299
300 /* We set the character in the runtime info */
301 playerContainer.setChoosenCharacter(clientid,msg.getCharacter());
302
303 /* We restore back the character to the world */
304 RPObject object=playerContainer.getRPObject(clientid,msg.getCharacter());
305 rpMan.addRPObject(object);
306
307 playerContainer.changeRuntimeState(clientid,playerContainer.STATE_GAME_BEGIN);
308
309 /* Correct: Character exist */
310 MessageS2CChooseCharacterACK msgChooseCharacterACK=new MessageS2CChooseCharacterACK(msg.getAddress(),new RPObject.ID(object));
311 msgChooseCharacterACK.setClientID(clientid);
312 netMan.addMessage(msgChooseCharacterACK);
313 }
314 else
315 {
316 marauroad.trace("GameServerManager::processChooseCharacterEvent","W","Client("+msg.getAddress().toString()+") hasn't character("+msg.getCharacter()+")");
317
318 playerContainer.changeRuntimeState(clientid,playerContainer.STATE_LOGIN_COMPLETE);
319
320 /* Error: There is no such character */
321 MessageS2CChooseCharacterNACK msgChooseCharacterNACK=new MessageS2CChooseCharacterNACK(msg.getAddress());
322 msgChooseCharacterNACK.setClientID(clientid);
323 netMan.addMessage(msgChooseCharacterNACK);
324 }
325 }
326 catch(Exception e)
327 {
328 marauroad.trace("GameServerManager::processChooseCharacterEvent","X",e.getMessage());
329 }
330 finally
331 {
332 marauroad.trace("GameServerManager::processChooseCharacterEvent","<");
333 }
334 }
335
336 private void processLogoutEvent(MessageC2SLogout msg)
337 {
338 marauroad.trace("GameServerManager::processLogoutEvent",">");
339
340 try
341 {
342 int clientid=msg.getClientID();
343
344 if(!playerContainer.hasRuntimePlayer(clientid))
345 {
346 /* Error: Player didn't login. */
347 marauroad.trace("GameServerManager::processLogoutEvent","W","Client("+msg.getAddress().toString()+") has not login yet");
348 return;
349 }
350
351 if(!playerContainer.verifyRuntimePlayer(clientid,msg.getAddress()))
352 {
353 /* Error: Player has not correct IP<->clientid relation */
354 marauroad.trace("GameServerManager::processLogoutEvent","E","Client("+msg.getAddress().toString()+") has not correct IP<->clientid relation");
355 return;
356 }
357
358 if(playerContainer.getRuntimeState(clientid)==PlayerEntryContainer.STATE_GAME_BEGIN)
359 {
360 RPObject.ID id=playerContainer.getRPObjectID(clientid);
361 RPObject object=rpMan.getRPObject(id);
362 rpMan.removeRPObject(id);
363
364 /* NOTE: Set the Object so that it is stored in Database */
365 playerContainer.setRPObject(clientid,object);
366 }
367 else
368 {
369 marauroad.trace("GameServerManager::processLogoutEvent","D","Player trying to logout without choosing character");
370 }
371
372 playerContainer.removeRuntimePlayer(clientid);
373
374 /* Send Logout ACK message */
375 MessageS2CLogoutACK msgLogout=new MessageS2CLogoutACK(msg.getAddress());
376 msgLogout.setClientID(clientid);
377 netMan.addMessage(msgLogout);
378 }
379 catch(Exception e)
380 {
381 marauroad.trace("GameServerManager::processLogoutEvent","X",e.getMessage());
382 }
383 finally
384 {
385 marauroad.trace("GameServerManager::processLogoutEvent","<");
386 }
387 }
388
389 static int lastActionIdGenerated=0;
390
391 private void processActionEvent(MessageC2SAction msg)
392 {
393 marauroad.trace("GameServerManager::processActionEvent",">");
394
395 try
396 {
397 int clientid=msg.getClientID();
398
399 if(!playerContainer.hasRuntimePlayer(clientid))
400 {
401 /* Error: Player didn't login. */
402 marauroad.trace("GameServerManager::processActionEvent","W","Client("+msg.getAddress().toString()+") has not login yet");
403 return;
404 }
405
406 if(playerContainer.getRuntimeState(clientid)!=playerContainer.STATE_GAME_BEGIN)
407 {
408 /* Error: Player has not choose a character yey. */
409 marauroad.trace("GameServerManager::processActionEvent","W","Client("+msg.getAddress().toString()+") has not chose a character yet");
410 return;
411 }
412
413 if(!playerContainer.verifyRuntimePlayer(clientid,msg.getAddress()))
414 {
415 /* Error: Player has not correct IP<->clientid relation */
416 marauroad.trace("GameServerManager::processActionEvent","E","Client("+msg.getAddress().toString()+") has not correct IP<->clientid relation");
417 return;
418 }
419
420 /* Send the action to RP Manager */
421 RPAction action=msg.getRPAction();
422
423 /* Enforce source_id and action_id*/
424 RPObject.ID id=playerContainer.getRPObjectID(clientid);
425 action.put("source_id",id.getObjectID());
426 action.put("action_id",lastActionIdGenerated);
427
428 rpMan.addRPAction(action);
429
430 /* Notify client that we recieved the action */
431 MessageS2CActionACK msgAction=new MessageS2CActionACK(msg.getAddress(),lastActionIdGenerated);
432 msgAction.setClientID(clientid);
433 netMan.addMessage(msgAction);
434
435 ++lastActionIdGenerated;
436 }
437 catch(Exception e)
438 {
439 marauroad.trace("GameServerManager::processActionEvent","X",e.getMessage());
440 }
441 finally
442 {
443 marauroad.trace("GameServerManager::processActionEvent","<");
444 }
445 }
446
447 private void processPerceptionACKEvent(MessageC2SPerceptionACK msg)
448 {
449 marauroad.trace("GameServerManager::processPerceptionACKEvent",">");
450
451 try
452 {
453 int clientid=msg.getClientID();
454
455 if(!playerContainer.hasRuntimePlayer(clientid))
456 {
457 /* Error: Player didn't login. */
458 marauroad.trace("GameServerManager::processPerceptionACKEvent","W","Client("+msg.getAddress().toString()+") has not login yet");
459 return;
460 }
461
462 if(playerContainer.getRuntimeState(clientid)!=playerContainer.STATE_GAME_BEGIN)
463 {
464 /* Error: Player has not choose a character yey. */
465 marauroad.trace("GameServerManager::processPerceptionACKEvent","W","Client("+msg.getAddress().toString()+") has not chose a character yet");
466 return;
467 }
468
469 if(!playerContainer.verifyRuntimePlayer(clientid,msg.getAddress()))
470 {
471 /* Error: Player has not correct IP<->clientid relation */
472 marauroad.trace("GameServerManager::processPerceptionACKEvent","E","Client("+msg.getAddress().toString()+") has not correct IP<->clientid relation");
473 return;
474 }
475
476 playerContainer.updateTimestamp(clientid);
477 }
478 catch(Exception e)
479 {
480 marauroad.trace("GameServerManager::processPerceptionACKEvent","X",e.getMessage());
481 }
482 finally
483 {
484 marauroad.trace("GameServerManager::processPerceptionACKEvent","<");
485 }
486 }
487 }