Networking code

Darkprophet, you said you had some networking code that you’d like to share and put into jme. Do you think you have time to explain the design and stuff? That way we can discuss how to integrate it into the current design of jme.

sure, umm…well, I need to know a few things about where jME wants to go with networking, TCP/IP or UDP? should I include RMI?



I have a server up and running and its currently in development for a flash game. The server is multithreaded, and it sends XML code to specify the player’s properties, e.g.



127.0.0.1

Code sent by one game to another. This will bs sent to ALL players.



Rooms are coming. As for networking in jME, i was hoping to use XML too.



perhaps havea class to connect and establish connection to server, and then a simple sendMessage(String message) will send a message to the server connected?



im suggesting this:



Client game =  new Client(IPCode, Port);

then, a simple game.sendMessage(String message)?

sure, umm...well, I need to know a few things about where jME wants to go with networking, TCP/IP or UDP? should I include RMI?


That's what I want to discuss. I'd like everyone to give their two cents on this. I really don't know where jME should go for networking, but now is as good a time as any to talk it over.

Supporting TCP/IP and UDP might be good. TCP/IP would be good for those games that require acknowledgements (turn based or something), while UDP can support games that can afford to drop packets and keep going (Shooters, etc). RMI? I'm not sure. Do we really need RMI support?

I have a server up and running and its currently in development for a flash game. The server is multithreaded, and it sends XML code to specify the player's properties, e.g.

127.0.0.1
Code sent by one game to another. This will bs sent to ALL players.

Rooms are coming. As for networking in jME, i was hoping to use XML too.

perhaps havea class to connect and establish connection to server, and then a simple sendMessage(String message) will send a message to the server connected?


Sounds reasonable. Would XML parsing be fast enough for action type games? I'm sure it would be fine for slower paced games.

I'm currently working with some server technology for the Army that might give you some ideas. It's PASS (Publish and Subscribe Services). There is a PASS server. This server supports Publishers and Subscribers. A Publisher connects to the server, creates a Topic and publishes messages to the topic. A Subscriber connects to the PASS server and subscribes to a topic. Whenever a subscriber posts a new message to a topic all subscribers are notified via a callback. You can further filter the messages by assigning an "AOI" or area of interest to a message and a subscriber. In the Army's case it's lat lon coordinates. So if a subscriber is using AOI A, and a message uses AOI B, the subscriber will only get the message if A == B or A intersects B. PASS also uses XML for it's messages. A single app can be both a publisher and a subscriber.

Anyways that's one paradigm used.

Let's discuss different paradigms.


im suggesting this:

Code:

Client game = new Client(IPCode, Port);

then, a simple game.sendMessage(String message)?


Why Client? Wouldn't you send messages to a server which would then let client's know?

The String Message. We do need to make sure we take care of creating this xml string for the user. Perhaps an XMLGenerator or something that can take some exisiting object.

We probably only want to send diffs, rather than entire data. I.e. I move, only send my new localTranslation. I move and turn, send translation and rotation. etc.

Again, let's all discuss this for a bit, and try to nail down a design. Looks like you have a great start Mr. Prophet.

RMI, as this is a library, perhaps someone wants it, dont know. Lets add that in later.



TCP/IP is currently in, and im using SAX, its wayyyy faster than DOM, thats what the servers use to process XML. XML is quite a reasonable medium to use, because its universal, everyone knows XML, its not exactly difficult.



As for sending stuff to the server first, how else would I go about it, would the server have to send down a list of all clients down to the client as it connects? what would happen if someone wanted to join middle game?



umm…as for the word “Client” it was made on spure of the moment thing. Change it to anything you like.



the XMLGenerator sounds a fair idea. send the localTranslation/rotation of the player in respect to the world. what about player status? shooting? jumping? different/customised (like Quake) models?



So this PASS system is basically rooms? a publisher makes rooms, and the the subscribers enter these rooms. I like the idea of recieving info if you want to. e.g. if the player is a wimp and is hiding behind some rock, and can’t see the explosions etc, does he really need to get info about the explosions?



