working connection
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 28 Mar 2022 19:49:24 +0000 (14:49 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 28 Mar 2022 19:49:24 +0000 (14:49 -0500)
doc.md
src/host.onyx
src/module.onyx
src/packet.onyx
src/peer.onyx
src/protocol.onyx
udp_client.onyx
udp_server.onyx

diff --git a/doc.md b/doc.md
index ee9fd13d6addd6ba26b4be9cb4b0aa8cd814e1d9..b5760463c691957bcfd042132e5dfebac790d255 100644 (file)
--- a/doc.md
+++ b/doc.md
@@ -20,3 +20,9 @@ transferred in a different way, or chunked up by the application layer.
 
 
 
+Each packet is at most 1400 bytes in size.
+
+0        2                     4           6           8    ...
+| peerID | sentTime | checksum | channelID | commandID | data 
+           OPTIONAL   OPTIONAL
+
index dfce83b9f593345880bad685e1beb85a0fc8eba3..85ec3073a21265c950ea5fd3bb040c73614e416e 100644 (file)
 package onyx_net
 
+@CLEANUP
+use package core
+
 Host :: struct {
     socket : net.Socket;
     addr   : net.Socket_Address;
 
     peers  : [] Peer;
-    maximum_peers: u32;
+    connected_peers: u32;
+
+    mtu: u32;
+    current_time: u32;
+
+    // Transient data used during processing of an message.
+    packet_data: [2] [Host_Default_MTU] u8;
+    event: Event;
+    produced_event: bool;
+
+    received_data: [] u8;
+    received_addr: net.Socket_Address;
+
+    pulse      :: host_pulse;
+    get_events :: host_get_events;
+}
+
+#local Host_Creation_Error :: enum {
+    None;
+    Socket_Creation_Failed;
+    Socket_Binding_Failed;
+}
+
+host_create :: (addr: ^net.Socket_Address, peer_count: u32) -> (^Host, Host_Creation_Error) {
+    errored := false;
+    host := new(Host);
+    defer if errored do cfree(host);
+
+    memory.alloc_slice(^host.peers, peer_count);
+    defer if errored do memory.free_slice(^host.peers);
+    host.connected_peers = 0;
+
+    if socket, err := net.socket_create(.Inet, .Dgram); err != .None {
+        errored = true;
+        return null, .Socket_Creation_Failed;
+
+    } else {
+        host.socket = socket;
+    }
+    defer if errored do net.socket_close(^host.socket);
+
+    if addr != null {
+        if !host.socket->bind(addr.port) {
+            errored = true;
+            return null, .Socket_Binding_Failed;
+        }
+    }
+
+    host.socket->setting(.NonBlocking, 1);    
+    host.socket->setting(.Broadcast, 1);
+
+    host.addr = *addr;
+    host.mtu = Host_Default_MTU;
+
+    for^ peer: host.peers {
+        peer.host = host;
+        peer.state = .Disconnected;
+        peer.channels = .{ null, 0 };
+        peer.incoming_id = ~~((cast(u32) peer - cast(u32) host.peers.data) / sizeof Peer);
+        peer.outgoing_id = ~~((cast(u32) peer - cast(u32) host.peers.data) / sizeof Peer);
+        peer.mtu = host.mtu;
+    }
+
+    return host, .None;
+}
+
+host_connect :: (host: ^Host, addr: ^net.Socket_Address, channel_count: u32) -> ^Peer {
+    peer: ^Peer;
+    
+    // Find first peer that is in the disconnected state.
+    for^ host.peers do if it.state == .Disconnected {
+        peer = it;
+        break;
+    }
+
+    if peer == null do return null;
+
+    memory.alloc_slice(^peer.channels, channel_count);
+    peer.host = host;
+    peer.state = .Connecting;
+    peer.addr = *addr;
+    peer.connect_id = random.int();
+    peer.mtu = Host_Default_MTU;
+
+    for^ channel: peer.channels {
+        channel.id = ~~((cast(u32) channel - cast(u32) peer.channels.data) / 4);
+        channel.seq_number = 0;
+        channel.max_reliable_windows = 16;
+        memory.alloc_slice(^channel.reliable_windows, channel.max_reliable_windows);
+    }
+    
+    command := new(Protocol_Connect);
+    command.command = .Connect;
+    command.command |= .Flag_Acknowledge;
+    command.channel = 255;
+    command.outgoing_peer_id = net.host_to_network(peer.outgoing_id);
+    command.mtu = net.host_to_network(peer.mtu);
+    command.window_size = 0;
+    command.channel_count = net.host_to_network(channel_count);
+    command.connect_id = net.host_to_network(peer.connect_id);
+
+    peer_queue_outgoing_command(peer, command);
+    return peer;
+}
+
+host_free :: (host: ^Host) {
+    net.socket_close(^host.socket);
+    memory.free_slice(^host.peers);
+    cfree(host);
+}
+
+host_broadcast :: (host: ^Host, channel: Channel_ID, packet: ^Packet) {
+    for ^peer: host.peers {
+        if peer.state != .Connected do continue;
+
+        peer_send(peer, channel, packet);
+    }
+}
+
+host_pulse :: (host: ^Host, timeout: u32 = 0) -> bool {
+    host.current_time = ~~ os.time();
+
+    sent_command     := host_send_commands(host);
+    received_command := host_receive_commands(host, timeout);
+
+    return host.produced_event;
+}
+
+host_send_commands :: (host: ^Host) -> bool {
+    for ^peer: host.peers {
+        //
+        // Skip over peers that are dead or disconnected.
+        if peer.state == .Disconnected || peer.state == .Zombie {
+            continue;
+        }
+
+        //
+        // Have peers send their acknowledgements for messages they received.
+        // Currently, this will issue its own send() call. A better way is to
+        // pool the messages together using something like sendmsg(), but Onyx
+        // does not currently support that.
+        if peer.acknowledgements.count > 0 {
+            peer_send_acknowledgements(peer);
+        }
+
+        // TODO
+        // Peer pinging
+        // Packet loss calcuations
+        // Dropped packet detection
+
+        peer_flush_outgoing_commands(peer);
+    }
+}
+
+host_receive_commands :: (host: ^Host, timeout: u32 = 0) -> bool {
+    if timeout > 0 {
+        check_sockets := (^net.Socket).[ ^host.socket ];
+        changed_buffer := alloc.array_from_stack(i32, 1);
+        changed := net.socket_poll_all(check_sockets, timeout, changed_buffer);
+
+        if changed.count == 0 do return false;
+    }
+
+    buffer: [] u8 = host.packet_data[0];
+    recv_addr, recv_bytes := host.socket ->recvfrom(buffer);
+
+    host.received_addr = recv_addr;
+    host.received_data = buffer[0 .. recv_bytes];
+
+    return host_handle_incoming_commands(host);
+}
+
+host_handle_incoming_commands :: (host: ^Host) -> bool {
+    header: ^Protocol_Header = ~~ host.received_data.data;
+    current_data := host.received_data;
+    
+    peer_id := cast(u32) net.network_to_host(header.peer_id);
+    peer: ^Peer;
+
+    if peer_id == 0xffff ---   // a Peer Id of 0xffff is used for a newly connecting peer.
+    elseif peer_id >= host.peers.count || peer_id >= host.peers.count {
+        return false;
+
+    } else {
+        peer = ^host.peers[peer_id];
+    }
+
+    if peer != null {
+        peer.addr = host.received_addr;
+    }
+
+    string.advance(^current_data, sizeof typeof *header);
+
+    return_block :: macro () {
+        printf("DISCARDING: {}\n", peer_id);
+        return host.event.type != .None;
+    }
+
+    while current_data.count > sizeof Protocol_Command_Header {
+        if host.produced_event do break;
+        
+        command := cast(^Protocol_Command_Header) current_data.data;
+        command_id := command_get_effective(command.command);
+        printf("Received command: {} {*p}\n", command_sizes[command_id], cast(Command) command_id, command);
+        string.advance(^current_data, command_sizes[command_id]);
+
+        switch command_id {
+            case .Acknowledge {
+                if peer == null do return_block();
+                host_handle_acknowledge_command(host, peer, ~~ command);
+            }
+
+            case .Connect {
+                if peer != null do return_block();
+                peer = host_handle_connect_command(host, ~~ command);
+                if peer == null do return_block();
+            }
+
+            case .Verify_Connect {
+                if peer == null do return_block();
+                host_handle_verify_connect_command(host, peer, ~~ command);
+            }
+
+            case .Disconnect {
+            }
+
+            case .Ping {
+            }
+
+            case .Send_Reliable {
+                if peer == null do return_block();
+                host_handle_send_reliable_command(host, peer, ~~ command, ^current_data);
+            }
+
+            case .Send_Unreliable {
+            }
+
+            case .Send_Unsequenced {
+            }
+        }
+
+        if peer != null && (command.command & .Flag_Acknowledge) != 0 {
+            sent_time := net.network_to_host(header.sent_time);
+
+            switch peer.state {
+                case .Acknowledging_Disconnect {
+                    if command_id == .Disconnect {
+                        peer_queue_acknowledgement(peer, command, sent_time);
+                    }
+                }
+
+                case #default {
+                    peer_queue_acknowledgement(peer, command, sent_time);
+                }
+            }
+        }
+    }
+
+    host.event.peer = peer;
+    return host.produced_event;
+}
+
+host_get_events :: (host: ^Host, timeout: u32 = 0) -> Iterator(^Event) {
+    Context :: struct {
+        host: ^Host;
+        timeout: u32 = 0;
+    }
+
+    next :: (use ctx: ^Context) -> (^Event, bool) {
+        if host_pulse(host, timeout) {
+            host.produced_event = false;
+            return ^host.event, true;
+        }
+
+        return null, false;
+    }
+
+    ctx := new(Context);
+    ctx.host = host;
+    ctx.timeout = timeout;
+    return .{ ctx, next, null_proc };
+}
+
+#local host_notify_connect :: (host: ^Host, peer: ^Peer) {
+    host.event.type = .Connection;
+    host.event.peer = peer;
+    host.event.channel_id = 255; // Magic constant
+    host.event.data = .[];
+
+    host.produced_event = true;
+}
+
+#local host_notify_disconnect :: (host: ^Host, peer: ^Peer) {
+    host.event.type = .Disconnection;
+    host.event.peer = peer;
+    host.event.channel_id = 255; // Magic constant
+    host.event.data = .[];
+
+    host.produced_event = true;
+}
+
+#local host_notify_message :: (host: ^Host, peer: ^Peer, channel_id: u8, data: [] u8) {
+    host.event.type = .Message;
+    host.event.peer = peer;
+    host.event.channel_id = channel_id;
+    host.event.data = data;
+
+    host.produced_event = true;
+}
+
+#local host_handle_acknowledge_command :: (host: ^Host, peer: ^Peer, command: ^Protocol_Acknowledge) {
+    if peer.state == .Disconnected || peer.state == .Zombie do return;
+
+    recv_sent_time := cast(u32) net.network_to_host(command.recv_sent_time);
+    recv_sent_time |= host.current_time & 0xFFFF0000;
+    if (recv_sent_time & 0x8000) > (host.current_time & 0x8000) {
+        recv_sent_time -= 0x10000;
+    }
+
+    //
+    // If the acknowledgement is for a message in the future, ignore it.
+    if host.current_time < recv_sent_time {
+        return;
+    }
+
+    recv_seq_number := net.network_to_host(command.recv_seq_number);
+    removed_command_id := peer_remove_sent_reliable_command(peer, recv_seq_number, command.channel);
+    printf("Received acknowledgement for {}.\n", removed_command_id);
+
+    switch peer.state {
+        case .Acknowledging_Connection {
+            if removed_command_id == .Verify_Connect {
+                peer.state = .Connected;
+                host_notify_connect(host, peer);
+            }
+        }
+
+        case .Disconnecting {
+            if removed_command_id == .Disconnect {
+                host_notify_disconnect(host, peer);
+            }
+        }
+
+        case .Disconnect_Later {
+            if peer.outgoing_commands.count == 0 {
+                peer_disconnect(peer);
+            }
+        }
+    }
+}
+
+#local host_handle_connect_command :: (host: ^Host, command: ^Protocol_Connect) -> ^Peer {
+    peer: ^Peer;
+
+    for^ host.peers {
+        if it.state == .Disconnected {
+            peer = it;
+            break;
+
+        } elseif it.state == .Connecting && it.addr.addr == host.received_addr.addr {
+            if it.addr.port == host.received_addr.port && it.connect_id == command.connect_id do return null;
+        }
+    }
+
+    if peer == null do return null;
+
+    channel_count := net.network_to_host(command.channel_count);
+    memory.alloc_slice(^peer.channels, channel_count);
+    memory.set(peer.channels.data, 0, channel_count * sizeof Channel);
+
+    peer.state = .Acknowledging_Connection;
+    peer.connect_id = net.network_to_host(command.connect_id);
+    peer.addr = host.received_addr;
+    peer.outgoing_id = net.network_to_host(command.outgoing_peer_id);
+    peer.mtu = net.network_to_host(command.mtu);
+
+    verify := new(Protocol_Verify_Connect);
+    verify.command = .Verify_Connect;
+    verify.command |= .Flag_Acknowledge;
+    verify.channel = 255;
+    verify.channel_count = net.host_to_network(peer.channels.count);
+    verify.outgoing_peer_id = net.host_to_network(peer.incoming_id);
+    verify.mtu = net.host_to_network(peer.mtu);
+    verify.window_size = command.window_size;
+    verify.connect_id = net.host_to_network(peer.connect_id);
+
+    peer_queue_outgoing_command(peer, verify);
+
+    return peer;
 }
 
