• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

Legacy GM UDP Networking

Hello!

So, I've been using Gamemaker for a good long while and have made many projects and a few games. Now I'm looking to expand my horizons and work on a multiplayer game. As such, I followed RealTutsGML tutorial on TCP networking. I came to understand it and I was able to write and send my own messages through the buffer beyond just what the tutorial was doing. However, I'm not really into the idea of port forwarding and all that stuff and so I was looking to make a game that supported UDP Hole Punching. Since I am completely new to networking, I don't really understand UDP hole punching, but I did see that GMnet had a UDP hole punching extension that can be used with an already-built UDP netcode. As such, I set out to convert my TCP connection to UDP. Now, I do understand (as FrostyCat has mentioned various times on the community) that converting from TCP to UDP is not as simple as just changing all the commands from TCP to UDP in all your code and so I followed this basic tutorial to convert my netcode with. I have deleted all the network connecting and disconnecting code and am just working with UDP sockets. So far, the server and the first client are communicating, however, when a second client "connects" (receives a socket and sends its first message to the server), it is, as far as I can tell, receiving the same socket as the first client such that whenever the server sends a message to a particular client, it's just going to both clients.

A server-side object is created to represent all the connected clients and a ds map is used to store the IDs of those clients on the server side. When a message is received from a client, if the client is not in that ds map, it will create a server client object for that socket id and then store that object's id in the ds map under the socket id. But this only works for the first client that "connects". All subsequent clients that "connect" are just treated as if they had the same socket as the first client.

Any help as to what is going on would be highly appreciated as I am very new to this and I cannot find much information on UDP connections in Gamemaker at all.

Here is some relevant code to the problem I am having:

Creating the server:
GML:
///server_create(port)

// Init argument variables
var port = argument0;
// Init misc variables
var socket;

// Create client list
clientmap = ds_map_create();
client_id_counter = 0;

// Create server
socket = network_create_socket_ext(network_socket_udp, port);

// Create buffer
send_buffer = buffer_create(256, buffer_fixed, 1);

// Show error if server socket creation failed
if (socket < 0)
    show_error("Could not Create Server Socket!", true);

// Return server value
return socket;
Client joining:
GML:
///client_connect(ip, port, name)

// Init argument variables
var ip = argument0;
var port = argument1;
var name = argument2;

// Connect socket to server | This seems to be returning the same socket for all clients???
socket = network_create_socket(network_socket_udp);

// Init buffer
send_buffer = buffer_create(256, buffer_fixed, 1);
   
// Create client list
clientmap = ds_map_create();


// Set the client object flag that tells the clietn to proceed with connection.
var ConnectSuccess = 1; //network_connect_raw(socket, ip, port);

// Send name to server
buffer_seek(send_buffer, buffer_seek_start, 0);
buffer_write(send_buffer, buffer_u8, MESSAGE_JOIN);
buffer_write(send_buffer, buffer_string, name);

// Send raw message
network_send_udp_raw(socket, ip, port, send_buffer, buffer_tell(send_buffer));
Receive messages from the buffer (Networking event):
GML:
// Run net codes
//if (ds_exists(async_load, ds_type_map))
//{
    switch(async_load[? "type"])
    {
        //case network_type_connect:
        //case network_type_non_blocking_connect:
            //server_handle_connect(async_load[? "socket"]);
            //break;
        case network_type_data:
            server_handle_message(async_load[? "id"], async_load[? "buffer"]);
            break;
        //case network_type_disconnect:
            //server_handle_disconnect(async_load[? "socket"]);       
            //break;
    }
//}
Process messages sent from clients to the server:
GML:
///server_handle_message(socket_id, buffer);

// Init argument variables
var socket_id = argument0;
var buffer = argument1;
// get message type
var message_id = buffer_read(buffer, buffer_u8);
// Init misc variables
var clientObject = server_get_client(socket_id);
var client_id_current = clientObject.client_id;

