I’m making a Flash game and am using TCP socket connections with it. I got it to connect with a simple AIR socket server, and though it does work alright, occasionally one of the clients will get a message that the other one won’t. It SHOULDN’T be packet loss, but I really don’t understand how one of them isn’t getting a message the other one is… how did I code it? Here’s how. This is part of the server:
function socketDataHandler(event:ProgressEvent):void
{
var socket:Socket = event.target as Socket;
var message:String = socket.readUTFBytes(socket.bytesAvailable);
for each (var socket:Socket in clientSockets)
{
if (message.indexOf("p1") >= 0)
{
msg1 = message;
socket.writeUTFBytes(msg1+msg2);
socket.flush();
continue;
}
if (message.indexOf("p2") >= 0)
{
msg2 = message;
socket.writeUTFBytes(msg1+msg2);
socket.flush();
continue;
}
The clients (p1 and p2) send messages whenever specific keys are pressed, moving their characters around. I have sort of a unique system as I couldn’t really find a way to send multiple individual signals after looking up on Google for WEEKS, but instead each player sends one single string of what keys are pressed. Those strings are determined by what combination of keys are being pressed and not pressed at a time. For example, if the down key and the left key are both being pressed, the string would be something like “p1_down0_down p1_left0_down p1_up0_up p1_right0_up p1_attack0_up”. That is the one message that sends from p1. For p2, it’s the same thing, only with p2 in place of p1. lol.
Here’s a part of the client’s programming:
if (msg.indexOf("p2_right0_down") >= 0)
{
p2_right = "down";
}
if (msg.indexOf("p2_left0_down") >= 0)
{
p2_left = "down";
}
Simple, huh? It uses indexOf to find parts of the string, and if said parts are found, it takes effect to the character’s keys being pressed, thus setting off the scripts in the game to move them around, attack, etc.
And get this… the characters’ movements do NOT take place UNTIL the signal has reached the client… that’s right, even the one pressing the keys in the first place. It will not move the character around until the messages have been sent to the server and then sent back to both the sender and the other client. And though this does work absolutely perfectly most of the time, occasionally if I try and press the keys fast in some certain combination, OCCASIONALLY the player will go off-course in one of the clients. How is this happening? Your guess is as good as mine because as far as my knowledge goes this shouldn’t be happening whatsoever… BOTH of the clients are getting the same messages… so if anyone were to go off-course, it would be with BOTH of the clients and then in the end the course would be the same thing… HOW ON EARTH is ONE of the clients not responding correctly?? This simply does not make any sense to me…
Both clients aren’t necessarily getting the same messages. TCP isn’t even designed to itself support discrete “messages” per se, just a continuous, order-guaranteed stream of bytes. So one of your clients may see 'p1x=4;p2x=7;' in a ProgressEvent, then 'p1x=50;p2x=0;' in a second, later ProgressEvent. The other client may see 'p1x=4;p2x=7;p1x=50;p2x=0;'in a single ProgressEvent.
The problem with your message parsing is that it uses an existence test, indexOf, which will lose information if the socket combines two of what you’re thinking of as messages. So, one way to address the problem is to count the number of p2_right0_downs in the string. (You’re also not guaranteed that Flash won’t chunk the delivery of your data in ways you aren’t expecting, like splitting messages in two (but for small messages you’ll likely be safe), because it can send data before you call flush.)
Generally, if you want to do messages over TCP, you should have a stricter message format. People will often do something like have the first N bytes hold the length of the message, so you know where the message ends (and whether or not you have the whole thing yet (or the whole thing + another (sometimes partial) message).
Edit: Sockets can be pretty frustrating to work with, given how low-level and old the technology is… so I’m not trying to excuse them from being confusing in my post above. But they’ve survived for so long because they’re useful and reflect how things work under the hood.
Thank you for the info, how would I be able to count the number of "p2_right0_down"s (or whatever) in the string, and how would that be a way to address the issue? I don’t quite yet understand how that would be a fix.
And as for the stricter message format, how would one do that? I’ve looked up and asked around forums allover the web with no straight answers. Thank you.
var msg = 'p2_right0_down slkdjfslkdjf p2_right0_down'
msg.match(/p2_right0_down/g).length // 2
Then you’d know that you’re getting two messages in one ProgressEvent, so you can do two of whatever action p2_right0_down represents. Like if it makes player 2 jump, then have them jump twice, or whatever. The disparity you’re seeing between clients is that they don’t get identical ProgressEvents due to the batching/clumping that’s sometimes seen when reading from sockets.
Right now your code acts like this:
Client 1:
Sees ProgressEvent with 'p2_right0_down'
Jumps
Sees ProgressEvent with 'p2_right0_down'
Jumps
(2 jumps total)
Client 2:
Sees ProgressEvent with 'p2_right0_down p2_right0_down'
Jumps once because indexOf sees only a single copy.
(1 jump total)
I think you want it to act like it does below, which is what using a count (or match length) would do instead of indexOf:
Client 1:
Sees ProgressEvent with 'p2_right0_down'
Jumps
Sees ProgressEvent with 'p2_right0_down'
Jumps
(2 jumps total)
Client 2:
Sees ProgressEvent with 'p2_right0_down p2_right0_down'
Jumps twice because match sees both.
(2 jumps total)
One thing you could use is Socket’s readObject/writeObject methods along with registerClassAlias. You still run into the message parsing problem, but someone on SO has described a way to solve that:
Another thing to do might be to use an XMLSocket instead of a Socket, because unlike regular sockets, XMLSockets will wait to send you data events until a complete XML document has been received.
XML is what Flash supports natively, but you have lots of choices as far as data formats go:
Edit: Ah, here’s someone using newline to delineate messages in an AIR socket server:
Thank you very much, I’m currently just attempting the match method, and though I got it to work obviously, it didn’t fix the issue, though it doesn’t surprise me BECAUSE when two messages are mixed together at once, they can contradict each other.
Here’s an example, let’s say none of the keys are being pressed by the player. This is what the message ends up being:
p1_down0_up p1_up0_up p1_attack0_up p1_left0_up p1_right0_up p1_block0_up p1_special10_up p1_special20_up
But let’s say the player pressed down on the “attack” key. Now if both the “everything is up” message sent as well as the “the attack key is down” message and they both ended up mixing into one SINGLE message… obviously that wouldn’t read correctly, because the client is reading BOTH “p1_attack0_up” AND “p1_attack0_down”, and ultimately the character would go off-course in comparison to the other client’s. What would be the fix to this? Again thank you for all of the info, I’m getting closer to the solution thanks to you.
I don’t know if I would say that any mixing really occurs. Just based on the left-to-right order of the message contents, if you run into anything that seems like a contradiction, you know that you’re receiving a message consisting of multiple messages. If you apply the effects of the keys/buttons in the right order, you’re not introducing any inconsistencies. (Unless time is involved, like buttons doing different things while in midair, in which case you’re in trouble anyway.)
Think of a game like chess. If you had an TCP-combined message that sounded contradictory, like “mousewheel is going up. mousewheel is going down”, you wouldn’t choke on that message, you’d just move a piece up and then move it back down again (if that’s what the mousewheel controlled). Again, things are trickier if any sort of time-based simulation is involved.
Networked multiplayer games have always run into this problem, and a typical solution is to have the server keep track of and periodically broadcast the game-relevant pieces of state that it knows about. It’s also how people prevent a common type of cheating.
The underlying time references used by TrueTime
are GPS and atomic clocks. TrueTime uses two forms
of time reference because they have different failure
modes. GPS reference-source vulnerabilities include antenna
and receiver failures, local radio interference, correlated
failures (e.g., design faults such as incorrect leapsecond
handling and spoofing), and GPS system outages.
Atomic clocks can fail in ways uncorrelated to GPS and
each other, and over long periods of time can drift significantly
due to frequency error.
TrueTime is implemented by a set of time master machines
per datacenter and a timeslave daemon per machine.
The majority of masters have GPS receivers with
dedicated antennas; these masters are separated physically
to reduce the effects of antenna failures, radio interference,
and spoofing. The remaining masters (which
we refer to as Armageddon masters) are equipped with
atomic clocks. An atomic clock is not that expensive:
the cost of an Armageddon master is of the same order
as that of a GPS master. All masters’ time references
are regularly compared against each other. Each master
also cross-checks the rate at which its reference advances
time against its own local clock, and evicts itself
if there is substantial divergence. Between synchronizations,
Armageddon masters advertise a slowly increasing
time uncertainty that is derived from conservatively applied
worst-case clock drift. GPS masters advertise uncertainty
that is typically close to zero.
Creating engaging and entertaining content for designers and developers since 1998.