+#local host_handle_verify_connect_command :: (host: ^Host, peer: ^Peer, command: ^Protocol_Verify_Connect) {
+    if net.network_to_host(command.connect_id) != peer.connect_id do return;
 
+    channel_count := net.network_to_host(command.channel_count);
+
+    //
+    // These magic constants refer to the fact that the connection
+    // packet will have sequence number 1, and channel id 255.
+    peer_remove_sent_reliable_command(peer, 1, 255);
+
+    peer.channels.count = math.min(peer.channels.count, channel_count);
+    peer.outgoing_id = net.network_to_host(command.outgoing_peer_id);
+
+    peer.mtu = math.min(peer.mtu, net.network_to_host(command.mtu));
+    peer.window_size = math.min(peer.window_size, net.network_to_host(command.window_size));
+
+    peer.state = .Connected;
+    host_notify_connect(host, peer);
+    return;
+}
+
+//
+// This is slightly misnamed, as it is actually handling a received reliable message.
+#local host_handle_send_reliable_command :: (host: ^Host, peer: ^Peer, command: ^Protocol_Send, data: ^[] u8) {
+    host_notify_message(host, peer, command.channel, *data);
+    string.advance(data, ~~ command.data_length);
+}
+
+
+@Relocate
+Event :: struct {
+    Type :: enum {
+        None;
+        Connection;
+        Disconnection;
+        Message;
+    }
+
+    type: Type;
+    peer: ^Peer;
+    channel_id: Channel_ID;
+    data: [] u8;
+}
+
+#local {
+    Host_Default_MTU :: 1400
+}
index c07d1766fe89c9c36a437ae32724a324bf16b31f..6db6c8a8c1f3ad5c05e4cf914abaff1257f2004f 100644 (file)
@@ -1,10 +1,14 @@
 package onyx_net
 
 #package {