so many questions!

My main concern with XML: (This might be misinformed, anyone feel free to correct me).



Sending via XML: Data converted to XML via SAX parser, Data sent through pipe, XML parsed by receiver, data dealt with (update object in world, etc).



Sending via serialized object: Data sent through pipe.



So, I believe that the parsing overhead will cause problems in a fast paced action game where many messages are sent very fast. It will probably work fantastically for slower paced games (strategy, role playing, etc). Anyone with real world experience please chime in.



Perhaps it should support multiple data package types: call 'em payload for now. If you want an easy to read payload send via XML, if you want speed send via the fast way.


As for sending stuff to the server first, how else would I go about it, would the server have to send down a list of all clients down to the client as it connects? what would happen if someone wanted to join middle game?


In the PASS world, a PASS server is nothing more than a specialized router. It receives a message it figures out who to send it to. New subscribers coming in can call the sync command and get a "snapshot" of all the current messages on the server. They are then updated as new messages come in.

Now, I was just bringing this up so you could see one of the other ways of doing it. It's also XML.

So this PASS system is basically rooms? a publisher makes rooms, and the the subscribers enter these rooms. I like the idea of recieving info if you want to. e.g. if the player is a wimp and is hiding behind some rock, and can't see the explosions etc, does he really need to get info about the explosions?


Calling it rooms might be a little too specific. How about calling it filters. Anyways, yes, a publisher makes a "topic" subscriber listens for any message on a particular topic.

Filtering out explosions, physics based stuff is too much for a network server (again, this is all my opinion). This is the job for the scene graph/physics engine/etc. The network would only send information about: Player info (orientation, position, health, etc) as that is updated, and then events (Player 1 shoots). Because the clients receive the information about Player 1, they know his current position and orientation, so when they get the shoot event, they calculate the shot. Of course, now you are getting into the whole cheating thing here.

You have to find a balance between security and the server not doing too much.

This is not an easy chore you gave yourself. :)

However, for the time being, just play around, try to see what possibilities there are, but do not commit anything in jME. For one thing, who knows where we are going with it, secondly, I'm trying to get CVS wiped by sun so we can recommit.

i know! :?



If we were not to use XML, how would you go about doing it.



You seem to push this PASS thing, seems you like it, i say get me some diagrams i can work from, so I know exactly what you want. And il go about making it part of jME!

Hello,

I have read some articles about networking and I know that there are many parameters to take in account in a game; like: there are still people using 28k modems or network packet not delivered …



With my little knowledge of networking i know that XML is really not good for games because the packets sent to servers or clients should be as small as possible. Knowing that one char will take 2 bytes, imagine with a full player description or just a player position!



I would recommend sending small packets of 32 bytes max (more if it’s a chatting stuff).



I have written a loooong time ago a networking application. unfortunately it has never really been tested and I 've lost some parts… 8-O



So, here are some code snippets of what I have done maybe it can give you ideas; That was a trial so the code value is “AS IS” :slight_smile:






package nps.network;

/**
 * @author Arman OZCELIK
 *
 */
public class DataPacket {

   private static int MAX_PACKET_SIZE;
   private final byte[] content;
   private final byte[] from;
   private final byte[] to;
   private final byte[] subject;
   private final byte[] buffer;
   private static int fromLength;
   private static int toLength, toOffset;
   private static int subjectLength, subjectOffset;
   private static int contentLength, contentOffset;
   private byte[] contentType;
   private boolean requestingUDPFeedback;
   private final byte[] zero;

   public DataPacket() {
      this((contentLength != 0) ? contentLength : 32);
   }

   public DataPacket(int contentSize) {
      //from and to values are IPv4 raw addresses
      this(true, 16, contentSize);
   }

   public int length() {
      return MAX_PACKET_SIZE;
   }

