Created stable multiplayer / basic network connectivity.

pull/1/head
Joshua Sigona 4 years ago
parent 0cc5a3d320
commit f7cf09ce4b
  1. 1
      .gitignore
  2. 0
      build/classes/.netbeans_automatic_build
  3. 0
      build/classes/.netbeans_update_resources
  4. BIN
      build/classes/mygame/Main.class
  5. 1
      nbproject/configs/Build_Client.properties
  6. 2
      nbproject/configs/Build_Server.properties
  7. 1
      nbproject/private/config.properties
  8. 2
      nbproject/private/private.properties
  9. 3
      nbproject/private/private.xml
  10. 100
      src/mygame/Main.java
  11. 141
      src/mygame/appstate/RunLevel.java
  12. 94
      src/mygame/control/NetworkPlayableCharacter.java
  13. 41
      src/mygame/control/PhysicsControl.java
  14. 92
      src/mygame/control/PlayableCharacter.java
  15. 58
      src/mygame/server/Entity.java
  16. 100
      src/mygame/server/Instance.java
  17. 342
      src/mygame/server/ServerMain.java
  18. 14
      src/mygame/server/sphereNode.java

1
.gitignore vendored

@ -0,0 +1 @@
/dist/

Binary file not shown.

@ -0,0 +1,2 @@
$label=Build Server
main.class=mygame.server.ServerMain

@ -1,4 +1,4 @@
compile.on.save=true
compile.on.save=false
do.depend=false
do.jar=true
javac.debug=true

@ -5,8 +5,9 @@
<group>
<file>file:/E:/GameProjects/Rabi-Bounce-Bounce-R/src/mygame/Main.java</file>
<file>file:/E:/GameProjects/Rabi-Bounce-Bounce-R/src/mygame/appstate/RunLevel.java</file>
<file>file:/E:/GameProjects/Rabi-Bounce-Bounce-R/src/mygame/control/PhysicsControl.java</file>
<file>file:/E:/GameProjects/Rabi-Bounce-Bounce-R/src/mygame/control/PlayableCharacter.java</file>
<file>file:/E:/GameProjects/Rabi-Bounce-Bounce-R/src/mygame/server/ServerMain.java</file>
<file>file:/E:/GameProjects/Rabi-Bounce-Bounce-R/src/mygame/control/NetworkPlayableCharacter.java</file>
</group>
</open-files>
</project-private>

@ -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,13 +68,12 @@ 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
@ -56,6 +81,39 @@ public class Main extends SimpleApplication {
//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<Client> {
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()) {
if (s instanceof TerrainQuad) {
@ -67,4 +125,10 @@ public class Main extends SimpleApplication {
}
return null;
}
@Override
public void destroy() {
client.close();
super.destroy();
}
}

@ -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<Integer> players = new ArrayList<>();
private Vector3f[] player_locations;
public static List<PlayerActionMessage> queuedPlayerActionMessages = new ArrayList<>();
public static List<SyncLevelMessage> 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;i<players.size();i++) {
if (players.get(i)!=main.client.getId()) {
if (networkedPlayersNode.getChild(Integer.toString(players.get(i)))==null) {
MakeNetworkPlayer(players.get(i),player_locations[i]);
System.out.println("Created a new Networked Player w/ID "+i);
}
}
}
}
@Override
@ -180,7 +223,13 @@ public class RunLevel extends BaseAppState
@Override
public void update(float tpf) {
for (SyncLevelMessage msg : queuedSyncLevelMessages) {
CreateObjects(msg.getEntities());
players.addAll(Arrays.asList(msg.getPlayers()));
player_locations = msg.getPositions();
createPlayers();
}
queuedSyncLevelMessages.clear();
}
@Override
@ -191,4 +240,54 @@ public class RunLevel extends BaseAppState
public void onAnimChange(AnimControl control, AnimChannel channel, String animName) {
}
private void CreateObjects(Entity[] entities) {
for (Entity e : entities) {
switch (e.type) {
case "PhysicsSphere":{ //MODELDATA: COL,ZSAMPLEs,RADIALSAMPLES,RADIUS
String[] data = e.modelData.split(",");
Geometry sphere = new Geometry(e.type+"_"+e.id,new Sphere(Integer.parseInt(data[1]),Integer.parseInt(data[2]),Float.parseFloat(data[1])));
Material mat = new Material(assetManager, "Materials/newMatDef.j3md");
mat.setColor("Color", new ColorRGBA().fromIntRGBA(Integer.parseInt(data[0])));
sphere.setMaterial(mat);
sphere.setLocalTranslation(e.position);
sphere.addControl(new PhysicsControl(0.0f,-0.2f,3f));
entityNode.attachChild(sphere);
}break;
}
}
}
public void getPlayerJoinMessage(JoinMessage playerJoinMessage) {
JoinMessage msg = playerJoinMessage;
MakeNetworkPlayer(msg.getEntity().id,msg.getEntity().position);
players.add(msg.getEntity().id);
createPlayers();
}
private void MakeNetworkPlayer(int id, Vector3f pos) {
Node networkPlayer = (Node)assetManager.loadModel("Models/Oto/Oto.mesh.xml");
Node networkPlayerNode = new Node();
networkPlayer.move(0,2.5f,0);
networkPlayer.setLocalScale(0.5f);
networkPlayerNode.attachChild(networkPlayer);
networkPlayerNode.setLocalTranslation(pos);
networkPlayerNode.addControl(new NetworkPlayableCharacter());
networkPlayerNode.setUserData("id", id);
networkPlayerNode.setName(Integer.toString(id));
//networkPlayerNode.move(0.01f,3f,0.01f);
//world.attachChild(networkPlayerNode);
networkedPlayersNode.attachChild(networkPlayerNode);
}
public void getPlayerActionMessage(PlayerActionMessage playerActionMessage) {
Node networkPlayer = (Node)networkedPlayersNode.getChild(Integer.toString(playerActionMessage.getClientID()));
networkPlayer.setUserData("lastActionMessage", playerActionMessage.getClientID());
networkPlayer.setUserData("lastAction", playerActionMessage.getAction());
networkPlayer.setUserData("lastData", playerActionMessage.getData());
networkPlayer.setUserData("lastPosition", playerActionMessage.getPosition());
networkPlayer.setUserData("lastRotation", playerActionMessage.getRotation());
networkPlayer.setUserData("lastCamDir", playerActionMessage.getCamera());
networkPlayer.setUserData("lastCamLeftDir", playerActionMessage.getCameraLeft());
}
}