-    array :: package core.array
-    net   :: package core.net
-    list  :: package core.list
-    alloc :: package core.alloc
+    array  :: package core.array
+    net    :: package core.net
+    list   :: package core.list
+    alloc  :: package core.alloc
+    memory :: package core.memory
+    string :: package core.string
+    random :: package core.random
+    os     :: package core.os
 }
 
 #load_all "./."
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..77175c72453cdec52333ca4aaae9f67301a2187f 100644 (file)
@@ -0,0 +1,67 @@
+package onyx_net
+
+Outgoing_Command :: struct {
+    command: ^Protocol_Command_Header;
+    packet : ^Packet;
+    reliable_seq_number   : u16;
+    unreliable_seq_number : u16;
+    sent_time             : u16;
+    send_attempts         : u16;
+
+    pack_into_buffer :: outgoing_command_pack_into_buffer;
+}
+
+Incoming_Command :: struct {
+    command: ^Protocol_Command_Header;
+    packet : ^Packet;
+    reliable_seq_number   : u16;
+    unreliable_seq_number : u16;
+}
+
+Packet :: struct {
+    Flags :: enum #flags {
+        Reliable;
+        Unsequenced;
+    }
+
+    flags: Flags;
+    data: [] u8;
+}
+
+Acknowledgement :: struct {
+    sent_time: u16;
+    command: ^Protocol_Command_Header;
+}
+
+
+outgoing_command_pack_into_buffer :: (out: ^Outgoing_Command, peer: ^Peer, buf: [] u8) -> ([] u8, bool) {
+    write_offset := 0;
+    write :: macro (data: [] u8) {
+        if write_offset + data.count > buf.count do return .[], false;
+        memory.copy(buf.data + write_offset, data.data, data.count);
+        write_offset += data.count;
+    }
+
+    peer_id := net.host_to_network(peer.outgoing_id);
+    sent_time := net.host_to_network(cast(u16)(peer.host.current_time & 0xFFFF));
+
+    //
+    // Connect packets expect to have a peer id of FFFF.
+    // However, the outgoing_id needs to remain its original value.
+    if command_get_effective(out.command.command) == .Connect {
+        peer_id = 65535;
+    }
+
+    @HACK @HACK
+    write((cast(^u8) ^peer_id)[0 .. 2]);
+    write((cast(^u8) ^sent_time)[0 .. 2]);
+
+    command_size := command_sizes[~~ command_get_effective(out.command.command)];
+    write((cast(^u8) out.command)[0 .. command_size]);
+
+    if out.packet != null {
+        write(out.packet.data);
+    }
+
+    return buf[0 .. write_offset], true;
+}
index 483e9414a612646bffac13de035fee5daa5188e8..1bebba7f173431da031bb3d7ee0b243aed4d5023 100644 (file)
@@ -6,7 +6,7 @@ Peer_State :: enum {
     Acknowledging_Connection;
     Connection_Pending;
     Connection_Successed;
-    Connection;
+    Connected;
     Disconnect_Later;
     Disconnecting;
     Acknowledging_Disconnect;
@@ -19,11 +19,196 @@ Peer :: struct {
     state: Peer_State;
 
     channels: [] Channel;
+
+    incoming_id: u16;
+    outgoing_id: u16;
+    connect_id: u32;
+    mtu: u32;
+    window_size: u32;
+    last_send_time: u32;
+
+    outgoing_reliable_seq_number: u16;
+    incoming_reliable_seq_number: u16;
+    outgoing_unsequenced_group:   u32;
+
+    outgoing_commands: [..] ^Outgoing_Command;
+    incoming_commands: [..] ^Incoming_Command;
+    acknowledgements:  [..] Acknowledgement;
+    sent_reliable_commands: [..] ^Outgoing_Command;
+}
+
+peer_destroy :: (peer: ^Peer) {
+    memory.free_slice(^peer.channels);
+    array.free(^peer.outgoing_commands);
+    array.free(^peer.incoming_commands);
+}
+
+peer_disconnect :: (peer: ^Peer) {
+    if peer.state == .Disconnected do return;
+
+    peer.state = .Disconnected;
+
+    peer_destroy(peer);
+}
+
+peer_queue_outgoing_command :: #match {
+    (peer: ^Peer, command: ^Protocol_Command_Header, packet: ^Packet = null) -> ^Outgoing_Command {
+        out := new(Outgoing_Command);
+        out.command = command;
+        out.packet = packet;
+
+        peer_setup_outgoing_command(peer, out);
+        return out;
+    }
+}
+
+peer_setup_outgoing_command :: (peer: ^Peer, command: ^Outgoing_Command) {
+    if command.command.channel == 255 {
+        peer.outgoing_reliable_seq_number += 1;
+        command.reliable_seq_number = peer.outgoing_reliable_seq_number;
+        command.unreliable_seq_number = 0;
+
+    } else {
+        channel := ^peer.channels[command.command.channel];
+        
+        // Oof... That's a long chain of command.
+        if (command.command.command & .Flag_Acknowledge) != 0 {
+            channel.outgoing_reliable_seq_number += 1;
+            command.reliable_seq_number = channel.outgoing_reliable_seq_number;
+            command.unreliable_seq_number = 0;
+
+        } elseif (command.command.command & .Flag_Unsequenced) != 0 {
+            peer.outgoing_unsequenced_group += 1;
+            command.reliable_seq_number = 0;
+            command.unreliable_seq_number = 0;
+
+        } else {
+            channel.outgoing_unreliable_seq_number += 1;
+
+            command.reliable_seq_number = channel.outgoing_reliable_seq_number;
+            command.unreliable_seq_number = channel.outgoing_unreliable_seq_number;
+        }
+    }
+
+    command.send_attempts = 0;
+    command.sent_time = 0;
+    command.command.seq_number = net.host_to_network(command.reliable_seq_number);
+
+    peer.outgoing_commands << command;
+}
+
+peer_send :: (peer: ^Peer, channel_id: Channel_ID, packet: ^Packet) -> bool {
+    if peer.state != .Connected || ~~channel_id > peer.channels.count do return false;
+
+    channel := ^peer.channels[channel_id];
+
+    @TODO // Fragmented sending
+    if packet.data.count > peer.mtu - sizeof Protocol_Header - sizeof Protocol_Send do return false;
+
+    command := new(Protocol_Send);
+    command.channel = channel_id;
+    command.data_length = net.host_to_network(cast(u16) packet.data.count);
+
+    if packet.flags & .Reliable {
+        command.command = .Send_Reliable;
+        command.command |= .Flag_Acknowledge;
+    } elseif packet.flags & .Unsequenced {
+        command.command = .Send_Unsequenced;
+        command.command |= .Flag_Unsequenced;
+    } else {
+        command.command = .Send_Unreliable;
+    }
+
+    return peer_queue_outgoing_command(peer, command, packet) != null;
+}
+
+peer_queue_acknowledgement :: (peer: ^Peer, command: ^Protocol_Command_Header, sent_time: u16) {
+    peer.acknowledgements << .{ sent_time, command };
+}
+
+peer_flush_outgoing_commands :: (peer: ^Peer) -> i32 {
+    peer.last_send_time = peer.host.current_time;
+
+    send_buffer: [65535] u8;
+
+    total_sent := 0;
+    for peer.outgoing_commands {
+        to_send, success := it->pack_into_buffer(peer, send_buffer);
+        if !success do continue;
+
+        sent_bytes := peer.host.socket->sendto(to_send, ^peer.addr);
+        if sent_bytes < 0 {
+            return -1;
+        }
+
+        total_sent += sent_bytes;
+        peer.sent_reliable_commands << it;
+    }
+
+    array.clear(^peer.outgoing_commands);
+    return total_sent;
+}
+
+peer_send_acknowledgements :: (peer: ^Peer) {
+    send_buffer: [65535] u8;
+
+    for ^ack: peer.acknowledgements {
+        command: Protocol_Acknowledge;
+        command.command = .Acknowledge;
+        command.channel = ack.command.channel;
+        command.seq_number = 0;
+        command.recv_seq_number = ack.command.seq_number;
+        command.recv_sent_time = ack.sent_time;
+
+        out: Outgoing_Command;
+        out.command = ^command;
+        
+        to_send, success := out->pack_into_buffer(peer, send_buffer);
+        if !success do continue;
+
+        sent_bytes := peer.host.socket->sendto(to_send, ^peer.addr);
+        if sent_bytes < 0 do return;
+    }
+
+    array.clear(^peer.acknowledgements);
 }
 