   public DataPacket(boolean isIPv4, int subjectSize, int contentSize)
      throws IllegalArgumentException {

      if (subjectSize < 0 || contentSize < 0) {
         throw new IllegalArgumentException(
            "INCONSISTENT PACKET DEFINITION." + "Negative argument detected.");
      }
      contentType = new byte[4];
      fromLength = isIPv4 ? 4 : 16;
      from = new byte[fromLength];
      toLength = isIPv4 ? 4 : 16;
      toOffset = 5 + fromLength; //the first 4 bytes is the content type and the 5th udp flag
      to = new byte[toLength];

      subjectLength = subjectSize;
      subjectOffset = toOffset + toLength;
      subject = new byte[subjectLength];

      contentLength = contentSize;
      contentOffset = subjectOffset + subjectLength;
      content = new byte[contentLength];
      MAX_PACKET_SIZE = contentOffset + contentLength;
      buffer = new byte[MAX_PACKET_SIZE];
      zero = new byte[MAX_PACKET_SIZE];
   }

   
   /**
    * Object creation is slower than System.arraycopy
    *
    */
   
   public void bzero() {
      System.arraycopy(zero, 0, buffer, 0, MAX_PACKET_SIZE);
   }

   /**
    * @return the sender ip
    */
   public byte[] getFrom() {
      return from;
   }

   /**
    * @return the subject
    */
   public byte[] getSubject() {
      return subject;
   }

   /**
    * @return the destination ip
    */
   public byte[] getTo() {
      return to;
   }

   /**
    * @param sets the sender ip
    */
   public void setFrom(byte[] bs) {
      if (bs.length > fromLength)
         System.arraycopy(bs, 0, from, 0, fromLength);
      else
         System.arraycopy(bs, 0, from, 0, bs.length);
   }

   /**
    * @param the subject
    */
   public void setSubject(byte[] bs) {
      if (bs.length > subjectLength)
         System.arraycopy(bs, 0, subject, 0, subjectLength);
      else
         System.arraycopy(bs, 0, subject, 0, bs.length);
   }

   /**
    * @param sets the destination
    */
   public void setTo(byte[] bs) {
      if (bs.length > toLength)
         System.arraycopy(bs, 0, to, 0, toLength);
      else
         System.arraycopy(bs, 0, to, 0, bs.length);
   }

   /**
       * @return the packet data
       */
   public byte[] getContent() {
      return content;
   }

   /**
    * @param the data
    */
   public void setContent(byte[] bs) {
      if (bs.length > contentLength)
         System.arraycopy(bs, 0, content, 0, contentLength);
      else
         System.arraycopy(bs, 0, content, 0, bs.length);
   }

   /**
    * packs the data in the following order
    * contentType+feedbackFlag+from + to + subject + content
    * @return null if the packet is greater than
    * maximum packet size defined with the <code>DataPacket</code> constructor
    * the packed data
    *
    */

   public byte[] pack() {
      System.arraycopy(contentType, 0, buffer, 0, 4);
      buffer[4] = requestingUDPFeedback ? (byte) 1 : 0;
      System.arraycopy(from, 0, buffer, 2, fromLength);
      System.arraycopy(to, 0, buffer, toOffset, toLength);
      System.arraycopy(subject, 0, buffer, subjectOffset, subjectLength);
      System.arraycopy(content, 0, buffer, contentOffset, MAX_PACKET_SIZE - contentLength);
      return buffer;
   }

   public void unpack(byte[] data) {
      bzero();
      if (data.length < MAX_PACKET_SIZE) {
         data = ensureSize(data);
      }
      System.arraycopy(data, 0, contentType, 0, 4);
      requestingUDPFeedback = (data[1] != 0) ? true : false;
      System.arraycopy(data, 1, from, 0, fromLength);
      System.arraycopy(data, toOffset, to, 0, toLength);
      System.arraycopy(data, subjectOffset, subject, 0, subjectLength);
      System.arraycopy(data, contentOffset, content, 0, MAX_PACKET_SIZE - contentLength);

   }

   private byte[] ensureSize(byte[] array) {
      byte[] resized = new byte[MAX_PACKET_SIZE];
      System.arraycopy(array, 0, resized, 0, array.length);
      return resized;
   }

