package edu.ups.gamedev.net;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.UnknownHostException;
import java.util.LinkedList;
import java.util.List;
import com.captiveimagination.jgn.ConnectionException;
import com.captiveimagination.jgn.synchronization.SyncObjectManager;
import com.captiveimagination.jgn.synchronization.SynchronizationManager;
import com.captiveimagination.jgn.synchronization.message.SynchronizeCreateMessage;
import com.captiveimagination.jgn.synchronization.message.SynchronizeRemoveMessage;
import com.captiveimagination.jmenet.JMEGraphicalController;
import com.jme.renderer.Renderer;
import com.jme.scene.Spatial;
import com.jme.scene.shape.Teapot;
import edu.ups.gamedev.game.TankGame;
import edu.ups.gamedev.net.message.*;
import edu.ups.gamedev.player.PlayerSphere;
import edu.ups.gamedev.player.Tank;
import edu.ups.gamedev.weapons.PhysicsFireable;
/**
* Common networking code used by both the <code>Client</code> and <code>
* Server</code> classes.
*
* @author Walker Lindley
* @version $Revision$, $Date$
*
* @see edu.ups.gamedev.net.Client
* @see edu.ups.gamedev.net.Server
*/
public class NetworkCommon implements SyncObjectManager {
SynchronizationManager syncManager;
InetSocketAddress serverReliable, serverFast;
JMEGraphicalController controller;
List<Thread> childThreads;
/**
* Constructor that takes a root node and uses default values for the server
* addresses.
*
* @param root root node of the scene graph
* @throws IOException
*/
public NetworkCommon() throws UnknownHostException, IOException {
this(null);
}
/**
* Constructor that takes a root node and an <code>InetAddress</code> for
* the server.
*
* @param root root <code>Node</code> of the scene graph
* @param address <code>InetAddress</code> of the server
*/
public NetworkCommon(InetAddress address) {
serverReliable = new InetSocketAddress(address, NetworkConstants.SERVER_RELIABLE_PORT);
serverFast = new InetSocketAddress(address, NetworkConstants.SERVER_FAST_PORT);
childThreads = new LinkedList<Thread>();
}
/**
* Registers a part of the scene-graph to be automatically synchronized across
* the network. This form of the method uses {@link edu.ups.gamedev.net.NetworkConstants}'
* <code>FIREABLE_UPDATE_RATE</code> for the network synchronization rate.
*
* @param object object to synchronize
* @throws IOException
*
* @see edu.ups.gamedev.net.Server#register(Spatial, long)
*/
public void register(Spatial object, int type) throws IOException {
register(object, type, NetworkConstants.FIREABLE_UPDATE_RATE);
}
/**
* Registers a part of the scene-graph to be automatically synchronized across
* the network, using <code>rate</code> as the network synchonrization rate.
*
* @param object object to synchronize
* @param rate milliseconds between each synch
* @throws IOException
*/
public void register(Spatial object, int type, long rate) throws IOException {
try {
switch(type) {
case NetworkConstants.SPHERE:
syncManager.register(object, new ColoredSynchronizeCreateMessage(((PlayerSphere) object).getColor()), rate);
break;
case NetworkConstants.TANK:
Tank tank = (Tank) object;
syncManager.register(object, new TankSynchronizeCreateMessage(tank), rate);
break;
case NetworkConstants.TEAPOT:
Teapot teapot = (Teapot) object;
syncManager.register(object, new TeapotSynchronizeCreateMessage(teapot), rate);
break;
case NetworkConstants.PHYSICS_FIREABLE:
PhysicsFireable pFire = (PhysicsFireable) object;
// Possibly need to pass the actual class(MissleFireable, ect.) rather than it's parent class(Physics Fireable)
syncManager.register(object, new PhysicsFireableSynchronizeCreateMessage(PhysicsFireable.class, pFire), rate);
break;
default:
System.err.println("Attempt to register unknown type, quitting...");
System.exit(-1);
}
}catch(ConnectionException e) {
System.out.println(e);
//System.exit(-1);
}
}
/**
* Unregisters a part of the scene-graph, informing this <code>Client
* </code> that it should no longer synchronize <code>object</code>
* across the network.
*
* @param object <code>Spatial</code> to unregister
*/
public void unregister(Spatial object) {
System.out.println("UNREGISTERING: " + object);
syncManager.unregister(object);
}
/**
* Creates and returns an appropriate object based on the message it recieves.
* Called automatically by JGN.
*/
public Object create(SynchronizeCreateMessage scm) {
if (scm instanceof ColoredSynchronizeCreateMessage) {
System.out.println("ColorSyncMsg");
ColoredSynchronizeCreateMessage msg = (ColoredSynchronizeCreateMessage) scm;
PlayerSphere p = new PlayerSphere("remote", msg.getColor());
TankGame.GAMESTATE.getRoot().attachChild(p);
TankGame.GAMESTATE.getRoot().updateGeometricState(0, true);
p.setRenderQueueMode(Renderer.QUEUE_OPAQUE);
TankGame.GAMESTATE.getRoot().updateRenderState();
return p;
} else if (scm instanceof TankSynchronizeCreateMessage) {
System.out.println("TankSyncMsg");
TankSynchronizeCreateMessage msg = (TankSynchronizeCreateMessage) scm;
Tank tank = null;
try {
tank = new Tank(msg);
} catch (MalformedURLException e) {
System.err.println("Could not create Tank");
e.printStackTrace();
System.exit(1);
}
TankGame.GAME.lock();
TankGame.GAMESTATE.getRoot().attachChild(tank);
TankGame.GAMESTATE.getRoot().updateGeometricState(0, true);
tank.setRenderQueueMode(Renderer.QUEUE_OPAQUE);
TankGame.GAMESTATE.getRoot().updateRenderState();
TankGame.GAME.unlock();
return tank;
} else if (scm instanceof TeapotSynchronizeCreateMessage) {
System.out.println("TeapotSyncMsg");
TeapotSynchronizeCreateMessage msg = (TeapotSynchronizeCreateMessage) scm;
Teapot teapot = msg.getTeapot();
TankGame.GAME.lock();
TankGame.GAMESTATE.getRoot().attachChild(teapot);
TankGame.GAMESTATE.getRoot().updateGeometricState(0, true);
teapot.setRenderQueueMode(Renderer.QUEUE_OPAQUE);
TankGame.GAMESTATE.getRoot().updateRenderState();
TankGame.GAME.unlock();
return teapot;
} else if (scm instanceof PhysicsFireableSynchronizeCreateMessage) {
System.out.println("PhysicsFireableSyncMsg");
PhysicsFireableSynchronizeCreateMessage msg = (PhysicsFireableSynchronizeCreateMessage) scm;
PhysicsFireable pFire = msg.getFireable();
TankGame.GAME.lock();
TankGame.GAMESTATE.getRoot().attachChild(pFire);
TankGame.GAMESTATE.getRoot().updateGeometricState(0, true);
pFire.setRenderQueueMode(Renderer.QUEUE_OPAQUE);
TankGame.GAMESTATE.getRoot().updateRenderState();
TankGame.GAME.unlock();
return pFire;
}
System.exit(1); //if we get here, it's an error, quit the program
return null; //it's actually impossible to reach this line, it's just to satisfy the compiler
}
/**
* Removes the specified object from the scene-graph. Called automatically
* by JGN.
*/
public boolean remove(SynchronizeRemoveMessage srm, Object obj) {
System.out.println("REMOVING: " + obj);
//cast to a Spatial and remove from the scene graph
Spatial s = (Spatial) obj;
TankGame.GAME.lock();
boolean removed = s.removeFromParent();
TankGame.GAME.unlock();
return removed;
}
public void kill() {
for(Thread child: childThreads) {
System.out.println("Killing...");
child.interrupt();
}
syncManager.shutdown();
}
/**
* Returns the address this <code>Server</code> uses for fast (UDP)
* communication.
*
* @return the address this <code>Server</code> uses for fast communication
*/
public InetSocketAddress getServerFastAddress() {
return serverFast;
}
/**
* Returns the address this <code>Server</code> uses for reliable (TCP)
* communication.
*
* @return the address this <code>Server</code> uses for reliable communication
*/
public InetSocketAddress getServerReliableAddress() {
return serverReliable;
}
}