+peer_remove_sent_reliable_command :: (peer: ^Peer, seq_num: u16, channel: Channel_ID) -> Command {
+    command: ^Outgoing_Command;
+    index := 0;
+    for peer.sent_reliable_commands {
+        if it.reliable_seq_number == seq_num && it.command.channel == channel {
+            command = it;
+            break;
+        }
+
+        index += 1;
+    }
+
+    if command == null do return .None;
+
+    defer {
+        cfree(command.command);
+        cfree(command);
+        array.fast_delete(^peer.sent_reliable_commands, index);
+    }
+    return command_get_effective(command.command.command);
+}
 
 
+Channel_ID :: u8;
+
 Channel :: struct {
+    id: Channel_ID;
     seq_number: u16;
-    
+
+    reliable_windows: [] u16;
+    max_reliable_windows: u32;
+
+    outgoing_unreliable_seq_number: u16;
+    outgoing_reliable_seq_number: u16;
+    incoming_reliable_seq_number: u16;
+
 }
+
+
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..76fa35240322fb0544449edf27cd4d9c3bd75d8e 100644 (file)
@@ -0,0 +1,74 @@
+package onyx_net
+
+Command :: enum (u8) {
+    None             :: 0;
+    Acknowledge      :: 1;
+    Connect          :: 2;
+    Verify_Connect   :: 3;
+    Disconnect       :: 4;
+    Ping             :: 5;
+    Send_Reliable    :: 6;
+    Send_Unreliable  :: 7;
+    Send_Unsequenced :: 8;
+
+    Flag_Acknowledge :: 1 << 6;
+    Flag_Unsequenced :: 1 << 7;
+}
+
+command_get_effective :: (n: Command) -> Command {
+    return ~~(n & 63);
+}
+
+Protocol_Header :: struct #pack {
+    peer_id: u16;
+    sent_time: u16;
+}
+
+Protocol_Command_Header :: struct #pack {
+    command: Command;
+    channel: Channel_ID;
+    seq_number: u16;
+}
+
+Protocol_Acknowledge :: struct #pack {
+    use header: Protocol_Command_Header;
+    recv_seq_number: u16;
+    recv_sent_time: u16;
+}
+
+Protocol_Connect :: struct #pack {
+    use header: Protocol_Command_Header;
+    outgoing_peer_id : u16;
+    mtu              : u32;
+    window_size      : u32;
+    channel_count    : u32;
+    connect_id       : u32;
+}
+
+Protocol_Verify_Connect :: struct #pack {
+    use header: Protocol_Command_Header;
+    outgoing_peer_id : u16;
+    mtu              : u32;
+    window_size      : u32;
+    channel_count    : u32;
+    connect_id       : u32;
+}
+
+Protocol_Send :: struct #pack {
+    use header: Protocol_Command_Header;
+    data_length: u16;
+}
+
+Protocol_Disconnect :: Protocol_Command_Header;
+
+command_sizes := u32.[
+    /* None             */ 0,
+    /* Acknowledge      */ sizeof Protocol_Acknowledge,
+    /* Connect          */ sizeof Protocol_Connect,
+    /* Verify_Connect   */ sizeof Protocol_Verify_Connect,
+    /* Disconnect       */ sizeof Protocol_Disconnect,
+    /* Ping             */ 0,
+    /* Send_Reliable    */ sizeof Protocol_Send,
+    /* Send_Unreliable  */ sizeof Protocol_Send,
+    /* Send_Unsequenced */ sizeof Protocol_Send,
+];
index dec1e2ddb4198f3918e90a9c680d9e8d0ef98729..b7b244b38eef9d6bd6f1ae8d2a84cecfb9467b32 100644 (file)
@@ -1,6 +1,8 @@
 #load "core/std"
