I’ve had this messaging protocol designed and mostly implemented for sometime now, and I wanted to get others opinions on it. Specifically, what value do you see in this, or what things would you change.
It’s a fairly simple, there are two things you need to understand:
- Message = an object that can write and read to a ByteBuffer, has measurable size in bytes, and a unique id
-
Channel = you send messages along a channel. A channel has the following characteristics.
[list]
[li]id = a number between 0 and the number of channels in your defined protocol) - reliable = whether or not the messages should reliably get to the other computer
- ordered = whether or not the messages need to be ordered upon arrival
- retryCount = the maximum number of times to try to resend a message if the channel is reliable
- retryTimeLimit = the maximum amount of time in milliseconds to try to resend the message, if it hasn’t been sent retryCount but the difference in time since the first send and now is greater than this then cease sending. notify client.
- queuedTimeLimit = the maximum amount of time in milliseconds to keep a message in queue to write (messages are queued if you are trying to send too many and only so many can fit in the packet
- priority = when there are too many messages for one packet, the channels with highest priority have their messages written out first
[/li]
[/list]
public interface Message {
public void write(ByteBuffer out);
public void read(ByteBuffer in);
public int size();
public int tag();
}
public enum Reliable { Yes, No; }
public enum Ordered { Yes, No; }
public class Channel {
public final int id;
public final Reliable reliable;
public final Ordered ordered;
public final int retryCount;
public final int retryTimeLimit;
public final int queuedTimeLimit;
public final int priority;
}
I’ve also added this idea of a “PersistentMessage”, essentially a message that you say most likely will be sent every packet (like user position). If the persistent message has data, it is prepared, then written out like a typical message.
public interface PersistentMessage {
public boolean hasData();
public void prepareData();
}
With all of this, both sides need to configure an agreed upon protocol:
public class Protocol {
public Protocol(int channelMax, int messageMax);
public Protocol(Enum<?> channelEnum, Enum<?> messageEnum);
public void setHeader(Message message);
public void setChannel(int id, ... );
public void setChannel(Enum<?> id, ...);
public void addMessage(Message message);
public void addPersistentMessage(PersistentMessage persistentMessage);
}
(something along those lines)
And of course the “connection”
public class Connection {
public Connection(Protocol protocol)
public void send(int channel, Message message);
public void send(Enum<?> channel, Message message);
public void read();
public void write();
}
(give or take some listeners that listen for dropped messages, when messages are read, etc)
Anyway, the whole point of this is to add flexibility… it all comes down to how much of your data is reliable, and needs to be ordered. If it always needs reliability, maybe TCP will work better. However even if you use TCP, there’s one stream of reliable data… with this model there’s any number of channels that can be marked reliable if a message is missed in one channel it won’t hold up traffic in your channel.
A few examples of when I care about ordering/reliable
Sending User State = Reliable and Ordered (maybe not reliable depending on your server implementation)
Receiving Correct User State = Reliable and Not Ordered
Receiving Remote Entity State = Not Reliable and Ordered
Events (explosions, door opens, etc) = Reliable and Not Ordered
Text Messages = Reliable and Ordered
Are there any glaring flaws with this design that I’ve missed? At this point I think it’s fairly flawless. I want to get this out there and usable sometime soon so other people can test it with their game types.