@ -0,0 +1,94 @@
package mygame.control;
import com.jme3.animation.AnimControl;
import com.jme3.animation.AnimEventListener;
import com.jme3.export.Savable;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.AnalogListener;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import static mygame.Main.main;
import mygame.server.ServerMain.PlayerActionMessage;
public class NetworkPlayableCharacter extends PlayableCharacter implements Savable, Cloneable, ActionListener, AnalogListener, AnimEventListener {
public NetworkPlayableCharacter() {
super();
}
/** 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).
* It can be used for both initialization and cleanup.
*/
@Override
public void setSpatial(Spatial spatial) {
//super.setSpatial(spatial);
//control = spatial.getControl(BetterCharacterControl.class);
this.spatial = spatial;
Node myNode = (Node)spatial;
physics = new PhysicsControl(
0.1f,
-0.25f,
5f
);
myNode.addControl(physics);
control = ((Node)spatial).getChild(0).getControl(AnimControl.class);
control.addListener(this);
channel = control.createChannel();
channel.setAnim("stand");
/*channel_lowerbody = control.createChannel();
channel_lowerbody.addBone("hip.right");
channel_lowerbody.addBone("hip.left");*/ //There is no strafing animation
/*main.getInputManager().addMapping("WalkForward", new KeyTrigger(KeyInput.KEY_W));
main.getInputManager().addMapping("WalkBackward", new KeyTrigger(KeyInput.KEY_S));
main.getInputManager().addMapping("StrafeLeft", new KeyTrigger(KeyInput.KEY_A));
main.getInputManager().addMapping("StrafeRight", new KeyTrigger(KeyInput.KEY_D));
main.getInputManager().addMapping("Jump", new KeyTrigger(KeyInput.KEY_SPACE));
main.getInputManager().addListener(this, "WalkForward");
main.getInputManager().addListener(this, "WalkBackward");
main.getInputManager().addListener(this, "StrafeRight");
main.getInputManager().addListener(this, "StrafeLeft");
main.getInputManager().addListener(this, "Jump");*/
}
@Override
protected void controlUpdate(float tpf){
super.controlUpdate(tpf);
if (spatial.getUserData("lastActionMessage")!=null &&
spatial.getUserData("lastActionMessage") instanceof Integer
&& Integer.parseInt(spatial.getName())==(Integer)spatial.getUserData("lastActionMessage")) {
//System.out.println((String)spatial.getUserData("lastAction")+","+(String)spatial.getUserData("lastData")+","+(Vector3f)spatial.getUserData("lastPosition"));
//spatial.setUserData("lastActionMessage","NULL");
//System.out.println(spatial.getUserData("lastActionMessage"));
String action = (String)spatial.getUserData("lastAction");
String data = (String)spatial.getUserData("lastData");
Vector3f pos = (Vector3f)spatial.getUserData("lastPosition");
Quaternion dir = (Quaternion)spatial.getUserData("lastRotation");
if (action.equalsIgnoreCase("Jump")) {
spatial.setLocalTranslation(pos);
//spatial.setLocalRotation(dir);
simulateJump(tpf);
} else {
if (!Boolean.parseBoolean(data)) {
spatial.setLocalTranslation(pos);
}
//spatial.setLocalRotation(dir);
simulateAction(action, Boolean.parseBoolean(data), tpf);
}
spatial.setUserData("lastActionMessage", null);
//System.out.println(getWalkDirection()+"Moving:"+moving+"/"+strafingLeft+"/"+strafingRight+"/"+walkingBackward+"/"+walkingForward);
}
}
public void simulateAction(String action, boolean isPressed, float tpf) {
this.onAction(action, isPressed, tpf);
}
public void simulateJump(float tpf) {
this.onAnalog("Jump", 1.0f, tpf);
}
}

@ -1,5 +1,6 @@
package mygame.control;
import com.jme3.collision.CollisionResult;
import com.jme3.collision.CollisionResults;
import template.*;
import com.jme3.export.JmeExporter;
@ -16,6 +17,9 @@ import com.jme3.scene.Spatial;
import com.jme3.scene.control.AbstractControl;
import com.jme3.scene.control.Control;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import static mygame.appstate.RunLevel.world;
public class PhysicsControl extends AbstractControl implements Savable, Cloneable {
@ -25,13 +29,10 @@ public class PhysicsControl extends AbstractControl implements Savable, Cloneabl
float modelHeight = 2.5f;
Node levelData;
float walkOffTime = 0.25f; //How long you can jump after becoming airborne.
float airTime = 0.0f; //Amount of time in air.
public PhysicsControl(Node levelData, float jumpSpd, float gravity, float modelHeight){
this.levelData=levelData;
public PhysicsControl(float jumpSpd, float gravity, float modelHeight){
this.jumpSpd=jumpSpd;
this.gravity=gravity;
this.modelHeight=modelHeight;
@ -45,7 +46,7 @@ public class PhysicsControl extends AbstractControl implements Savable, Cloneabl
public void setSpatial(Spatial spatial) {
super.setSpatial(spatial);
spatial.setShadowMode(ShadowMode.CastAndReceive);
spatial.setUserData("Level", levelData);
//spatial.setUserData("Level", levelData);
}
/** Implement your spatial's behaviour here.
@ -67,7 +68,7 @@ public class PhysicsControl extends AbstractControl implements Savable, Cloneabl
@Override
public Control cloneForSpatial(Spatial spatial){
final PhysicsControl control = new PhysicsControl((Node)(levelData.clone()),jumpSpd,gravity,modelHeight);
final PhysicsControl control = new PhysicsControl(jumpSpd,gravity,modelHeight);
/* Optional: use setters to copy userdata into the cloned control */
// control.setIndex(i); // example
control.setSpatial(spatial);
@ -117,17 +118,25 @@ public class PhysicsControl extends AbstractControl implements Savable, Cloneabl
return false;
}
CollisionResults results = new CollisionResults();
CollisionResults newResults = new CollisionResults();
Ray r = new Ray(spatial.getLocalTranslation().add(0,(modelHeight/2)-vspd,0),Vector3f.UNIT_Y.negate());
GetLevel().updateGeometricState();
GetLevel().collideWith(r, results);
if (results.size()>0) {
//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<CollisionResult> collisions = new ArrayList<>();
for (int i=0;i<results.size();i++) {
if (results.getCollision(i).getGeometry()!=spatial) {
newResults.addCollision(results.getCollision(i));
}
}
if (newResults.size()>0) {
//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;

@ -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;
Vector3f walkDirection;
public PlayableCharacter() {
} // empty serialization constructor
// 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();
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;
}
}

@ -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();
}
}

@ -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<HostedConnection> clients = new ArrayList<>(); //The clients connected to this instance.
protected HashMap<Integer,Vector3f> lastKnownPositions = new HashMap<>();
protected List<Entity> entities = new ArrayList<>(); //Entity data specific to this instance.
List<Integer> 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;i<clients.size();i++) {
players[i] = clients.get(i).getId();
}
return players;
}
public Vector3f[] getPlayerPositions() {
Vector3f[] positions = new Vector3f[clients.size()];
for (int i=0;i<clients.size();i++) {
if (lastKnownPositions.containsKey(clients.get(i).getId())) {
positions[i] = lastKnownPositions.get(clients.get(i).getId());
} else {
positions[i] = Vector3f.ZERO;
}
}
return positions;
}
/**
* Adds an entity to the server.
* @param entity The new entity.
*/
public void addEntity(String type) {
int newID = -1;
if (nullEntities.size()>0) {
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);
}
}

@ -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<String,Instance> instances = new HashMap<>();
public static HashMap<Integer,Instance> 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<HostedConnection> {
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();
}
}

@ -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 {
}
Loading…
Cancel
Save