   /**
    * @return the content type of this packet
    */
   public byte[] getContentType() {
      return contentType;
   }

   /**
    * @param b the type of the packet
    */
   public void setContentType(byte[] b) {
      contentType = b;
   }

   /**
    * @return true if this packet must have feedback
    */
   public boolean isRequestingUDPFeedback() {
      return requestingUDPFeedback;
   }

   /**
    * @param b enable/disable udp feedback
    */
   public void setRequestingUDPFeedback(boolean b) {
      requestingUDPFeedback = b;

   }

}




Another class





package nps.network;

import java.util.ArrayList;

import nps.network.utils.PacketCache;

/**
 * @author Arman Ozcelik
 *
 */
public class MessageQueue {

   public final int MAX_QUEUE_SIZE;
   public final int MAX_MESSAGE_SIZE;
   private final ArrayList messages;

   /**
    * Creates a new message queue of size 128 and message size 128
    * Remark that the queue is not resizeable.
    *
    */

   public MessageQueue() {
      this(128, 128);
   }

   /**
    * Creates a new FIFO message queue of given size.
    * Remark that the queue is not resizeable.
    * @param size the size of the queue
    * @param messageSize the maximum length of message
    */

   public MessageQueue(int size, int messageSize) {
      MAX_QUEUE_SIZE = size;
      MAX_MESSAGE_SIZE = messageSize;
      messages = new ArrayList();
   }

   /**
    * Puts a new message in the queue.
    * If the size of the que has reached MAX_QUEUE_SIZE
    * the oldest value (index ==0) will be removed
    * If the length of the message is
    * greater than MAX_MESSAGE_SIZE the message will be right trimmed.
    *
    */

   public void push(DataPacket msg) {
      if (messages.size() == MAX_QUEUE_SIZE) {
         messages.remove(0);
      }
      messages.add(msg);
   }

   public int size() {
      return messages.size();
   }

   /**
    * Removes all the packets in the queue which has the same content
    * @param content
    */


   public synchronized void removeAll(byte[] content) {
      
      Label : for (int a = 0; a < messages.size(); a++) {
         byte[] pContent = ((DataPacket) messages.get(a)).getContent();
         for (int b = 0; b < pContent.length; b++) {
            if (pContent[b] != content[b])
               continue Label;
         }
         PacketCache.put((DataPacket)messages.remove(a));
      }
   }

   /**
    * Pops the the oldest element in queue
    * @return null if the queue is emty
    */

   public DataPacket pop() {
      return messages.size() >0 ? (DataPacket) messages.remove(0) : null;
      
   }
   
   /**
    * Adds the <code>DataPacket</code> content into a new Data packet and adds the new
    * packet in the queue.
    * This method is intented to be used only with <code>UDPFeedbackReciever</code> class.
    * The maximum size of the queue is not limited by using this method.
    * @param p
    */
   
   public void put(DataPacket p){
      DataPacket packet=new DataPacket();
      packet.unpack(p.pack());
      messages.add(packet);
   }

}



An interface


package nps.network;

/**
 * @author Arman Ozcelik
 *
 */

public interface ContentTypes {

   public static final int SYN = 1;
   public static final int ACK = 2;
   public static final int PING = 3;

}



A cache for reusing packets and save memory


package nps.network.utils;

import java.util.ArrayList;

import nps.network.DataPacket;

/**
 * @author Arman Ozcelik
 *
 */
public class PacketCache {
   
   private static ArrayList cache=new ArrayList();
   
   public static void put(DataPacket p){
      cache.add(p);
   }
   
   public static DataPacket nextPacket(){
      DataPacket p=(DataPacket)cache.remove(0);
      if(p !=null){
         return p;
      }else{
         return new DataPacket();
      }
      
   }

}




An UDP sender




package nps.network.protocol;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.nio.*;

import nps.network.ContentTypes;
import nps.network.DataPacket;
import nps.network.MessageQueue;
import nps.network.utils.PacketCache;

/**
 * @author Arman Ozcelik
 *
 */
//TODO comment
public class UDPSender implements Runnable {

   private boolean waitEnabled;
   private boolean suspended;
   private Thread current;
   private MessageQueue queue;
   private long waitTime;
   private boolean killed;
   private int port;
   private DatagramPacket datagram;
   private DatagramSocket socket;
   private InetAddress ip;
   private final DataPacket buffer;
   //   TODO comment
   public UDPSender(MessageQueue queue, int port, long waitTime) throws SocketException {
      buffer = new DataPacket();
      this.queue = queue;

      this.waitTime = waitTime;
      this.port = port;
      datagram = new DatagramPacket(new byte[1], 0);
      datagram.setPort(port);
      socket = new DatagramSocket();
      current = new Thread(this);

   }

   public void suspend() {
      suspended = true;
   }

   //   TODO comment

   public void resume() {
      suspended = false;
   }

   //   TODO comment
   public void timeBomb(final long time) {
      Runnable r = new Runnable() {
         public void run() {
            try {
               Thread.sleep(time);
            } catch (InterruptedException ie) {
            }
            kill();
         }
      };
      Thread timer = new Thread(r);
      timer.start();
   }

   //TODO comment

   public boolean isWaitEnabled() {
      return waitEnabled;
   }

   //TODO comment
   public void setWaitEnabled(boolean enabled) {
      waitEnabled = enabled;
   }

   //TODO comment
   public void start() {
      current.start();

   }

   //TODO comment
   public void kill() {
      killed = true;

   }

   //TODO comment

   public void send(DataPacket dp) {
      queue.push(dp);
   }

   public void run() {
      while (!killed) {
         while (suspended && !killed) {
            try {
               Thread.sleep(waitTime);
            } catch (InterruptedException ie) {
            }
         }
         if (waitEnabled && !killed) {
            try {
               Thread.sleep(waitTime);
            } catch (InterruptedException ie) {

            }
         }
         try {

            if (!killed) {

               DataPacket buffer = queue.pop();
               if (buffer != null) {
                  datagram.setAddress(InetAddress.getByAddress(buffer.getTo()));
                  datagram.setData(buffer.pack());
                  datagram.setLength(buffer.length());
                  try {
                     socket.send(datagram);
                     datagram.setPort(port);
                     PacketCache.put(buffer);
                  } catch (IOException e) {
                     e.printStackTrace();
                  }
               }
            }
         } catch (UnknownHostException uhe) {
            uhe.printStackTrace();
         }
      }

   }

   //   TODO comment

   public void sendNow(DataPacket p) {
      //TODO sendNow exceptions +comment
      if (p != null) {
         try {
            datagram.setAddress(InetAddress.getByAddress(p.getTo()));
         } catch (UnknownHostException uhe) {
            uhe.printStackTrace();
         }
         datagram.setData(p.pack());
         datagram.setLength(p.length());
         try {
            socket.send(datagram);
            PacketCache.put(p);
         } catch (IOException e) {
            e.printStackTrace();
         }
      }
   }

   public static void main(String[] args) throws Exception {
      ByteBuffer buf = ByteBuffer.allocate(4).putInt(ContentTypes.ACK);
      DataPacket p = new DataPacket();
      p.setRequestingUDPFeedback(true);
      p.setContentType(buf.array());
      p.setFrom(InetAddress.getByName("localhost").getAddress());
      p.setTo(InetAddress.getByName("localhost").getAddress());
      MessageQueue messageQueue = new MessageQueue();
      messageQueue.push(p);
      UDPSender s = new UDPSender(messageQueue, 7777, 100);

      s.start();
      s.timedKill(5000);

   }

}



An UDP Reciever


/**
 * @author Arman Ozcelik
 *
 */
package nps.network.protocol;
import java.net.DatagramSocket;
import java.net.DatagramPacket;
import java.io.IOException;
import java.net.SocketException;
import nps.network.DataPacket;
import nps.network.MessageQueue;
import nps.network.utils.PacketCache;

