GMS 2 [HELP] LAN works perfectly, WAN is really buggy and weird

LDinos

Member
Hello! I have been making a 3-match game and I recently added online functionality. After I read the documentation about making servers, I finally managed to make a LAN gamemode, and I saw that it worked perfectly! So it was time to port forward my ip and give it to somewhere else to try it with me. And it was a disaster. When the objects spawned, buffers went crazy, some random info was given instead of the intented (for example, when sent the info for the points, the info for what gems to spawn were given instead). I mostly want to know WHY this is happening rather HOW to fix it. I believe if I find out why this happens, I will be able to do some research to my code :)

Here is some info for my obj_server and obj_client

(obj_server)

Code:
//Create Event//
ip = "0.0.0.0"
client_socket = noone
server_socket = network_create_server(network_socket_tcp, 6969, 2);
client_connected = false
buffer = buffer_create(1,buffer_grow,1)
(obj_client)

Code:
//Create Event//
ping = 0
network_set_config(network_config_connect_timeout,3000)
textmsg = ""
client_socket = network_create_socket(network_socket_tcp);
server = network_connect(client_socket , string(global.ip), 6969);
buffer = buffer_create(1,buffer_grow,1)

In the Async networking event I use whatever the tutorial says, and in the network_type_data I run a script that gets the info and it looks like this:
Code:
var buffer = argument0
var msg = buffer_read(buffer,buffer_u8)
switch msg
{
    case PLAYER_POS:
        player2.x = buffer_read(buffer,buffer_u16)
        player2.y = buffer_read(buffer,buffer_u16)
        player2.xlim = buffer_read(buffer,buffer_u8)
        player2.ylim = buffer_read(buffer,buffer_u8)
        .
        .
        .
.....}
etc, where PLAYER_POS is a macro.

And when I send buffer info it looks like this:
Code:
            buffer_resize(buffer,1) buffer_seek(buffer,buffer_seek_start,0)
            buffer_write(buffer,buffer_u8,CONSTANT) //2
            buffer_write(buffer,buffer_u16,whateverinfo)
            network_send_packet(client_socket,buffer,buffer_tell(buffer))
If you need to know anything else for my project go ahead and ask me and I will answer :)
Video of how my project looks :
 
Last edited:
K

kevins_office

Guest
Sounds to me like a packet ordering issue.
It is expected that data packets received over the internet can come out of order.
You have to re-assemble received packets back into the correct order after receiving them all.
Or have your code be able to work with packets in any order.
 

LDinos

Member
Sounds to me like a packet ordering issue.
It is expected that data packets received over the internet can come out of order.
You have to re-assemble received packets back into the correct order after receiving them all.
Or have your code be able to work with packets in any order.
Can you help me with what you mean by "putting recieved packets back in order"? Maybe a code example on how to do it. Thanks :)
 

Humayun

Member
I don't think its order issue because you are already using TCP which takes are of ordering and packet loss. TCP is slow because of its reliability try debugging packets. Are you using same packet more than a time? or are you receiving delay between packets and then all packets at once? There are some things you need to do your self, if still issue doesn't fix post your debug info about packets.
 

LDinos

Member
I don't think its order issue because you are already using TCP which takes are of ordering and packet loss. TCP is slow because of its reliability try debugging packets. Are you using same packet more than a time? or are you receiving delay between packets and then all packets at once? There are some things you need to do your self, if still issue doesn't fix post your debug info about packets.
I will try to explain what happens, and it might be able to answer your question aswell. In the video I provided, do you see how the gems are spawned all together? In online multiplayer, each player sends info for his gems on his board. Player 1 sends info from the 64 gems from his board, and player 2 from the other board. When joining the server, I do NOT have the gems spawned until a button from the host is clicked. Before the button is clicked, only the indicator can be moved, and it works both in LAN and WAN with no problems. After the button is pressed, 64 gems are spawned in one board and 64 from the other in which all of them send info for their x and y and/or whatever other properties they have. THIS is when the problem happens. So by your reply, does spawning all the gems at once create this problem?
 
L

Lonewolff

Guest
Sounds to me like a packet ordering issue.
It is expected that data packets received over the internet can come out of order.
You have to re-assemble received packets back into the correct order after receiving them all.
Or have your code be able to work with packets in any order.
It's TCP. Packets will always come in the correct order.

However, TCP is a stream protocol. So something that is sent in one call may take multiple receive calls to come in in its entirety. With this in mind, you need to make sure that you have received the amount of data that you are expecting or you may be working with partial data.
 

DukeSoft

Member
See if you're exceedig a packet size of 1500 bytes.. I'm not sure how GM handles this but there's a ton of layers in between on the internet that can split up TCP packets. I'm using TCP over internet and its working fine (and in order!) Thing is that my packets barely exceed 100bytes. I just send tons of little packages.
 

LDinos

