You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
AM2R-Multitroid/scripts/nat_step_hole_punching.gml

120 lines
4.6 KiB

/// nat_step_hole_punching()
// Handles hole punching in Step event
// Call this from oClient Step event when using NAT traversal
if (!use_nat_traversal) exit;
// Only run hole punching if in the PUNCHING state
if (nat_state != NAT_STATE_PUNCHING) exit;
nat_hole_punch_timer++;
// If WE are symmetric, create 256 random UDP sockets (birthday paradox)
if (nat_type == NAT_TYPE_SYMMETRIC && !nat_random_sockets_created) {
for (var i = 0; i < 256; i++) {
var random_socket = network_create_socket_ext(network_socket_udp, 0);
if (random_socket >= 0) {
ds_list_add(nat_random_sockets, random_socket);
}
}
nat_random_sockets_created = true;
}
// Send hole punch packet every NAT_HOLE_PUNCH_INTERVAL steps
if (nat_hole_punch_timer >= NAT_HOLE_PUNCH_INTERVAL) {
nat_hole_punch_timer = 0;
nat_hole_punch_count++;
// Determine punch strategy based on OUR NAT type and PEER's NAT type
if (nat_type == NAT_TYPE_SYMMETRIC) {
// WE are symmetric - send from all 256 random sockets to peer's known port
// This creates 256 different source ports, leveraging birthday paradox
var socket_count = ds_list_size(nat_random_sockets);
for (var i = 0; i < socket_count; i++) {
var random_socket = ds_list_find_value(nat_random_sockets, i);
nat_send_hole_punch(random_socket, nat_peer_ip, nat_peer_port);
}
} else if (nat_peer_nat_type == NAT_TYPE_SYMMETRIC) {
// PEER is symmetric - we scan random ports to find their dynamic port
for (var i = 0; i < 100; i++) {
var random_port = irandom_range(1024, 65535);
nat_send_hole_punch(socket, nat_peer_ip, random_port);
}
} else {
// Neither is symmetric - simple bidirectional punch to known port
nat_send_hole_punch(socket, nat_peer_ip, nat_peer_port);
}
// After receiving enough packets, confirm connection (only once)
if (nat_hole_punch_received >= NAT_HOLE_PUNCH_THRESHOLD && !nat_hole_punch_confirmed) {
// Send confirmation to mediation server
var params = ds_map_create();
ds_map_add(params, "ConnectionID", nat_connection_id);
ds_map_add(params, "IsServer", false);
nat_send_tcp_message(mediation_socket, MSG_RECEIVED_PEER, params);
ds_map_destroy(params);
nat_hole_punch_confirmed = true;
// Connection is established - update state
nat_state = NAT_STATE_CONNECTED;
server_ip = nat_peer_ip;
server_port = nat_peer_port;
isConnected = 1;
connected = true; // Mark as connected to prevent duplicate connection flow in Step event
popup_text("Connected!");
// If symmetric NAT and we found a successful socket, switch to it NOW before sending initial packet
if (nat_successful_socket != noone) {
// Destroy old main socket
network_destroy(socket);
// Switch to successful socket
socket = nat_successful_socket;
// Clean up remaining random sockets
for (var i = ds_list_size(nat_random_sockets) - 1; i >= 0; i--) {
var sock = ds_list_find_value(nat_random_sockets, i);
network_destroy(sock);
}
ds_list_clear(nat_random_sockets);
}
// Send initial connection packet to server
buffer_delete(buffer);
var size, type, alignment;
size = 1024;
type = buffer_grow;
alignment = 1;
buffer = buffer_create(size, type, alignment);
buffer_seek(buffer, buffer_seek_start, 0);
buffer_write(buffer, buffer_u8, 1);
buffer_write(buffer, buffer_string, name + "," + global.multitroid_version);
buffer_write(buffer, buffer_u8, global.sax);
var bufferSize = buffer_tell(buffer);
buffer_seek(buffer, buffer_seek_start, 0);
buffer_write(buffer, buffer_s32, bufferSize);
buffer_write(buffer, buffer_u8, 1);
buffer_write(buffer, buffer_string, name + "," + global.multitroid_version);
buffer_write(buffer, buffer_u8, global.sax);
network_send_udp(socket, server_ip, server_port, buffer, buffer_tell(buffer));
// Create nametag instance
instance_create(x, y, oNametag);
// Disconnect from mediation server
if (mediation_socket != -1) {
network_destroy(mediation_socket);
mediation_socket = -1;
mediation_connected = false;
}
}
// Timeout after too many attempts
if (nat_hole_punch_count > 300) { // 30 seconds
popup_text("Connection timeout");
nat_state = NAT_STATE_ERROR;
}
}