001         package com.croftsoft.apps.mars.net;
002    
003         import java.io.*;
004         import java.net.*;
005         import java.util.*;
006    
007         import com.croftsoft.core.lang.NullArgumentException;
008         import com.croftsoft.core.lang.lifecycle.Commissionable;
009         import com.croftsoft.core.math.MathConstants;
010         import com.croftsoft.core.role.Server;
011         import com.croftsoft.core.util.loop.FixedDelayLoopGovernor;
012         import com.croftsoft.core.util.loop.Loopable;
013         import com.croftsoft.core.util.loop.Looper;
014    
015         import com.croftsoft.apps.mars.ai.TankOperator;
016         import com.croftsoft.apps.mars.model.World;
017         import com.croftsoft.apps.mars.net.request.FireRequest;
018         import com.croftsoft.apps.mars.net.request.MoveRequest;
019         import com.croftsoft.apps.mars.net.request.Request;
020         import com.croftsoft.apps.mars.net.request.ViewRequest;
021    
022         /*********************************************************************
023         * Mars server.
024         *
025         * @version
026         *   2003-06-13
027         * @since
028         *   2003-05-13
029         * @author
030         *   <a href="https://www.croftsoft.com/">David Wallace Croft</a>
031         *********************************************************************/
032    
033         public final class  MarsServer
034           implements Commissionable, Server
035         //////////////////////////////////////////////////////////////////////
036         //////////////////////////////////////////////////////////////////////
037         {
038    
039         private static final boolean  DEBUG           = true;
040    
041         private static final double   UPDATE_RATE     = 30.0;
042    
043         private static final long     SAMPLE_PERIOD   = 10 * 1000;
044    
045         private static final long     REQUEST_TIMEOUT = 10 * 1000;
046    
047         //
048    
049         private final String  primaryFilename;
050    
051         private final String  fallbackFilename;
052    
053         private final Looper  looper;
054    
055         //
056    
057         private GameInitAccessor  gameInitAccessor;
058    
059         private NetGame           netGame;
060    
061         private long              count;
062    
063         private long              startTime;
064    
065         private long              lastRequestTime;
066    
067         //////////////////////////////////////////////////////////////////////
068         //////////////////////////////////////////////////////////////////////
069    
070         public  MarsServer (
071           String  primaryFilename,
072           String  fallbackFilename )
073         //////////////////////////////////////////////////////////////////////
074         {
075           this.primaryFilename  = primaryFilename;
076    
077           this.fallbackFilename = fallbackFilename;
078    
079           setGameInitAccessor ( GameInit.createDefaultGameInit ( ) );
080    
081           looper = new Looper (
082             new Loopable ( )
083             {
084               public boolean  loop ( )
085               {
086                 return MarsServer.this.loop ( );
087               }
088             },
089             new FixedDelayLoopGovernor ( UPDATE_RATE ),
090             null,
091             ( String ) null,
092             Thread.MIN_PRIORITY,
093             true );
094         }
095    
096         //////////////////////////////////////////////////////////////////////
097         //////////////////////////////////////////////////////////////////////
098    
099         public void  setGameInitAccessor (
100           GameInitAccessor  gameInitAccessor )
101         //////////////////////////////////////////////////////////////////////
102         {
103           NullArgumentException.check (
104             this.gameInitAccessor = gameInitAccessor );
105         }
106    
107         //////////////////////////////////////////////////////////////////////
108         //////////////////////////////////////////////////////////////////////
109    
110         public void  init ( )
111         //////////////////////////////////////////////////////////////////////
112         {
113           if ( primaryFilename != null )
114           {
115             try
116             {
117               netGame = NetGame.load (
118                 gameInitAccessor, primaryFilename, fallbackFilename );
119             }
120             catch ( FileNotFoundException  ex )
121             {
122             }
123             catch ( Exception  ex )
124             {
125               ex.printStackTrace ( );
126             }
127           }
128    
129           if ( netGame == null )
130           {
131             netGame = new NetGame ( gameInitAccessor );
132           }
133    
134           startTime = System.currentTimeMillis ( );
135    
136           lastRequestTime = startTime;
137    
138           looper.init  ( );
139         }
140    
141         public Object  serve ( Object  requestObject )
142         //////////////////////////////////////////////////////////////////////
143         {
144           synchronized ( this )
145           {
146             lastRequestTime = System.currentTimeMillis ( );
147    
148             if ( DEBUG )
149             {
150               ++count;
151             }
152           }
153    
154           if ( !( requestObject instanceof Request ) )
155           {
156             throw new IllegalArgumentException ( );
157           }
158    
159           looper.start ( );
160    
161           Request  request = ( Request ) requestObject;
162    
163           String  playerName = request.getPlayerName ( );
164    
165           if ( playerName == null )
166           {
167             return netGame.getGameData ( );
168           }
169    
170           Player  player = netGame.getPlayer ( playerName );
171    
172           if ( player == null )
173           {
174             return netGame.getGameData ( );
175           }
176    
177           player.setLastRequestTime ( System.currentTimeMillis ( ) );
178    
179           if ( request instanceof ViewRequest )
180           {
181             GameData  gameData = player.getGameData ( );
182    
183             if ( gameData == null )
184             {
185               gameData = netGame.getGameData ( );
186             }
187    
188             return gameData;
189           }
190    
191           TankOperator  tankOperator
192             = player.getSeriTank ( ).getTankOperator ( );
193    
194           if ( request instanceof MoveRequest )
195           {
196             MoveRequest  moveRequest = ( MoveRequest ) request;
197    
198             tankOperator.go ( moveRequest.getDestination ( ) );
199    
200             return null;
201           }
202    
203           if ( request instanceof FireRequest )
204           {
205             tankOperator.fire ( );
206    
207             return null;
208           }
209    
210           throw new IllegalArgumentException ( );
211         }
212    
213         public void  destroy ( )
214         //////////////////////////////////////////////////////////////////////
215         {
216           looper.stop ( );
217    
218           looper.destroy ( );
219    
220           try
221           {
222             synchronized ( this )
223             {
224               netGame.save ( primaryFilename, fallbackFilename );
225             }
226           }
227           catch ( Exception  ex )
228           {
229             ex.printStackTrace ( );
230           }
231         }
232    
233         //////////////////////////////////////////////////////////////////////
234         // private methods
235         //////////////////////////////////////////////////////////////////////
236    
237         private synchronized boolean  loop ( )
238         //////////////////////////////////////////////////////////////////////
239         {
240           netGame.update ( );
241    
242           long  currentTime = System.currentTimeMillis ( );
243    
244           if ( DEBUG )
245           {
246             if ( currentTime >= startTime + SAMPLE_PERIOD )
247             {
248               System.out.println ( "requests per second:  "
249                 + ( MathConstants.MILLISECONDS_PER_SECOND * count )
250                 / ( currentTime - startTime ) );
251    
252               startTime = currentTime;
253    
254               count = 0;
255             }
256           }
257    
258           if ( currentTime >= lastRequestTime + REQUEST_TIMEOUT )
259           {
260             if ( DEBUG )
261             {
262               System.out.println ( "MarsServer game loop pausing..." );
263             }
264    
265             return false;
266           }
267    
268           return true;
269         }
270    
271         //////////////////////////////////////////////////////////////////////
272         //////////////////////////////////////////////////////////////////////
273         }