diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..838458f --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/dist/ \ No newline at end of file diff --git a/build/classes/.netbeans_automatic_build b/build/classes/.netbeans_automatic_build deleted file mode 100644 index e69de29..0000000 diff --git a/build/classes/.netbeans_update_resources b/build/classes/.netbeans_update_resources deleted file mode 100644 index e69de29..0000000 diff --git a/build/classes/mygame/Main.class b/build/classes/mygame/Main.class index e13354c..98689ab 100644 Binary files a/build/classes/mygame/Main.class and b/build/classes/mygame/Main.class differ diff --git a/nbproject/configs/Build_Client.properties b/nbproject/configs/Build_Client.properties new file mode 100644 index 0000000..02f337e --- /dev/null +++ b/nbproject/configs/Build_Client.properties @@ -0,0 +1 @@ +$label=Build Client diff --git a/nbproject/configs/Build_Server.properties b/nbproject/configs/Build_Server.properties new file mode 100644 index 0000000..705f650 --- /dev/null +++ b/nbproject/configs/Build_Server.properties @@ -0,0 +1,2 @@ +$label=Build Server +main.class=mygame.server.ServerMain diff --git a/nbproject/private/config.properties b/nbproject/private/config.properties index e69de29..9bb741f 100644 --- a/nbproject/private/config.properties +++ b/nbproject/private/config.properties @@ -0,0 +1 @@ +config=Build_Client diff --git a/nbproject/private/private.properties b/nbproject/private/private.properties index 989c3db..d5997be 100644 --- a/nbproject/private/private.properties +++ b/nbproject/private/private.properties @@ -1,4 +1,4 @@ -compile.on.save=true +compile.on.save=false do.depend=false do.jar=true javac.debug=true diff --git a/nbproject/private/private.xml b/nbproject/private/private.xml index 4732b6b..bd3a23f 100644 --- a/nbproject/private/private.xml +++ b/nbproject/private/private.xml @@ -5,8 +5,9 @@ file:/E:/GameProjects/Rabi-Bounce-Bounce-R/src/mygame/Main.java file:/E:/GameProjects/Rabi-Bounce-Bounce-R/src/mygame/appstate/RunLevel.java - file:/E:/GameProjects/Rabi-Bounce-Bounce-R/src/mygame/control/PhysicsControl.java file:/E:/GameProjects/Rabi-Bounce-Bounce-R/src/mygame/control/PlayableCharacter.java + file:/E:/GameProjects/Rabi-Bounce-Bounce-R/src/mygame/server/ServerMain.java + file:/E:/GameProjects/Rabi-Bounce-Bounce-R/src/mygame/control/NetworkPlayableCharacter.java diff --git a/src/mygame/Main.java b/src/mygame/Main.java index 8f7db8c..3f284a3 100644 --- a/src/mygame/Main.java +++ b/src/mygame/Main.java @@ -1,40 +1,66 @@ package mygame; -import com.jme3.app.Application; import com.jme3.app.SimpleApplication; -import com.jme3.app.state.BaseAppState; -import com.jme3.material.Material; -import com.jme3.math.ColorRGBA; -import com.jme3.math.Vector3f; -import com.jme3.post.FilterPostProcessor; +import com.jme3.network.Client; +import com.jme3.network.ClientStateListener; +import com.jme3.network.Message; +import com.jme3.network.MessageListener; +import com.jme3.network.Network; import com.jme3.renderer.RenderManager; -import com.jme3.renderer.queue.RenderQueue; -import com.jme3.renderer.queue.RenderQueue.Bucket; -import com.jme3.scene.Geometry; import com.jme3.scene.Node; import com.jme3.scene.Spatial; -import com.jme3.scene.shape.Box; import com.jme3.system.AppSettings; import com.jme3.terrain.geomipmap.TerrainQuad; -import com.jme3.util.SkyFactory; -import com.jme3.water.SimpleWaterProcessor; -import com.jme3.water.WaterFilter; +import java.io.IOException; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; import mygame.appstate.RunLevel; -import mygame.control.PlayablePhysicsCharacter; +import static mygame.appstate.RunLevel.networkedPlayersNode; +import static mygame.appstate.RunLevel.queuedPlayerActionMessages; +import mygame.control.NetworkPlayableCharacter; +import mygame.server.ServerMain; +import mygame.server.ServerMain.EntityMessage; +import mygame.server.ServerMain.JoinMessage; +import mygame.server.ServerMain.PlayerActionMessage; +import mygame.server.ServerMain.PlayerPositionMessage; +import mygame.server.ServerMain.ServerMessage; +import mygame.server.ServerMain.SyncLevelMessage; -public class Main extends SimpleApplication { +public class Main extends SimpleApplication implements ClientStateListener{ public static Main main; + public static Client client; + public static RunLevel level; + + Main(){ + try { + client = Network.connectToServer("Rabi-Bounce-Bounce-Rabi", ServerMain.VERSION, "localhost", 19919, 19919); + //client = Network.connectToServer("localhost",19919); + client.addMessageListener(new ClientListener(), ServerMessage.class); + client.addMessageListener(new ClientListener(), PlayerPositionMessage.class); + client.addMessageListener(new ClientListener(), SyncLevelMessage.class); + client.addMessageListener(new ClientListener(), EntityMessage.class); + client.addMessageListener(new ClientListener(), JoinMessage.class); + //client.addMessageListener(new ClientListener(), PlayerJoinMessage.class); + client.addMessageListener(new ClientListener(), PlayerActionMessage.class); + client.addClientStateListener(this); + + client.start(); + } catch (IOException ex) { + Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); + } + } public static void main(String[] args) { AppSettings settings = new AppSettings(true); settings.setResolution(1600,900); settings.setVSync(true); settings.setFrameRate(120); - Main app = new Main(); app.setSettings(settings); main = app; + app.setPauseOnLostFocus(false); app.start(); } @@ -42,19 +68,51 @@ public class Main extends SimpleApplication { public void simpleInitApp() { flyCam.setEnabled(false); //flyCam.setMoveSpeed(50); - BaseAppState level = new RunLevel(); + level = new RunLevel("TestLevel"); stateManager.attach(level); } @Override public void simpleUpdate(float tpf) { - //TODO: add update code } @Override public void simpleRender(RenderManager rm) { //TODO: add render code } + + @Override + public void clientConnected(Client c) { + System.out.println("Client successfully connected!"); + } + + @Override + public void clientDisconnected(Client c, DisconnectInfo info) { + System.out.println("Client disconnected. Reason:"+((info!=null)?info.reason+". Error: "+info.error.getMessage():"Leave")); + } + + public class ClientListener implements MessageListener { + public void messageReceived(Client source, Message message) { + if (message instanceof ServerMessage) { + System.out.println("Client #"+source.getId()+" received: "+message); + } else + if (message instanceof PlayerPositionMessage) { + System.out.println("Client #"+source.getId()+" position updated: "+message); + } else + if (message instanceof SyncLevelMessage) { + level.getSyncLevelMessage((SyncLevelMessage)message); + } else + /*if (message instanceof PlayerJoinMessage) { + level.getPlayerJoinMessage((PlayerJoinMessage)message); + } else*/ + if (message instanceof PlayerActionMessage) { + level.getPlayerActionMessage((PlayerActionMessage)message); + } else + if (message instanceof JoinMessage) { + level.getPlayerJoinMessage((JoinMessage)message); + } + } + } public static TerrainQuad SearchForTerrain(Node node) { for (Spatial s : node.getChildren()) { @@ -67,4 +125,10 @@ public class Main extends SimpleApplication { } return null; } + + @Override + public void destroy() { + client.close(); + super.destroy(); + } } diff --git a/src/mygame/appstate/RunLevel.java b/src/mygame/appstate/RunLevel.java index 27aa511..33bb334 100644 --- a/src/mygame/appstate/RunLevel.java +++ b/src/mygame/appstate/RunLevel.java @@ -3,44 +3,38 @@ package mygame.appstate; import com.jme3.animation.AnimChannel; import com.jme3.animation.AnimControl; import com.jme3.animation.AnimEventListener; -import com.jme3.animation.LoopMode; -import template.*; import com.jme3.app.Application; import com.jme3.app.SimpleApplication; import com.jme3.app.state.AppStateManager; import com.jme3.app.state.BaseAppState; import com.jme3.asset.AssetManager; import com.jme3.bullet.BulletAppState; -import com.jme3.bullet.control.BetterCharacterControl; -import com.jme3.bullet.control.RigidBodyControl; import com.jme3.input.ChaseCamera; import com.jme3.input.InputManager; -import com.jme3.input.KeyInput; -import com.jme3.input.controls.KeyTrigger; import com.jme3.light.DirectionalLight; import com.jme3.material.Material; import com.jme3.math.ColorRGBA; import com.jme3.math.Vector3f; import com.jme3.post.FilterPostProcessor; -import com.jme3.post.ssao.SSAOFilter; import com.jme3.renderer.ViewPort; -import com.jme3.renderer.queue.RenderQueue; -import com.jme3.renderer.queue.RenderQueue.ShadowMode; import com.jme3.scene.Geometry; import com.jme3.scene.Node; import com.jme3.scene.Spatial; import com.jme3.scene.shape.Sphere; -import static com.jme3.shader.Shader.ShaderType.Geometry; -import static com.jme3.shader.VarType.Vector3; import com.jme3.shadow.DirectionalLightShadowFilter; -import com.jme3.shadow.DirectionalLightShadowRenderer; import com.jme3.util.SkyFactory; import com.jme3.water.WaterFilter; -import mygame.Main; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import static mygame.Main.main; +import mygame.control.NetworkPlayableCharacter; import mygame.control.PhysicsControl; import mygame.control.PlayableCharacter; -import mygame.control.PlayablePhysicsCharacter; +import mygame.server.ServerMain.JoinMessage; +import mygame.server.ServerMain.SyncLevelMessage; +import mygame.server.Entity; +import mygame.server.ServerMain.PlayerActionMessage; public class RunLevel extends BaseAppState @@ -52,6 +46,20 @@ public class RunLevel extends BaseAppState private InputManager inputManager; private ViewPort viewPort; private BulletAppState physics; + private String level; + private List players = new ArrayList<>(); + private Vector3f[] player_locations; + public static List queuedPlayerActionMessages = new ArrayList<>(); + public static List queuedSyncLevelMessages = new ArrayList<>(); + public static Node world; + Node entityNode; + public static Node networkedPlayersNode; + + public RunLevel(String levelName) { + //System.out.println("In here. Initialize"); + this.level = levelName; + } + @Override protected void initialize(Application app) { //It is technically safe to do all initialization and cleanup in the @@ -60,13 +68,13 @@ public class RunLevel extends BaseAppState //implementor. //TODO: initialize your AppState, e.g. attach spatials to rootNode //super.initialize(stateManager, app); + //System.out.println("In here."); this.app = (SimpleApplication) app; // can cast Application to something more specific this.rootNode = this.app.getRootNode(); this.assetManager = this.app.getAssetManager(); this.stateManager = this.app.getStateManager(); this.inputManager = this.app.getInputManager(); this.viewPort = this.app.getViewPort(); - this.physics = this.stateManager.getState(BulletAppState.class); //rootNode.setShadowMode(ShadowMode.CastAndReceive); @@ -78,8 +86,8 @@ public class RunLevel extends BaseAppState Node reflectedScene = new Node("Reflected Scene"); rootNode.attachChild(reflectedScene); //rootNode.addLight(sceneLight); - Spatial TestLevel = assetManager.loadModel("Scenes/TestLevel.j3o"); - Node world = (Node)TestLevel; + Spatial TestLevel = assetManager.loadModel("Scenes/"+level+".j3o"); + world = (Node)TestLevel; //TestLevel.addControl(new RigidBodyControl(0)); //bulletAppState.getPhysicsSpace().addAll(TestLevel); //System.out.println(world.getLocalLightList().size()); @@ -89,9 +97,19 @@ public class RunLevel extends BaseAppState Node playerNode = new Node(); playerNode.attachChild(player); playerNode.addControl(new PlayableCharacter()); - playerNode.setUserData("Level", world); - for (int i=0;i<100;i++) { + entityNode = new Node(); + networkedPlayersNode = new Node(); + + //Node networkPlayer = (Node)assetManager.loadModel("Models/Oto/Oto.mesh.xml"); + /*Node networkPlayerNode = new Node(); + networkPlayer.move(0,2.5f,0); + networkPlayerNode.attachChild(networkPlayer); + networkPlayerNode.setLocalTranslation(7f,15f,-14f); + networkPlayerNode.addControl(new NetworkPlayableCharacter()); + world.attachChild(networkPlayerNode);*/ + + /*for (int i=0;i<100;i++) { Node sphereNode = new Node(); Geometry sphere = new Geometry("PhysicsSphere",new Sphere((int)(Math.random()*10)+3,(int)(Math.random()*10)+3,3f)); Material mat = new Material(assetManager, "Materials/newMatDef.j3md"); @@ -102,7 +120,14 @@ public class RunLevel extends BaseAppState sphereNode.addControl(new PhysicsControl(world,0.0f,-0.2f,3f)); sphereNode.setLocalTranslation(0.01f+75f*(float)Math.random()-75f*(float)Math.random(),25f+300f*(float)Math.random(),0.01f+75f*(float)Math.random()-75f*(float)Math.random()); reflectedScene.attachChild(sphereNode); - } + }*/ + + Entity ent = new Entity(main.client.getId(),"NETWORKPLAYER"); + ent.position = playerNode.getLocalTranslation(); + JoinMessage msg = new JoinMessage(level,ent); + main.client.send(msg); + + //System.out.println("In here.,"); ChaseCamera chaseCam = new ChaseCamera(this.app.getCamera(), player, inputManager); chaseCam.setLookAtOffset(new Vector3f(0,2.5f,0)); @@ -125,6 +150,9 @@ public class RunLevel extends BaseAppState //.addLight(sceneLight); //world.addLight(sceneLight); reflectedScene.addLight(sceneLight); + world.attachChild(entityNode); //ONLY PUT COLLIDEABLES IN WORLD NODE!! + //world.attachChild(networkedPlayersNode); + reflectedScene.attachChild(networkedPlayersNode); reflectedScene.attachChild(world); reflectedScene.attachChild(TestLevel); reflectedScene.attachChild(playerNode); @@ -152,7 +180,22 @@ public class RunLevel extends BaseAppState SSAOFilter ssaoFilter = new SSAOFilter(12.94f, 43.92f, 0.33f, 0.61f); fpp.addFilter(ssaoFilter); viewPort.addProcessor(fpp);*/ - + } + + public void getSyncLevelMessage(SyncLevelMessage msg) { + System.out.println("Received sync level message: "+msg); + queuedSyncLevelMessages.add(msg); + } + + public void createPlayers() { + for (int i=0;i0) { - //System.out.println(results.getCollision(0)); - if (results.getClosestCollision().getContactPoint().x!=0 || - results.getClosestCollision().getContactPoint().y!=0 || - results.getClosestCollision().getContactPoint().z!=0) { - //System.out.println(results.getClosestCollision()); - if (results.getClosestCollision().getDistance()<=(modelHeight/2)+0.1-vspd) { - spatial.setLocalTranslation(results.getClosestCollision().getContactPoint()); + //world.updateGeometricState(); + world.collideWith(r, results); + //Get closest collision that doesn't include myself. + List collisions = new ArrayList<>(); + for (int i=0;i0) { + //System.out.println(newResults.getCollision(0)); + if (newResults.getClosestCollision().getContactPoint().x!=0 || + newResults.getClosestCollision().getContactPoint().y!=0 || + newResults.getClosestCollision().getContactPoint().z!=0) { + //System.out.println(newResults.getClosestCollision()); + if (newResults.getClosestCollision().getDistance()<=(modelHeight/2)+0.1-vspd) { + spatial.setLocalTranslation(newResults.getClosestCollision().getContactPoint()); return true; } else { return false; diff --git a/src/mygame/control/PlayableCharacter.java b/src/mygame/control/PlayableCharacter.java index b98a4b7..995146c 100644 --- a/src/mygame/control/PlayableCharacter.java +++ b/src/mygame/control/PlayableCharacter.java @@ -16,6 +16,7 @@ import com.jme3.math.FastMath; import com.jme3.math.Quaternion; import com.jme3.math.Ray; import com.jme3.math.Vector3f; +import com.jme3.network.Message; import com.jme3.renderer.RenderManager; import com.jme3.renderer.ViewPort; import com.jme3.scene.Geometry; @@ -25,6 +26,9 @@ import com.jme3.scene.control.AbstractControl; import com.jme3.scene.control.Control; import java.io.IOException; import static mygame.Main.main; +import mygame.server.ServerMain.PlayerActionMessage; +import mygame.server.ServerMain.PlayerPositionMessage; +import mygame.server.ServerMain.ServerMessage; public class PlayableCharacter extends AbstractControl implements Savable, Cloneable, ActionListener, AnalogListener, AnimEventListener { @@ -45,9 +49,9 @@ public class PlayableCharacter extends AbstractControl implements Savable, Clone AnimControl control; PhysicsControl physics; - - public PlayableCharacter() { - } // empty serialization constructor + Vector3f walkDirection; + + // empty serialization constructor /** This method is called when the control is added to the spatial, * and when the control is removed from the spatial (setting a null value). @@ -60,7 +64,6 @@ public class PlayableCharacter extends AbstractControl implements Savable, Clone Node myNode = (Node)spatial; physics = new PhysicsControl( - (Node)spatial.getUserData("Level"), 0.1f, -0.25f, 5f @@ -85,7 +88,6 @@ public class PlayableCharacter extends AbstractControl implements Savable, Clone main.getInputManager().addListener(this, "StrafeRight"); main.getInputManager().addListener(this, "StrafeLeft"); main.getInputManager().addListener(this, "Jump"); - } /** Implement your spatial's behaviour here. @@ -96,38 +98,29 @@ public class PlayableCharacter extends AbstractControl implements Savable, Clone @Override protected void controlUpdate(float tpf){ //System.out.println(((Geometry)(((Node)((Node)spatial).getChild(0)).getChild(0))).getName()); //Possibility of using geometry node names. + /*if (this instanceof NetworkPlayableCharacter) { + System.out.println("1:"+getWalkDirection()+"Moving:"+moving+"/"+strafingLeft+"/"+strafingRight+"/"+walkingBackward+"/"+walkingForward); + }*/ if (moving) { if (!channel.getAnimationName().equalsIgnoreCase("Walk")) { channel.setAnim("Walk"); channel.setLoopMode(LoopMode.Loop); } - Vector3f camDir = main.getCamera().getDirection(); camDir.y=0; camDir.normalizeLocal(); - Vector3f camLeftDir = main.getCamera().getLeft(); camLeftDir.y=0; camLeftDir.normalizeLocal(); - - Vector3f walkDirection = new Vector3f(0,0,0); - //spatial.setLocalRotation(new Quaternion().fromAngleAxis(spatial.getControl(ChaseCamera.class).getHorizontalRotation(),Vector3f.UNIT_Y)); - //System.out.println(camDir); + moving=false; - if (strafingLeft) { - walkDirection.addLocal(camLeftDir); - moving=true; - } - if (strafingRight) { - walkDirection.addLocal(camLeftDir.negate()); - moving=true; - } - - if (walkingForward) { - walkDirection.addLocal(camDir); - moving=true; - } - if (walkingBackward) { - walkDirection.addLocal(camDir.negate()); - moving=true; - } + if (this instanceof NetworkPlayableCharacter) { + walkDirection = getWalkDirection((Vector3f)spatial.getUserData("lastCamDir"),(Vector3f)spatial.getUserData("lastCamLeftDir")); + } else { + walkDirection = getWalkDirection(main.getCamera().getDirection(),main.getCamera().getLeft()); + } + /*if (this instanceof NetworkPlayableCharacter) { + System.out.println(" 2:"+getWalkDirection()+"Moving:"+moving+"/"+strafingLeft+"/"+strafingRight+"/"+walkingBackward+"/"+walkingForward); + }*/ if (moving) { SmoothMoveWalk(walkDirection, tpf); + //Message msg = new PlayerPositionMessage(spatial.getLocalTranslation()); + //main.client.send(msg); } else { channel.setAnim("stand"); channel.setLoopMode(LoopMode.DontLoop); @@ -139,6 +132,9 @@ public class PlayableCharacter extends AbstractControl implements Savable, Clone private void SmoothMoveWalk(Vector3f walkDirection, float tpf) { walkDirection.multLocal(speed).multLocal(tpf); spatial.move(walkDirection); + /*if (this instanceof NetworkPlayableCharacter) { + System.out.println("Moving. My speed is "+speed+" walkDir is "+walkDirection); + }*/ Quaternion q = new Quaternion().fromAngleAxis((float)FastMath.atan2(walkDirection.x,walkDirection.z),Vector3f.UNIT_Y); Quaternion q2 = spatial.getLocalRotation(); q2.slerp(q,Math.min(current_time/rotation_time,1)); @@ -177,24 +173,40 @@ public class PlayableCharacter extends AbstractControl implements Savable, Clone prevRot = spatial.getLocalRotation(); strafingLeft = isPressed; moving = true; + if (!(this instanceof NetworkPlayableCharacter)) { //Only send if this is the source client. + PlayerActionMessage action = new PlayerActionMessage(name,Boolean.toString(isPressed),main.client.getId(),spatial.getLocalTranslation(),spatial.getLocalRotation(),main.getCamera().getDirection(),main.getCamera().getLeft()); + main.client.send(action); + } }break; case "StrafeRight":{ current_time = 0.0f; prevRot = spatial.getLocalRotation(); strafingRight = isPressed; moving = true; + if (!(this instanceof NetworkPlayableCharacter)) { //Only send if this is the source client. + PlayerActionMessage action = new PlayerActionMessage(name,Boolean.toString(isPressed),main.client.getId(),spatial.getLocalTranslation(),spatial.getLocalRotation(),main.getCamera().getDirection(),main.getCamera().getLeft()); + main.client.send(action); + } }break; case "WalkBackward":{ current_time = 0.0f; - prevRot = spatial.getLocalRotation(); + prevRot = spatial.getLocalRotation(); walkingBackward = isPressed; moving = true; + if (!(this instanceof NetworkPlayableCharacter)) { //Only send if this is the source client. + PlayerActionMessage action = new PlayerActionMessage(name,Boolean.toString(isPressed),main.client.getId(),spatial.getLocalTranslation(),spatial.getLocalRotation(),main.getCamera().getDirection(),main.getCamera().getLeft()); + main.client.send(action); + } }break; case "WalkForward":{ current_time = 0.0f; prevRot = spatial.getLocalRotation(); walkingForward = isPressed; moving = true; + if (!(this instanceof NetworkPlayableCharacter)) { //Only send if this is the source client. + PlayerActionMessage action = new PlayerActionMessage(name,Boolean.toString(isPressed),main.client.getId(),spatial.getLocalTranslation(),spatial.getLocalRotation(),main.getCamera().getDirection(),main.getCamera().getLeft()); + main.client.send(action); + } }break; } } @@ -205,6 +217,10 @@ public class PlayableCharacter extends AbstractControl implements Savable, Clone case "Jump":{ if (isOnGround() || physics.airTime<=physics.walkOffTime) { physics.jump(); + if (!(this instanceof NetworkPlayableCharacter)) { //Only send if this is the source client. + PlayerActionMessage action = new PlayerActionMessage(name,"",main.client.getId(),spatial.getLocalTranslation(),spatial.getLocalRotation(),main.getCamera().getDirection(),main.getCamera().getLeft()); + main.client.send(action); + } } }break; } @@ -222,4 +238,30 @@ public class PlayableCharacter extends AbstractControl implements Savable, Clone @Override public void onAnimChange(AnimControl control, AnimChannel channel, String animName) { } + + public Vector3f getWalkDirection(Vector3f camDir, Vector3f camLeftDir) { + camDir.y=0; camDir.normalizeLocal(); + camLeftDir.y=0; camLeftDir.normalizeLocal(); + + Vector3f walkDirection = new Vector3f(0,0,0); + + if (strafingLeft) { + walkDirection.addLocal(camLeftDir); + moving=true; + } + if (strafingRight) { + walkDirection.addLocal(camLeftDir.negate()); + moving=true; + } + + if (walkingForward) { + walkDirection.addLocal(camDir); + moving=true; + } + if (walkingBackward) { + walkDirection.addLocal(camDir.negate()); + moving=true; + } + return walkDirection; + } } \ No newline at end of file diff --git a/src/mygame/server/Entity.java b/src/mygame/server/Entity.java new file mode 100644 index 0000000..39f72d6 --- /dev/null +++ b/src/mygame/server/Entity.java @@ -0,0 +1,58 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mygame.server; + +import com.jme3.math.Vector3f; +import com.jme3.network.serializing.Serializable; +import java.lang.reflect.Field; + +/** + * + * @author sigon + */ +@Serializable +public class Entity { + public Integer id; + public String type; + public String modelData; + public Vector3f position = Vector3f.ZERO; + public String stateData; + public Entity() { + + } + public Entity(Integer id, String type) { + this.id=id; + this.type=type; + } + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(this.getClass().getName()+"("); + boolean first=false; + for (Field f : this.getClass().getDeclaredFields()) { + if (!first) { + try { + sb.append(f.getName()+"="+f.get(this)); + first=true; + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } else { + try { + sb.append(","+f.getName()+"="+f.get(this)); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + } + sb.append(")"); + return sb.toString(); + } +} diff --git a/src/mygame/server/Instance.java b/src/mygame/server/Instance.java new file mode 100644 index 0000000..0a0a8ff --- /dev/null +++ b/src/mygame/server/Instance.java @@ -0,0 +1,100 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mygame.server; + +import com.jme3.math.Vector3f; +import com.jme3.network.Filters; +import com.jme3.network.HostedConnection; +import com.jme3.network.Message; +import com.jme3.scene.Spatial; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import mygame.server.ServerMain.EntityMessage; +import static mygame.server.ServerMain.players; +import static mygame.server.ServerMain.server; + +/** + * + * @author sigon + */ +public class Instance { + String levelName; //The scene name. + protected List clients = new ArrayList<>(); //The clients connected to this instance. + protected HashMap lastKnownPositions = new HashMap<>(); + protected List entities = new ArrayList<>(); //Entity data specific to this instance. + List nullEntities = new ArrayList<>(); //A list of "null" entities. When adding new entities, these slots will be consumed first. + Instance(String levelName) { + this.levelName = levelName; + } + + public void addPlayer(HostedConnection connection) { + //System.out.println("Inside here."); + //server.broadcast(Filters.in(clients),new PlayerJoinMessage(new Entity(connection.getId(),"NETWORKPLAYER"))); + + //Update the player's location with this location. They cannot be in two places at once. + if (players.containsKey(connection.getId())) { + Instance i = players.get(connection.getId()); + i.removePlayer(connection); + } + players.put(connection.getId(), this); + clients.add(connection); + } + public void removePlayer(HostedConnection connection) { + //System.out.println("Inside here."); + //server.broadcast(Filters.in(clients),new PlayerJoinMessage(new Entity(connection.getId(),"NETWORKPLAYER"))); + clients.remove(connection); + } + public Integer[] getPlayers() { + Integer[] players = new Integer[clients.size()]; + for (int i=0;i0) { + newID = nullEntities.remove(0); + entities.set(newID, new Entity(newID,type)); + } else { + newID = entities.size(); + entities.add(new Entity(newID,type)); + } + updateEntityToClients(newID); + } + public void updateEntityToClients(Integer id) { + Entity ent = entities.get(id); + server.broadcast(Filters.in(clients),new EntityMessage(ent)); + } + public Entity[] getEntities() { + return entities.toArray(new Entity[entities.size()]); + } + + @Override + public String toString() { + return "Entities: "+entities+" / Players: "+clients+" / Null Entities: "+nullEntities; + } + + void updatePosition(int id, Vector3f position) { + lastKnownPositions.put(id,position); + } +} diff --git a/src/mygame/server/ServerMain.java b/src/mygame/server/ServerMain.java new file mode 100644 index 0000000..d327808 --- /dev/null +++ b/src/mygame/server/ServerMain.java @@ -0,0 +1,342 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mygame.server; + +import com.jme3.app.SimpleApplication; +import com.jme3.math.ColorRGBA; +import com.jme3.math.Quaternion; +import com.jme3.math.Vector3f; +import com.jme3.network.*; +import com.jme3.network.HostedConnection; +import com.jme3.network.Network; +import com.jme3.network.Server; +import com.jme3.network.serializing.Serializable; +import com.jme3.network.serializing.Serializer; +import com.jme3.system.AppSettings; +import com.jme3.system.JmeContext; +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author sigon + */ +public class ServerMain extends SimpleApplication{ + + public final static int VERSION = 00001; + public static Server server; + int clientIDMax = 0; + public static HashMap instances = new HashMap<>(); + public static HashMap players = new HashMap<>(); //The last instance location this player was seen in. + + ServerMain() { + try { + server = Network.createServer("Rabi-Bounce-Bounce-Rabi", VERSION, 19919, 19919); + //server = Network.createServer(19919); + server.addConnectionListener(new ConnectionListener() { + @Override + public void connectionAdded(Server server, HostedConnection conn) { + System.out.println("Client connected: "+conn); + } + + @Override + public void connectionRemoved(Server server, HostedConnection conn) { + System.out.println("Client disconnected: "+conn); + if (players.containsKey(conn.getId())) { + Instance i = players.get(conn.getId()); + i.removePlayer(conn); + } + } + }); + + Serializer.registerClass(ServerMessage.class); + Serializer.registerClass(PlayerPositionMessage.class); + Serializer.registerClass(EntityMessage.class); + Serializer.registerClass(JoinMessage.class); + Serializer.registerClass(SyncLevelMessage.class); + Serializer.registerClass(Entity.class); + //Serializer.registerClass(PlayerJoinMessage.class); + Serializer.registerClass(PlayerActionMessage.class); + + server.addMessageListener(new ServerListener(), ServerMessage.class); + server.addMessageListener(new ServerListener(), PlayerPositionMessage.class); + server.addMessageListener(new ServerListener(), EntityMessage.class); + server.addMessageListener(new ServerListener(), JoinMessage.class); + server.addMessageListener(new ServerListener(), SyncLevelMessage.class); + //server.addMessageListener(new ServerListener(), PlayerJoinMessage.class); + server.addMessageListener(new ServerListener(), PlayerActionMessage.class); + + server.start(); + } catch (IOException ex) { + Logger.getLogger(ServerMain.class.getName()).log(Level.SEVERE, null, ex); + } + } + + public static void main(String[] args) { + if (args.length>0) { + if (Boolean.parseBoolean(args[0])) { //To enable fine logging, set first arg to "true" + System.out.println("Setting logging to max"); + Logger networkLog = Logger.getLogger("com.jme3.network"); + networkLog.setLevel(Level.FINEST); + // And we have to tell JUL's handler also + // turn up logging in a very convoluted way + Logger rootLog = Logger.getLogger(""); + if( rootLog.getHandlers().length > 0 ) { + rootLog.getHandlers()[0].setLevel(Level.FINEST); + } + } + } + + AppSettings settings = new AppSettings(true); + settings.setFrameRate(120); + ServerMain app = new ServerMain(); + app.start(JmeContext.Type.Headless); + } + + @Override + public void simpleInitApp() { + } + + @Override + public void simpleUpdate(float tpf) { + /*if (server!=null) { + System.out.println("Clients connected: "+server.getConnections().size()); + for (HostedConnection c : server.getConnections()) { + if (c.getAttribute("id")==null) { + c.setAttribute("id", clientIDMax++); + System.out.println("New client detected! Assigned ID "+c.getAttribute("id")); + } + } + }*/ + } + + public class ServerListener implements MessageListener { + public void messageReceived(HostedConnection source, Message message) { + if (message instanceof ServerMessage) { + System.out.println("Server received '"+message+"' from client #"+source.getId()); + } else + if (message instanceof PlayerPositionMessage) { + System.out.println("Position update for client "+source.getId()+". Broadcasting to others."); + server.broadcast(Filters.notEqualTo(source), message); + server.broadcast(Filters.in(source), new ServerMessage("Sent an update to other clients!")); + } else + if (message instanceof JoinMessage) { + JoinMessage msg = (JoinMessage)message; + System.out.println("New join request: "+msg); + Instance instance; + //System.out.println("Got here."); + if (instances.containsKey(msg.levelName)) { + //We will load the entities and players already in this room. + instance = instances.get(msg.levelName); + System.out.println("Instance "+msg.levelName+" already exists. Loading "+instance); + } else { + instance = new Instance(msg.levelName); + System.out.println("Instance "+msg.levelName+" does not exist. Creating... "+instance); + CreateTestObj(instance); + System.out.println(instance); + instances.put(msg.levelName, instance); + } + //System.out.println("Got here 2."); + instance.addPlayer(source); + SyncLevelMessage sync = new SyncLevelMessage(instance.getEntities(),instance.getPlayers(),instance.getPlayerPositions()); + server.broadcast(Filters.in(source),sync); + server.broadcast(Filters.notEqualTo(source),message); + } else + if (message instanceof PlayerActionMessage) { + System.out.println("Received player action message: "+message); + server.broadcast(Filters.notEqualTo(source), message); + PlayerActionMessage msg = (PlayerActionMessage)message; + if (players.containsKey(source.getId())) { + Instance i = players.get(source.getId()); + i.updatePosition(source.getId(),msg.getPosition()); + } + } /*else + if (message instanceof PlayerJoinMessage) { + System.out.println("Player has joined "+message); + server.broadcast(Filters.notEqualTo(source), message); + }*/ + } + } + + public void CreateTestObj(Instance instance) { + for (int i=0;i<30;i++) { + Entity sphere = new Entity(i,"PhysicsSphere"); + sphere.modelData = (ColorRGBA.randomColor().asIntRGBA()+","+Integer.toString((int)(Math.random()*10)+3)+","+Integer.toString((int)(Math.random()*10)+3)+","+(3f)); + sphere.position = new Vector3f(0.01f+75f*(float)Math.random()-75f*(float)Math.random(),25f+20f*(float)Math.random(),0.01f+75f*(float)Math.random()-75f*(float)Math.random()); + instance.entities.add(sphere); + } + } + + @Serializable + public static class ServerMessage extends AbstractMessage { + String message; + + public ServerMessage() { + } + + public ServerMessage(String message) { + this.message=message; + } + + @Override + public String toString() { + return message; + } + } + + @Serializable + public static class PlayerPositionMessage extends AbstractMessage { + Vector3f pos; + + public PlayerPositionMessage() { + } + + public PlayerPositionMessage(Vector3f pos) { + this.pos=pos; + } + + @Override + public String toString() { + return pos.toString(); + } + } + + @Serializable + public static class EntityMessage extends AbstractMessage { + Integer id; + String type; + + public EntityMessage() { + } + + public EntityMessage(Entity ent) { + this.id = ent.id; + this.type = ent.type; + } + + @Override + public String toString() { + return "Entity ID: "+ id + " / Entity type: "+type; + } + } + @Serializable + public static class JoinMessage extends AbstractMessage { + String levelName; + Entity ent; + + public JoinMessage() { + } + + public JoinMessage(String levelName, Entity ent) { + this.levelName = levelName; + this.ent = ent; + } + + @Override + public String toString() { + return "Client ID "+ent.id+" joined. Entity data: "+ent+". In Instance "+levelName.toString(); + } + + public Entity getEntity() { + return ent; + } + } + @Serializable + public static class SyncLevelMessage extends AbstractMessage { + Entity[] entities; + Integer[] clients; + Vector3f[] positions; + + public SyncLevelMessage() { + } + + public SyncLevelMessage(Entity[] entities, Integer[] clients, Vector3f[] positions) { + this.entities = entities; + this.clients = clients; + this.positions = positions; + } + + public Entity[] getEntities() { + return entities; + } + + public Integer[] getPlayers() { + return clients; + } + + public Vector3f[] getPositions() { + return positions; + } + + @Override + public String toString() { + return "Level entities: "+Arrays.asList(entities).toString()+"/ Clients Connected: "+Arrays.asList(clients).toString(); + } + } + + @Serializable + public static class PlayerActionMessage extends AbstractMessage { + String action; + String value; + int id; + Vector3f pos,camera,cameraLeft; + Quaternion rotation; + + public PlayerActionMessage(){} + + public PlayerActionMessage(String action, String value, int id, Vector3f pos, Quaternion rotation, Vector3f camera, Vector3f cameraLeft) { + this.action=action; + this.value=value; + this.id=id; + this.pos=pos; + this.rotation=rotation; + this.camera=camera; + this.cameraLeft=cameraLeft; + } + + @Override + public String toString() { + return "Player Action: "+action+"-"+value+" at pos: "+pos+" from Client "+id; + } + + public String getAction() { + return action; + } + + public String getData() { + return value; + } + + public int getClientID() { + return id; + } + + public Vector3f getPosition() { + return pos; + } + + public Quaternion getRotation() { + return rotation; + } + + public Vector3f getCamera() { + return camera; + } + + public Vector3f getCameraLeft() { + return cameraLeft; + } + } + + @Override + public void destroy() { + server.close(); + super.destroy(); + } +} diff --git a/src/mygame/server/sphereNode.java b/src/mygame/server/sphereNode.java new file mode 100644 index 0000000..ba3a882 --- /dev/null +++ b/src/mygame/server/sphereNode.java @@ -0,0 +1,14 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mygame.server; + +/** + * + * @author sigon + */ +class sphereNode { + +}