View Javadoc

1   /*
2   @See License.txt@
3    */
4   
5   package spellcast.game;
6   
7   import java.beans.PropertyChangeEvent;
8   import java.beans.PropertyChangeListener;
9   import java.util.Iterator;
10  
11  import org.apache.log4j.Logger;
12  
13  import spellcast.beings.Gender;
14  import spellcast.beings.IWizard;
15  import spellcast.beings.Wizard;
16  import spellcast.event.ConnectionEvent;
17  import spellcast.event.ConnectionRequestEvent;
18  import spellcast.event.DisconnectionEvent;
19  import spellcast.event.DisconnectionRequestEvent;
20  import spellcast.event.GameUpdateEvent;
21  import spellcast.event.MessageEvent;
22  import spellcast.event.OkEvent;
23  import spellcast.model.Id;
24  import spellcast.questions.Question;
25  
26  /***
27   * The Game Server class is the control logic for the Game.
28   * It inherits from Game which contains the Data Access methods.
29   *
30   * @author Barrie Treloar
31   */
32  public class Game_Server implements PropertyChangeListener, Runnable {
33      private Game theGame;
34      private IPC ipc;
35      private Thread gameThread;
36      private boolean isRunning;
37  
38      /***
39       * When this percentage of wizards have indicated they are ready to start then
40       * the game will commence.
41       */
42      private static final int READY_TO_START_PERCENTAGE = 50;
43  
44      private static final Logger logger = Logger.getLogger("game");
45  
46      public Game_Server(IPC ipc, String gameName) {
47          this.ipc = ipc;
48          theGame = new Game(gameName, null);
49      }
50  
51      public Game getGame() {
52          return theGame;
53      }
54  
55      /***
56       * Unless otherwise specified, all property change events are forwarded to all clients
57       */
58      public void propertyChange(PropertyChangeEvent pce) {
59          Id recipient = Id.EVERYONE;
60  
61          GameUpdateEvent response = new GameUpdateEvent(pce);
62          if (!recipient.equals(Id.EVERYONE)) {
63              ipc.send(recipient, response);
64          }
65          else {
66              ipc.sendToAll(response);
67          }
68      }
69  
70      public void run() {
71          while (isRunning) {
72              processInput();
73              if (!theGame.hasStarted()) {
74                  runPreGame();
75              }
76              else {
77                  if (!theGame.hasFinished()) {
78                      runTurn();
79                  }
80              }
81          }
82      }
83  
84      /***
85       * Process input from ipc handles.
86       * If the handle is not connected, then we only allow <code>ConnectionRequestEvent</code>s.
87       * If the handle is connected then we process events normally.
88       */
89      private void processInput() {
90          IPCRequest request = ipc.receive();
91  
92          if (!request.getHandle().isConnected()) {
93              if (request.getGameEvent() instanceof ConnectionRequestEvent) {
94                  processConnectionRequestEvent(request);
95              }
96          }
97          else {
98              if (request.getGameEvent() instanceof DisconnectionRequestEvent) {
99                  processDisconnectionRequestEvent(request);
100             }
101             else
102                 if (request.getGameEvent() instanceof MessageEvent) {
103                     processMessageEvent(request);
104                 }
105                 else
106                     if (request.getGameEvent() instanceof OkEvent) {
107                         processOkEvent(request);
108                     }
109                     else {
110                         logger.error(
111                             "Unknown Game Event received: " + request.getGameEvent().getClass().getName());
112                     }
113         }
114     }
115 
116     /***
117      * Process ConnectionRequestEvent.
118      * Checks for duplicate IDs (fatal) and duplicate names (adds a uniquifier)
119      * before adding the wizard to the system.
120      * Sends the ConnectionEvent for this wizard as well as sending one to the
121      * connecting host for every wizard already connected.
122      * The connecting wizard also gets a GameSyncEvent.
123      */
124     private void processConnectionRequestEvent(IPCRequest request) {
125         ConnectionRequestEvent event = (ConnectionRequestEvent) request.getGameEvent();
126 
127         Gender gender = Gender.getGender(event.getGender());
128         if (gender == null) {
129             logger.error("Unknown gender specified (" + event.getGender() + ")");
130             return;
131         }
132 
133         String originalWizardName = event.getWizardName().trim();
134         String wizardName = originalWizardName;
135         int uniquifier = 1;
136         boolean nameMatched = false;
137         do {
138             nameMatched = false;
139             Iterator wizardIterator = theGame.getWizards().listIterator();
140             while (wizardIterator.hasNext()) {
141                 Wizard wiz = (Wizard) wizardIterator.next();
142                 if (wiz.getName().equals(wizardName)) {
143                     wizardName = originalWizardName + " (" + uniquifier + ")";
144                     uniquifier++;
145                     nameMatched = true;
146                     break;
147                 }
148             }
149         }
150         while (nameMatched);
151 
152         if (theGame.hasStarted()) {
153             Wizard tmpWiz = new Wizard(Id.createId(), wizardName, gender);
154             ipc.connect(request.getHandle(), tmpWiz.getId());
155             ipc.send(
156                 tmpWiz.getId(),
157                 new MessageEvent("The game has already started.\nYou have been disconnected.\n"));
158             ipc.send(tmpWiz.getId(), new DisconnectionEvent(tmpWiz));
159             ipc.disconnect(request.getHandle());
160         }
161         else {
162             Wizard newWizard = new Wizard(Id.createId(), wizardName, gender);
163 
164             ipc.connect(request.getHandle(), newWizard.getId());
165             ipc.sendToAll(new ConnectionEvent(newWizard));
166             sendMessage2(
167                 newWizard.getId(),
168                 "You have connected to the server.\n",
169                 newWizard.getName() + " has connected to the server.\n");
170             newWizard.addPropertyChangeListener(this);
171             theGame.addWizard(newWizard);
172 
173 //            ipc.send(newWizard.getId(), new GameSyncEvent(new Game(theGame)));
174             newWizard.setActive(true);
175 
176         }
177     }
178 
179     /***
180      * Process DisconnectionRequestEvent.
181      */
182     private void processDisconnectionRequestEvent(IPCRequest request) {
183         DisconnectionRequestEvent event =
184             (DisconnectionRequestEvent) request.getGameEvent();
185         IWizard wiz = theGame.getWizard(request.getId());
186         if (wiz != null) {
187             sendMessage2(
188                 wiz.getId(),
189                 "You have disconnected from the server.\n",
190                 wiz.getName() + " has disconnected from the server.\n");
191             ipc.sendToAll(new DisconnectionEvent(wiz));
192             ipc.disconnect(request.getHandle());
193             theGame.removeWizard(wiz);
194         }
195         else {
196             logger.warn("Could not find wizard to remove.");
197         }
198     }
199 
200     /***
201      * Process MessageEvent.
202      */
203     private void processMessageEvent(IPCRequest request) {
204         MessageEvent event = (MessageEvent) request.getGameEvent();
205         IWizard wiz = theGame.getWizard(request.getId());
206         sendMessage2(
207             wiz.getId(),
208             "You say: " + event.getMessage() + "\n",
209             wiz.getName() + " says: " + event.getMessage() + "\n");
210     }
211 
212     /***
213      * Process OkEvent.
214      */
215     public void processOkEvent(IPCRequest request) {
216         OkEvent event = (OkEvent) request.getGameEvent();
217         IWizard wiz = theGame.getWizard(request.getId());
218         if (wiz.isActive()) {
219             wiz.setLeftHandGesture(event.getLeftHandGesture());
220             wiz.setRightHandGesture(event.getRightHandGesture());
221         }
222     }
223 
224     /***
225      * Start the Game Server.
226      * This creates a new thread and starts the thread running.
227      */
228     public void start() {
229         isRunning = true;
230         gameThread = new Thread(this, "GameThread");
231         gameThread.start();
232     }
233 
234     /***
235      * Stop the Game Server.
236      * This sets <code>isRunning</code> to false which allows the <code>run</code>
237      * method to terminate.  <code>stop</code> will cause the calling thread
238      * to wait until the Game Server Thread has terminated.
239      */
240     public void stop() {
241         isRunning = false;
242         try {
243             gameThread.join();
244         }
245         catch (InterruptedException e) {
246             logger.error("Unexepectedly Interrupted.", e);
247         }
248     }
249 
250     /***
251      * Before the game has started we allow wizards to connect to the game.
252      * The game will start when over 50% of the wizards have ended their move
253      * and have answered "Yes" to query "Are you ready to start?".
254      */
255     private void runPreGame() {
256         if (theGame.getWizards().size() == 0) {
257             return;
258         }
259 
260     }
261 
262     private void beginGame() {
263         theGame.setTurn(new Turn(0, TurnType.NORMAL));
264         theGame.setGameLog(new GameLog());
265         Iterator wizardIterator = theGame.getWizards().iterator();
266         while (wizardIterator.hasNext()) {
267             Wizard wiz = (Wizard) wizardIterator.next();
268             sendMessage2(
269                 wiz.getId(),
270                 "You advance confidently into the arena. The referee casts the formal Dispel Magic and Anti-Spell on you....\n",
271                 wiz.getName()
272                     + " strides defiantly into the arena. The referee casts the formal Dispel Magic and Anti-Spell on "
273                     + wiz.getGender().pro_him()
274                     + "....\n");
275         }
276     }
277 
278     private void runTurn() {
279 
280     }
281 
282     /***
283      * Send a message to everyone.
284      */
285     public void sendMessage(String message) {
286         ipc.sendToAll(new MessageEvent(message));
287     }
288 
289     /***
290      * Send a message to one Id and another message to everyone else.
291      */
292     public void sendMessage2(
293         Id id1,
294         String messageForID1,
295         String messageForEveryoneElse) {
296         sendMessage3(id1, messageForID1, null, null, messageForEveryoneElse);
297     }
298 
299     /***
300      * Send a message to one Id, a message to another Id, and a message to everyone else.
301      */
302     public void sendMessage3(
303         Id id1,
304         String messageForID1,
305         Id id2,
306         String messageForID2,
307         String messageForEveryoneElse) {
308         MessageEvent id1Event = new MessageEvent(messageForID1);
309         MessageEvent id2Event = new MessageEvent(messageForID2);
310         MessageEvent everyoneElseEvent = new MessageEvent(messageForEveryoneElse);
311 
312         Iterator wizardIterator = theGame.getWizards().iterator();
313         while (wizardIterator.hasNext()) {
314             Wizard wiz = (Wizard) wizardIterator.next();
315             if (wiz.getId().equals(id1)) {
316                 ipc.send(wiz.getId(), id1Event);
317             }
318             else
319                 if (wiz.getId().equals(id2)) {
320                     ipc.send(wiz.getId(), id2Event);
321                 }
322                 else {
323                     ipc.send(wiz.getId(), everyoneElseEvent);
324                 }
325         }
326     }
327 
328 }