+#load "src/module"
 
 use package core
+onyx_net :: package onyx_net
 
 octets_to_addr :: (a, b, c, d: u8) -> u32 {
     return (cast(u32) a << 24) |
@@ -10,6 +12,37 @@ octets_to_addr :: (a, b, c, d: u8) -> u32 {
 }
 
 main :: (args) => {
+    //
+    // 'null' signifies that this host should not bind a socket
+    // to a port. Only 1 peer is needed, as this is only connecting
+    // to one server.
+    host: ^onyx_net.Host;
+    if host', err := onyx_net.host_create(null, 1); err != .None {
+        println(err);
+    }
+
+    addr: net.Socket_Address;
+    addr.addr = octets_to_addr(127, 0, 0, 1);
+    addr.port = 8080;
+    peer := onyx_net.host_connect(host, ^addr, 2);
+
+    while true {
+        println("Waiting for events....");
+        for host->get_events(timeout=1000) {
+            printf("{*}\n", it);
+
+            if it.type == .Message {
+                packet := new(onyx_net.Packet);
+                packet.flags |= .Reliable;
+                packet.data = "HELLO";
+
+                onyx_net.peer_send(it.peer, 0, packet);
+            }
+        }
+    }
+}
+
+old_main :: (args) => {
     udp_socket, err := net.socket_create(.Inet, .Dgram);
     assert(err == .None, "Failed to create socket");
     defer udp_socket->close();
@@ -21,7 +54,7 @@ main :: (args) => {
 
 
     random.set_seed(12341);
-    for 1000 {
+    for 10 {
         to_send := random.string(50, alpha_numeric=true);
 
         sent_bytes := udp_socket->sendto(to_send, ^dest_addr);
index 4e4af2b51ed3916252f284be0eb81a628b73570e..304839e5413c133d6b851e2fddf7c64d6f6c9001 100644 (file)
@@ -1,14 +1,45 @@
 #load "core/std"
+#load "src/module"
 
 use package core
+onyx_net :: package onyx_net
+
 
 main :: (args) => {
+    addr: net.Socket_Address;
+    addr.port = 8080;
+
+    server, err := onyx_net.host_create(^addr, 32);
+    if err != .None {
+        println(err);
+        return;
+    }
+
+    while true {
+        println("Getting events...");
+        for server->get_events(timeout = 2000) {
+            printf("{*}\n", it);
+
+            if it.type == .Connection {
+                packet := new(onyx_net.Packet);
+                packet.flags |= .Reliable;
+                packet.data = "Welcome!!!";
+
+                onyx_net.peer_send(it.peer, 0, packet);
+            }
+        }
+    }
+}
+
+old_main :: (args) => {
 
     udp_socket, err := net.socket_create(.Inet, .Dgram);
     assert(err == .None, "Failed to create socket");
 
     assert(udp_socket->bind(8080), "Failed to bind socket");
 
+    udp_socket->setting(.NonBlocking, 1);
+
     while true {
         recv_buffer: [1024] u8;
         recv_addr, recv_bytes := udp_socket->recvfrom(recv_buffer);