Member
See if you're exceedig a packet size of 1500 bytes.. I'm not sure how GM handles this but there's a ton of layers in between on the internet that can split up TCP packets. I'm using TCP over internet and its working fine (and in order!) Thing is that my packets barely exceed 100bytes. I just send tons of little packages.
Sorry for not providing this video earlier but in case you want to see what happens in live action, here it is
Can you understand whats wrong with what is happening in here?
 

LDinos

Member
Can you show me the packets you're building and reading for the lines of gems?
Yes, right below is only the receiving packet code that has to do with the gems.
CL_gem object is a fake gem that has no code except for one variable that connects it to the real gem from the other player and so gets its x and y changed. In the last video for example, when I am hosting, the gems you see to the other player's board is CL_gem.
Gem_1 are the gems in the first board
Gem_2 represents the gems of the second board
Each gem has its own id when spawned (from 1 to 64) and that CL_gem gets that id aswell. So if something changes from a gem, the gem sends its id, checks which CL_gem has that id, and if the condition is true, the specific CL_gem changes

The reason why I used "if instance_exists(obj_client) else if instance_exists(obj_server)" is not visible for these things below, but some other buffers give different info depending if im the host or the client (second player).

Code:
var buffer = argument0
var msg = buffer_read(buffer,buffer_u8)
if instance_exists(obj_client) //if I am the second player
{
    switch msg
    {
        case GEM_CREATION:
            xx = buffer_read(buffer,buffer_u16)
            yy = buffer_read(buffer,buffer_u16)
            xxyyskin = buffer_read(buffer,buffer_u8)
            xxyyid = buffer_read(buffer,buffer_u8)
            xxyycolor = find_color(xxyyskin)
            xxyygem = instance_create_depth(xx,yy,-1,CL_gem)
            xxyygem.myid = xxyyid
            xxyygem.mycolor = xxyycolor
        break;
      
        case GEM_MATCHUP:
            xx = buffer_read(buffer,buffer_u16)
            yy = buffer_read(buffer,buffer_u16)
            xxyyskin = buffer_read(buffer,buffer_u8)
            xxyyid = buffer_read(buffer,buffer_u8)
            xxyypower = buffer_read(buffer,buffer_u8)
            with(CL_gem)
            {
            if myid == other.xxyyid
                {  
                    x = other.xx
                    y = other.yy
                    image_index = other.xxyyskin
                    gempower = other.xxyypower
                }
            }      
        break;
      
        case GEM_DIE:
            xxyyid = buffer_read(buffer,buffer_u8)
            with(CL_gem)
            {
                if myid = other.xxyyid instance_destroy()
            }
        break;
      
        case INITIATE_SPAWN:
            for(i=0;i<=7;i++)
            {
                instance_create(Board_2.x + i*64, Board_2.y, Gem_2)
            }
        break;
    }
  
}
else if instance_exists(obj_server) //if im the host
{
    switch msg
    {
    case GEM_CREATION:
        xx = buffer_read(buffer,buffer_u16)
        yy = buffer_read(buffer,buffer_u16)
        xxyyskin = buffer_read(buffer,buffer_u8)
        xxyyid = buffer_read(buffer,buffer_u8)
        xxyycolor = find_color(xxyyskin)
        xxyygem = instance_create_depth(xx,yy,-1,CL_gem)
        xxyygem.myid = xxyyid
        xxyygem.mycolor = xxyycolor
        break;

    case GEM_MATCHUP:
        xx = buffer_read(buffer,buffer_u16)
        yy = buffer_read(buffer,buffer_u16)
        xxyyskin = buffer_read(buffer,buffer_u8)
        xxyyid = buffer_read(buffer,buffer_u8)
        xxyypower = buffer_read(buffer,buffer_u8)
        with(CL_gem)
        {
        if myid == other.xxyyid
            {  
                x = other.xx
                y = other.yy
                image_index = other.xxyyskin
                gempower = other.xxyypower
            }
        }
      
        break;

    case GEM_DIE:
        xxyyid = buffer_read(buffer,buffer_u8)
        with(CL_gem)
        {
            if myid = other.xxyyid instance_destroy()
        }
        break;
    }
}
Gem_1 and Gem_2 Creation event
Code:
with(obj_server)/with(obj_client) //<- Gem_1 uses obj_server, Gem_2 obj_client
        {
            buffer_resize(buffer,1) buffer_seek(buffer,buffer_seek_start,0)
            buffer_write(buffer,buffer_u8,GEM_CREATION) //2
            buffer_write(buffer,buffer_u16,other.x)
            buffer_write(buffer,buffer_u16,other.y)
            buffer_write(buffer,buffer_u8,other.skinnum) //2 x y skin
            buffer_write(buffer,buffer_u8,other.myid) //2 x y skin creation_id
            network_send_packet(client_socket,buffer,buffer_tell(buffer))
        }
