Gravityhamster48
Member
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:
Client joining:
Receive messages from the buffer (Networking event):
Process messages sent from clients to the server:
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:
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;
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));
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;
}
//}
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;
}
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: