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 {
+
+}