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 }