Server refactoring need info

So have been working on my game for a while and I think it’s time to get everything into a nice and clean state. The game aim to be a tactical MmoRpg, square grid base. For the moment its build into 5 main sections.

The client (UDP and TCP)

The connection server (UDP)

The world server (UDP)

The combat server (TCP)

The game editor (NA)

For the moment I simply send strings through sockets. They start with a 3 digit code, that represent the packet type. For example a 000 is a connection packet, and the 010 can be a movement packet. The client sends a string, the server receives the string, check the 3 first character, go into a switch case to see what type it is. It takes the class, use the build packet method and start applying a regex on the string. It then takes the info in the regex as they are supposed to be presented, and usually send a packet to anyone concern but the packet. Same Thing when the client recive a packet.

A pretty simple and stray forward thing, but I felt like it’s missing something. Btw, yes, everything is built around asynchronous packets. At the moment if the client misses a packet it completely ignore it. This is something that will need to be changed, but for now, it doesn’t seem to cause real problems.

The problem comes from my division between the world and combat server. At the time I though it would be clever to be able to use 2 computers for one server, and the 2 packet type (udp and tcp) at their best.

The world part is mostly movement and chat. Those 2 things can easily take a packet lost whit-out been catastrophic.

The combat part is built around turns and single actions. Each action has its own packet. A packet lost would make the player unable to follow the combat correctly. The idea would be to put an id on each action and increment this id for every action, so that if the client does lose a packet he can backtrack every missed action to get to the actual state.

The stupid thing I did was to put the world and combat into 2 different projects and act as if they would never be on the same computer. This is making thing really hard and don’t offer much as the 2 computer need to frequently send each other info, using tcp. I believe now it would be way easier to make each zone a separated server and put both the combat and world into the same computer. But as I do this I could also refactor my packet conception.

So to recap, here are my questions

Does String send into packet whit 3 digit code make sense or can I do
better?

Does my use of TCP and UDP make sense to you?

Should I have my world and combat server merge into one project?

What would spieder monkey do to help me at this point?

Thanks you :smiley:

So no answer so far :confused: I do underthant its pretty hard and complex stuff, but any little help would be greatly appreciated.

So maybe it will be easier if you only consider those one liner:

  1. Does String pre divise for regex send into packet whit 3 digit code make sense or can I do better?

  2. Does the use of both tcp and udp on a client make sence.

  3. What would spider monkey do to help me at this point? And how it does facing something like smartfoxserver

I was using the similar approach: 2 bytes for message type and ByteArray for the message itself, also sizes and delimiters were included. I’ve came up with really big dispatcher to handle that stuff. It had to deserialize all array with different rules to make the result instance and pass it to exact service. A lot of code to support, hard to add new message, and much logic in the simple transport layer.
There is another solution. Use messages for communications and serialization for transport.
In such case you will have class per packet type. Serializing lib will handle instanceOf for you. In fact you have to send message and receive it. That is it.

I’ve take Kryo, SpiderMonkey doing the same + it has Client and Server endpoints. Take a look to SpiderMonkey tutorials to get into this approach.
“Versioning” and supporting such structure is much more easier, and transport update will cost updating 1-few messages but not the whole package-processing-logic.

1 Like

Spidermonkey does object serialization/deserialization automatically, if this suits your design, you can use it and stop caring about packet parsing.

You can do better.
For example, the three digit id can be represented by 2 bytes (short) (or 1 if you can live with max 255 packets instead of 999) , but in your way you are using 3 bytes to represent it as an utf8 string.
Of course the same applies to all the other numeric data you are sending.

What i suggest is using bytes to represent your data and not strings, in this way packets will be lighter and you will parse it without involving regexes.
There are plenty ways to do it, see the java doc for those classes:
ByteArrayOutputStream/ByteArrayInputStream ByteBuffer DataOutputStream/DataInputStream
and read something about bit operators if you are not familiar with them.

It seems so.
But i’m not sure why you are talking about packet loss if you are using tcp for combat system. It already handles that.

I don’t see any reason to split them.
If you does this for having some sort of load balancing, just ask to your self: With all this cloud-magic we have today is it really necessary? Wouldn’t be easier to just scale the server?
In most cases it’s also cheaper…:wink:

1 Like

Okay so the main idea here is to send a serialise version of a class indead of a long caracter describing the action? But won’t that be heavier for the server?

It does look easier but can’t a really clean architecture of String builder/reader be as good? I guess am just getting scare cause that would mean reworking everything :chimpanzee_lobotized: But oh well, part of the learning :stuck_out_tongue:

I really love the byte instead of String part tho. I could easily give a byte ID to every packet, and also to every actor in the server, cutting my packet length by a couple of characters

The idea is that you need objects to process logic on server. In String case you create instances from the set of rules, what are described in your-packet-header(3 first chars, or 2 bytes, or other flags).
Those rules has to be written, maintain, changed, rewritten… and that is ok. This leads to rules-processing changes. Change of rules became complicated soon. It will require more time to make simple change. on every next step of changes. But the result of the rule-processing is concrete instance what will go to concrete game-service.

Spidermonkey take your instance, serialize it (convert to bytes), send to another endpoint, and deserialize it back to concrete instance. There are no rules open to you, this is not matter to you. It just know how to teleport instance to the server. Any instance.

If you have to add new message - you just add new class to serializer. at this step Spidermonkey know how to pass correct instance to server(or back). That mean you do not have to make new set of rules, to convert your string(byte)array to usable instance.

As the result - there are no overhead with changing your transport. your messages do not depend on other messages, and do not depend on packet-header, and there are no rules to depend on. Very flexible instead of arrayBased transport.

2 Likes

Well not really.
The idea is to send numbers and read numbers instead of converting them forth and back from strings.
Said that you can serialize a class (as spidermonkey does), or just build your byte buffer on the fly, it’s up to you.

It’s a lot lighter in terms of computation, since you don’t have to use regex or anything like that.

But it could make packets bigger in some circumstances.
Let’s say you want to send an integer (4 bytes): if the number is ‘1’, with your text protocol you’ll need 1 byte (utf8 char), while if the number is 123456 you will need 6.
If you use a binary protocol, an integer will always take 4 bytes, no matter what.
So you could see some text packets being smaller than their binary counterpart, but this is nothing compared to all the work you do for parsing them and it’s unpredictable, so even worse.

1 Like

…plus length or at least one extra terminator byte. If the length could handle any size string then that’s another 4 bytes.