public class UDPReceiver implements Runnable {

   private DataPacket packet;
   private DatagramSocket socket;
   private DatagramPacket datagram;
   private boolean killed;
   private int port;
   private MessageQueue queue;

   public UDPReceiver(MessageQueue queue, int port) throws SocketException {
      this.queue = queue;
      this.port = port;
      datagram = new DatagramPacket(new byte[queue.MAX_MESSAGE_SIZE], queue.MAX_MESSAGE_SIZE);
      socket = new DatagramSocket(port);
      socket.setSoTimeout(1000);
   }

   public void kill() {
      killed = true;
   }

   public void run() {
      while (!killed) {
         packet = PacketCache.nextPacket();

         try {
            socket.receive(datagram);
            packet.unpack(datagram.getData());
            queue.push(packet);
         } catch (SocketException se) {
            //TODO socket timeout
            se.printStackTrace();
         } catch (IOException e) {
            e.printStackTrace();
            //TODO ioexception on receive
         }

      }
      socket.close();

   }
}



Another un-tested class


package nps.network.protocol;

import java.io.IOException;
import java.lang.reflect.Array;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

import nps.network.ContentTypes;
import nps.network.DataPacket;
import nps.network.MessageQueue;
import nps.network.utils.PacketCache;

/**
 * @author Arman Ozcelik
 *
 */
public class UDPFeedback implements Runnable, ContentTypes {

   private DatagramSocket socket;
   private DatagramPacket datagram;
   private int port;
   private MessageQueue queueIn;
   
   ;
   private boolean killed;
   private UDPSender replier;

   public UDPFeedback(MessageQueue queue, int port) throws SocketException {
      
      this.queueIn = queue;
      this.port = port;
      datagram = new DatagramPacket(new byte[1], 0);
      datagram.setPort(port);
      socket = new DatagramSocket(port);
      replier=new UDPSender(null, port, 0);

   }

   public void kill() {
      killed = true;
   }

   public void enableSoTimeout() {

      try {
         socket.setSoTimeout(1000);
      } catch (SocketException e) {
         // TODO enable timeout socket exception
         e.printStackTrace();
      }
   }

   public void run() {
      while (!killed) {
         DataPacket packet = PacketCache.nextPacket();
         try {
            socket.receive(datagram);
            packet.unpack(datagram.getData());
            if (isACK(packet)) {
               queueIn.removeAll(packet.getContent());
            }
            if (isPING(packet)) {
               replier.sendNow(packet);
               PacketCache.put(packet);               
            }

         } catch (SocketException se) {
            //TODO timeout print?
         } catch (IOException ioe) {
            ioe.printStackTrace();
         }
      }

   }

   public static final boolean isACK(DataPacket p) {
      return (Array.getInt(p.getContentType(), 0) == ACK) ? true : false;
   }
   public static final boolean isPING(DataPacket p) {
      return (Array.getInt(p.getContentType(), 0) == PING) ? true : false;
   }

}

Hmmm that is a good point about the extra bytes used by the XML headers. It wouldn’t make much sense to send a 4k update with a 32K header (exaggeration, I know) attached to it.



Could you give a quick little run down on how you’d send updates using the code you posted?

Thanks for that. My experience with PASS is pretty much the same, and I agree. For games, we need the smallest possible data, sent the quickest possible way. At least as the base line. Sending a player update should not require the data to be wrapped in XML and parsed. However, having said that, I also think that we might want to go ahead and build a more complex system on top of the simple, fast solution. If we can give users options, the more likely they will be to use it.

my current system uses a completely dumb server. Send it stuff, and it will send it to all other people except the sender. Having said that. The game developer would have to create a way in which they would like to handle data. Meaning that they would have to format the reading/writing of the text.



The only problem with creating a full network abilities into jME is that there are so many possibilities of what they might send.



So one route to suggest is to implement abstract classes that the developer would gear towards their own implementation. How does that sound?

Yeah, I’d say that would be the best approach. Then you could write some tools that make use of these Abstract classes (probably seperate of the API) that the user could then use, or learn from. I guess the best way to start is at the low level, something just on top of the socket api. The build up from there.