// Loop when data is sent
while (true)
{
   
    // Determie the type of message received
    switch (message_id)
    {
        // Handle moving
        case MESSAGE_MOVE:
            // Get new position
            var new_x = buffer_read(buffer, buffer_u16);
            var new_y = buffer_read(buffer, buffer_u16);
            var new_image_speed = buffer_read(buffer, buffer_f32);
            var new_image_xscale = buffer_read(buffer, buffer_f32);
            var new_image_index = buffer_read(buffer, buffer_u8);
            // Return to the beginning of the buffer
            buffer_seek(send_buffer, buffer_seek_start, 0);
            // Write new position out to all players
            buffer_write(send_buffer, buffer_u8, MESSAGE_MOVE);
            buffer_write(send_buffer, buffer_u16, client_id_current);
            buffer_write(send_buffer, buffer_u16, new_x);
            buffer_write(send_buffer, buffer_u16, new_y);
            buffer_write(send_buffer, buffer_f32, new_image_speed);
            buffer_write(send_buffer, buffer_f32, new_image_xscale);
            buffer_write(send_buffer, buffer_u8, new_image_index);
            // Send data to the server clients
            with (oServerClient)
            {
                if (client_id != client_id_current)
                {
                    // For everyone else, send the packet
                    network_send_udp_raw(self.socket_id, remote_ip, remote_port, other.send_buffer, buffer_tell(other.send_buffer));
                }   
            }
            break;
        // Handle join
        case MESSAGE_JOIN:
            // Get username
            username = buffer_read(buffer, buffer_string);
            clientObject.name = username;
           
            // Send username out
            buffer_seek(send_buffer, buffer_seek_start, 0);
            buffer_write(send_buffer, buffer_u8, MESSAGE_JOIN);
            buffer_write(send_buffer, buffer_u16, client_id_current);
            buffer_write(send_buffer, buffer_string, username);           
            // Send data to the server clients
            with (oServerClient)
            {
                if (client_id != client_id_current)
                {
                    // For everyone else, send the packet
                    network_send_udp_raw(self.socket_id, remote_ip, remote_port, other.send_buffer, buffer_tell(other.send_buffer));
                }   
            }
           
            // Send the newly joined client name of all other clients
            with(oServerClient)
            {
                if (client_id != client_id_current)
                {
                    // Send username out
                    buffer_seek(other.send_buffer, buffer_seek_start, 0);
                    buffer_write(other.send_buffer, buffer_u8, MESSAGE_JOIN);
                    buffer_write(other.send_buffer, buffer_u16, client_id);
                    buffer_write(other.send_buffer, buffer_string, name);
                    network_send_udp_raw(self.socket_id, remote_ip, remote_port, other.send_buffer, buffer_tell(other.send_buffer));
                }
            }           
            break;
        // Handle moving
        case MESSAGE_CREATE_BULLET:
            // Get new position
            var index = buffer_read(buffer, buffer_u16);
            var new_x = buffer_read(buffer, buffer_u16);
            var new_y = buffer_read(buffer, buffer_u16);
            var new_speed = buffer_read(buffer, buffer_f32);
            var new_direction = buffer_read(buffer, buffer_f32);
            // Return to the beginning of the buffer
            buffer_seek(send_buffer, buffer_seek_start, 0);
            // Write new position out to all players
            buffer_write(send_buffer, buffer_u8, MESSAGE_CREATE_BULLET);
            buffer_write(send_buffer, buffer_u16, index);
            buffer_write(send_buffer, buffer_u16, new_x);
            buffer_write(send_buffer, buffer_u16, new_y);
            buffer_write(send_buffer, buffer_f32, new_speed);
            buffer_write(send_buffer, buffer_f32, new_direction);
            // Send data to the server clients
            with (oServerClient)
            {
                if (client_id != client_id_current)
                {
                    // For everyone else, send the packet
                    network_send_udp_raw(self.socket_id, remote_ip, remote_port, other.send_buffer, buffer_tell(other.send_buffer));
                }   
            }
            break;
    }
       
    // Quit if the buffer size is reached
    if (buffer_tell(buffer) == buffer_get_size(buffer))
        break;   
}
Create a new server client representation object if this is the first time the server has received a message from this socket. Otherwise, return the object id associated with this socket:
GML:
///client_get_object(socket_id)

// Init argument variables
var socket_id = argument0;
  
// Have we received a message from this jerk before?
if (ds_map_exists(clientmap, string(socket_id)))
{
    // Get the instance id of the client
    return clientmap[? string(socket_id)];
}
else
{
    // Init misc variables
    ServerClientID = instance_create(0,0,oServerClient);
    ServerClientID.socket_id = socket_id;
    ServerClientID.client_id = client_id_counter;
    ServerClientID.remote_port = async_load[? "port"];
    ServerClientID.remote_ip = async_load[? "ip"];
    //show_message(string(ServerClientID.remote_ip) + ", " + string(ServerClientID.remote_port) + ", " + string(ServerClientID.socket_id))
    // Increment counter
    client_id_counter++;
   
    // Wrap counter
    if (client_id_counter >= 65000)
    {
        client_id_counter = 0;
    }
   
    // Client map
    clientmap[? string(socket_id)] = ServerClientID;
   
    // Return the client id
    return ServerClientID;
}
 
Last edited:
Top