Gem_1/Gem_2 Step event
Code:
with(obj_server)/with(obj_client)
        {
            buffer_resize(buffer,1) buffer_seek(buffer,buffer_seek_start,0)
            buffer_write(buffer,buffer_u8,GEM_MATCHUP)
            buffer_write(buffer,buffer_u16,other.x)
            buffer_write(buffer,buffer_u16,other.y)
            buffer_write(buffer,buffer_u8,other.skinnum)
            buffer_write(buffer,buffer_u8,other.myid)
            buffer_write(buffer,buffer_u8,other.gempower)
            network_send_packet(client_socket,buffer,buffer_tell(buffer))
        }
Gem_1/Gem_2 Destroy event
Code:
with(obj_server)/with(obj_client)
{  
    buffer_resize(buffer,1) buffer_seek(buffer,buffer_seek_start,0)
    buffer_write(buffer,buffer_u8,GEM_DIE)
    buffer_write(buffer,buffer_u8,other.myid)
    network_send_packet(client_socket,buffer,buffer_tell(buffer))
}
 

jaydee

Member
It's TCP. Packets will always come in the correct order.

However, TCP is a stream protocol. So something that is sent in one call may take multiple receive calls to come in in its entirety. With this in mind, you need to make sure that you have received the amount of data that you are expecting or you may be working with partial data.
@LDinos I would suggest this be something you need to look into. Especially since you know the code works on LAN, so most likely what you are experiencing is the result of sending data over a larger network, which immediately makes the performance of sockets far worse. Luckily TCP does most of the work for you, you've just gotta make sure the data is what you expect before your process it.
 

DukeSoft

Member
Okay - just to be sure, can you put "buffer_seek(buffer,buffer_seek_start,0)" on receiving a buffer?

What I find to be weird is that there's "lag" on the client side. Its like you're sending the X/Y and information of gems for every step, instead of just creation. I'll look into this more tonight, don't have much time now ^_^
 

DukeSoft

Member
And for now, it might be smart to completely log the contents of every buffer you're receiving (mainly those on client side). Because it looks like you're receiving gibberish buffers, and performing the wrong data on the wrong buffer (e.g. the message ID is wrong, and a random packet gets read as "gem spawn" or "score").
 

LDinos

Member
Will try using buffer seek after "var buffer = argument0" but I am not sure when I will be able to test it! (I have to find my friend to test it with him)
 

DukeSoft

Member
I'd be happy to help you test it (I could also read out the network packets client side). Another option might be using a phone hotspot and connecting a laptop to that. Will come back to this in an hour or 5 :)
 

DukeSoft

Member
Tried, but made no difference unfortunately :(
Allright. Sorry for not coming back to you yesterday, had a few unplanned events that popped up. If you're on tonight I'll contact you and we can test it out a little. I'll look at the network packets coming in / going out and see if there's anything in particular that could cause issues.
 

LDinos

Member
Allright. Sorry for not coming back to you yesterday, had a few unplanned events that popped up. If you're on tonight I'll contact you and we can test it out a little. I'll look at the network packets coming in / going out and see if there's anything in particular that could cause issues.
add me at anytime and we could set up when we can play together there :)
 

LDinos

Member
So to post my last message in this post, I have FIXED IT. With the help of my friend DukeSoft, it seemed like I was sending very large packets, usually on the step event. I had to find some smarter ways to send info and so I did.
 

descrubb

Member
So to post my last message in this post, I have FIXED IT. With the help of my friend DukeSoft, it seemed like I was sending very large packets, usually on the step event. I had to find some smarter ways to send info and so I did.
Two Years and One Day Later...

Any chance you could post a brief summary of how you went about finding and implementing the "smarter ways to send info"?
 

LDinos

Member
Two Years and One Day Later...

Any chance you could post a brief summary of how you went about finding and implementing the "smarter ways to send info"?
Yeah sure! In my game, instead of sending x and y values of my gems every step event, I instead let the client do the physics work. The only info I send is
* When a gem was destroyed, send a single u8 buffer that is the gem id (the other player knows which id it coresponds to and destroys it)
* When a swap happens just send the two u8 gem id buffers and let the client do the swap
* When a new gem spawns send a u8 skin info and a u8 collumn value and let the client spawn the gem on that collumn
* Add u16 point values when points change

That's basically it
 

descrubb

Member
Yeah sure! In my game, instead of sending x and y values of my gems every step event, I instead let the client do the physics work. The only info I send is
* When a gem was destroyed, send a single u8 buffer that is the gem id (the other player knows which id it coresponds to and destroys it)
* When a swap happens just send the two u8 gem id buffers and let the client do the swap
* When a new gem spawns send a u8 skin info and a u8 collumn value and let the client spawn the gem on that collumn
* Add u16 point values when points change

That's basically it

Ahh... wow! Did not expect that fast of a response :oops:

that all makes sense... very useful for me learning this stuff!

thank you!!
 
Top