this is gonna be difficult, for me anywa, inventing something that is universal, but unique to jME!



I’l do my best. :slight_smile:

I’d have to echo the belief that XML should not be a fundamental feature of a good game networking layer where bandwidth is a concern.



I am looking at jME after becoming disenchanted with Torque Game Engine (the cross-platform C++ basis for the old Tribes 2 title). It has a fairly nice networking layer (if grossly underdocumented) which permits such niceties as allowing game objects to have data members that are ghosted magically across to other clients and a server design that is cognizant of the viewing frustum of each client (to avoid sending data that does not matter to the receiver). These net-friendly data members also are aware that only changes in state need to be signaled to avoid unnecessary data being sent (except in the case where someone enters a client’s frustum when they had previously been outside it, in which case all their state information is sent).



The other portion of the networking model is “events” which can be subclassed for relaying other types of data not bound to the state of game objects (e.g.: to signal that someone has typed a chat message).



The last nuance that matters, I feel is that the networking layer there also had interpolation/extrapolation for smoothing out motion of objects controlled by other client machines.



I’m not very good at writing network code myself, but I could quickly appreciate the benefits of having necessary networking “just happen” as a consequence of programmatically modifying the state of my local client objects.



Hmm… I see the big CGDC news for Torque is that they are releasing their networking layer separately as a middleware product: blurb on their site





tone

While I appreciate jMe including lots of useful features, I worry that it might become too tightly coupled in a mad rush to incorporate all of the features of a game library.



A non-blocking, fast (if unreliable) data replication system would certainly be very handy, and doesn’t come with much conceptual baggage.



Networking in a more general sense, can be very intrusive to a game’s architecture, particularly when you consider its integration with the physics system.



Decisions about which network nodes are authoritative for what kinds of data can get complicated. Many FPS games seem to make the player the authority for his/her orientation, but the server the authority for the player’s position. Some effects are completely ephemeral (most particle systems).



I’m not saying don’t add networking support, only that I urge you to consider the implications for how general jMe is to a wide variety of games with each feature that’s added.

Hallo all !



Take a look at this dev.java.net project:

https://jsdt.dev.java.net/



Altough i doesn’t have tried it out myself, from what i have seen in the docs and heared on javagaming.org it looks quite reasonable for the base toolkit to implement a game network layer.

It seem to be an old sun package that was released under the sun community license (source available).

(it was anounced and discussed on javagaming.org some time ago)



May be worth a look at least.



and btw… Hi i’m new here … :slight_smile:



regards

Martin

I’d like to suggest that the focus should remain on graphics/sound for now. There’s a dearth of useful non-GPL open-source media libraries for java. There are probably thousands of networking libraries.



Each game will have different networking requirements, depending on what data has to be sent, where it has to be sent, how often it has to be sent, over what transport it can be sent, how latent/reliable the sending, how securely the data has to be sent, and so on.



I’ve written dozens of networked applications/games, and while there are some generalizations between them, on the whole, each has a separate networking implementation.



-Mike

Agreed, and it’s that reason that nothing has been touched yet. Simply discussion about what we might do in the future.

Per mentioned networking and I was re-reading this and I think we can do everything that has been discussed here with the AI system with a few networking additions.



The messages can be recieved via the messagingSystem (found in AISystem). The server would hold the RootEntityNode and when people connect to the server, they would be attached to the RootEntityNode that the server holds.



The Actual entities would be remote entities and when messages are sent, they are sent to the networking layer which sits between the AISystem and the Entity before it is sent off to the players.



This is nice because the players wont be differnetiated from AI Bots and are both treated the same (We’l have to deal with cheating later on).



When the players disconnect, they are simply removed from the RootEntityNode and play is continued as usual.



What do you think?



DP

Networking code is too application specific to easily build a framework. It will turn into something like DirectPlay, and like DirectPlay no one will use it.

fair enough. Agreed



Whats directPlay btw?



DP