"connecting" to the server
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 4 Apr 2022 18:17:23 +0000 (13:17 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 4 Apr 2022 18:17:23 +0000 (13:17 -0500)
src/client/build.onyx
src/client/connect_menu.onyx
src/client/game.onyx
src/client/net.onyx
src/server/build.onyx
src/server/server.onyx
src/shared/packets.onyx

index 2d1168e3a9acc865e9f0b60cbb6c92ff045fefd9..3c7bdc5c172dcbb72be57345906424f152c1a294 100644 (file)
@@ -13,6 +13,8 @@
 #load_all "./utils"
 #load_all "./world"
 
+#load_all "./../shared"
+
 // Onyx modules
 #load "glfw3/module"
 #load "opengles/module"
index 4ddf29db81d611aac7af717a1da619cf9d9b2a10..ffa1ba4f46369d8e926af836c16a145abd534309 100644 (file)
@@ -19,14 +19,13 @@ Connect_Menu :: struct {
     leave :: (_: rawptr) {
         array.free(^ip_addr);
         array.free(^port);
-        array.free(^name);
     }
 
     join :: (_: rawptr) {
         net_connect(ip_addr, ~~ conv.str_to_i64(port));
 
         pop_game_state();
-        push_game_state(Game_State, null);
+        push_game_state(Connecting_Menu, null);
     }
 
     draw :: (_: rawptr) {
@@ -38,12 +37,53 @@ Connect_Menu :: struct {
         glClearColor(0.1, 0.1, 0.1, 1);
         glClear(GL_COLOR_BUFFER_BIT);
 
-        draw_textbox(.{0, 0, 200, 40},  ^ip_addr, "IP Address");
-        draw_textbox(.{0, 40, 200, 40}, ^port, "Port");
-        draw_textbox(.{0, 80, 200, 40}, ^name, "Name");
+        ww, wh := camera.window_width, camera.window_height;
 
-        if draw_button(.{0, 120, 200, 40}, "Join") {
+        tx := (ww - 200) / 2;
+        ty := (wh - 160) / 2;
+        draw_textbox(.{tx, ty+0, 200, 40},  ^ip_addr, "IP Address");
+        draw_textbox(.{tx, ty+40, 200, 40}, ^port, "Port");
+        draw_textbox(.{tx, ty+80, 200, 40}, ^name, "Name");
+
+        if draw_button(.{tx, ty+120, 200, 40}, "Join") {
             join(_);
         }
     }
+}
+
+
+
+Connecting_Menu :: struct {
+    #persist font: Font
+
+    init :: (_: rawptr) {
+        font = font_lookup(.{"./assets/fonts/calibri.ttf", 32});
+        net_set_on_connect_callback(.{ _, on_connect });
+    }
+
+    on_connect :: (_: rawptr) {
+        net_send_connect(name);
+        array.free(^name); @HACK // This feels gross to free this down here.
+    }
+
+    update :: (_: rawptr, dt: f32) {
+        net_pulse();
+    }
+
+    draw :: (_: rawptr) {
+        if is_key_just_down(GLFW_KEY_ESCAPE) {
+            net_disconnect();
+            glfwSetWindowShouldClose(window, true);
+        }
+
+        glDisable(GL_DEPTH_TEST);
+        glClearColor(0.1, 0.1, 0.1, 1);
+        glClear(GL_COLOR_BUFFER_BIT);
+
+        ww, wh := camera.window_width, camera.window_height;
+
+        tx := (ww - 200) / 2;
+        ty := (wh - 16) / 2;
+        font_draw_centered(font, tx, ty, 200, "Connecting...");
+    }
 }
\ No newline at end of file
index 7a884d7507a219fb51c642630cb9ef82f0605746..e3236ec9ba88de1e142232c88f241fe8275efb02 100644 (file)
@@ -101,6 +101,7 @@ draw_scene :: () {
     chunk_highlight_block(~~selected_block.x, ~~selected_block.y, ~~selected_block.z);
 }
 
+@Temporary
 chat_messages: [..] [] u8;
 
 draw_chat :: () {
@@ -122,7 +123,7 @@ draw_chat :: () {
     #persist pending_message: [..] u8;
     if draw_textbox(.{0, ~~(wh - 32), 400, 32}, ^pending_message) == .Enter_Pressed {
         if pending_message.count > 0 {
-            net_send_message(string.alloc_copy(pending_message));
+            net_send_chat_message(string.alloc_copy(pending_message));
             array.clear(^pending_message);
         }
     }
index a343fd658aac4b6ed04bbca1aba59a173ca3311f..d35eb590d3eff1d7f6d263a6941c5519ef4ec1dd 100644 (file)
@@ -1,5 +1,4 @@
 
-
 net_connect :: (ip_addr: [] u8, port: u16) {
     addr: net.Socket_Address;
     addr.addr = net.str_to_ipv4(ip_addr);
@@ -8,33 +7,95 @@ net_connect :: (ip_addr: [] u8, port: u16) {
     peer = onet.host_connect(host, ^addr, 2);
 }
 
+net_disconnect :: () {
+    onet.peer_send_disconnect(peer);
+    onet.peer_flush_outgoing_commands(peer);
+    onet.peer_disconnect(peer);
+}
+
+net_set_on_connect_callback :: (callback: On_Connect_Callback) {
+    on_connect_callback = callback;
+}
+
 net_pulse :: () {
     for host->get_events(timeout=0) {
         if it.type == .Connection {
-            chat_messages << "[Connected to server]";
+            if on_connect_callback.func != null_proc {
+                on_connect_callback.func(on_connect_callback.data);
+            }
         }
 
         if it.type == .Message {
-            chat_messages << string.alloc_copy(it.data);
+            net_handle_packet(it.data);
+        }
+    }
+}
+
+net_handle_packet :: (packet_data: [] u8) {
+    packet := cast(^packets.Packet_Base) packet_data.data;
+    switch packet.type {
+        case .Verify_Connect {
+            vc_packet := cast(^packets.Verify_Connect) packet;
+            player_id = vc_packet.player_id;
+
+            push_game_state(Game_State, null);
+        }
+
+        case .Chat_Message {
+            chat_packet := cast(^packets.Chat_Message) packet;
+            chat_messages << string.alloc_copy(.{
+                ~~ ^chat_packet.message_data,
+                ~~ chat_packet.message_length,
+            });
         }
     }
 }
 
-@Temporary
-net_send_message :: (message: str) {
-    packet := new(onet.Packet);
-    packet.flags |= .Reliable;
-    packet.data = message;
-    onet.peer_send(peer, 0, packet);
+net_send_connect :: (name: str) {
+    msg := cast(^packets.Connect) calloc(sizeof packets.Connect + name.count);
+    msg.type = .Connect;
+    msg.client_version = (package runtime.vars).Game_Version;
+    msg.name_length = ~~ name.count;
+    memory.copy(~~ ^msg.name_data, name.data, name.count);
+
+    p := new(onet.Packet);
+    p.flags |= .Reliable;
+    p.data = .{ ~~msg, sizeof packets.Connect + name.count };
+    onet.peer_send(peer, 0, p);
+}
+
+net_send_chat_message :: (message: str) {
+    msg := cast(^packets.Chat_Message) calloc(sizeof packets.Chat_Message + message.count);
+    msg.type = .Chat_Message;
+    msg.sender_id = player_id;
+    msg.message_length = ~~ message.count;
+    memory.copy(~~ ^msg.message_data, message.data, message.count);
+
+    p := new(onet.Packet);
+    p.flags |= .Reliable;
+    p.data = .{ ~~msg, sizeof packets.Chat_Message + message.count };
+    onet.peer_send(peer, 0, p);
 }
 
 
 #local {
     use package core
-    onet :: package onyx_net
+    onet    :: package onyx_net
+    packets :: package packets
     
     host: ^onet.Host;
     peer: ^onet.Peer;
+
+    @TEMPORARY @HACK
+    player_id: u16;
+
+    On_Connect_Callback :: struct {
+        data: rawptr;
+        func: (rawptr) -> void;
+    }
+    on_connect_callback: On_Connect_Callback;
+
+    any_to_buffer :: macro (x: ^$T) => ([] u8).{ ~~x, sizeof T };
 }
 
 
index 0f46cd2672352eb6f8b6458e68945f16bb4bebba..de3b1a29abf3c6c1a0d640ed43f543bb74d3b58d 100644 (file)
@@ -10,4 +10,6 @@ package runtime.vars
 #load "./../src/config"
 #load "server"
 
+#load "./../shared/packets"
+
 #load "onyx-net/src/module"
index c25f9a02360f876ff9c5ac9ccf51203b77b36340..76a8e7f43ed572761d25671d060a0c4549275b20 100644 (file)
 
 use package core
+use package core.intrinsics.onyx {__initialize}
 
 #local {
-    onet :: package onyx_net
+    onet    :: package onyx_net
+    packets :: package packets
 }
 
-main :: (args) => {
+Player_Data :: struct {
+    id: u16;
+    name: str;
+    peer: ^onet.Peer;
+}
+
+max_connected_players := 8;
+next_player_id        := 1;
+connected_players: Map(u32, Player_Data);
+
+host: ^onet.Host;
+
+setup_server :: () {
     addr: net.Socket_Address;
     addr.port = (package runtime.vars).Game_Port;
 
-    host, err := onet.host_create(^addr, 16);
+    host', err := onet.host_create(^addr, 16);
     if err != .None {
+        println(err);
         os.exit(1);
     }
+}
+
+handle_packet :: (peer: ^onet.Peer, packet_data: [] u8) {
+    packet := cast(^packets.Packet_Base) packet_data.data;
+    switch packet.type {
+        case .Connect {
+            connect_packet := cast(^packets.Connect) packet;
+
+            if connect_packet.client_version != (package runtime.vars).Game_Version {
+                respond_with_error(.Client_Version_Wrong);
+            }
+
+            if connected_players.entries.count >= max_connected_players {
+                respond_with_error(.Server_Full);
+            }
+
+            name := str.{
+                ~~ ^connect_packet.name_data,
+                ~~connect_packet.name_length
+            };
+
+            defer next_player_id += 1;
+            connected_players[next_player_id] = .{
+                id = ~~ next_player_id,
+                name = string.alloc_copy(name),
+                peer = peer,
+            };
+
+            {
+                msg_buffer: [128] u8;
+                msg := conv.format(msg_buffer, "{} joined the server.\n", connected_players[next_player_id].name);
+                print(msg);
+                send_chat_message(0, msg);
+            }
+
+            msg := new(packets.Verify_Connect);   @LEAK
+            msg.type = .Verify_Connect;
+            msg.player_id = ~~next_player_id;
+
+            p := new(onet.Packet);                     @LEAK
+            p.flags |= .Reliable;
+            p.data = any_to_buffer(msg);
+            onet.peer_send(peer, 0, p);
+
+            respond_with_error :: macro (reason: packets.Connection_Rejected.Reason) {
+                msg := new(packets.Connection_Rejected);   @LEAK
+                msg.type = .Connection_Rejected;
+                msg.reason = reason;
+
+                p := new(onet.Packet);                     @LEAK
+                p.flags |= .Reliable;
+                p.data = any_to_buffer(msg);
+                onet.peer_send(peer, 0, p);
+                return;
+            }
+        }
+
+        case .Chat_Message {
+            packet := new(onet.Packet); @LEAK
+            packet.flags |= .Reliable;
+            packet.data = memory.copy_slice(packet_data);
+            onet.host_broadcast(host, 0, packet);
+        }
+    }
+}
+
+send_chat_message :: (from: u16, msg: [] u8) {
+    chat_packet, packet_size := new_with_extra(packets.Chat_Message, msg.count); @LEAK
+    chat_packet.type = .Chat_Message;
+    chat_packet.sender_id = from;
+    chat_packet.message_length = ~~ msg.count;
+    memory.copy(~~ ^chat_packet.message_data, msg.data, msg.count);
+
+    packet := new(onet.Packet); @LEAK
+    packet.flags |= .Reliable;
+    packet.data = .{ ~~chat_packet, packet_size };
+    onet.host_broadcast(host, 0, packet);
+}
+
+any_to_buffer :: macro (x: ^$T) => ([] u8).{ ~~x, sizeof T };
 
+new_with_extra :: macro ($T: type_expr, extra: u32) -> (^T, u32) {
+    size := sizeof T + extra;
+    ptr  := cast(^T) calloc(size);
+    __initialize(ptr);
+    return ptr, size;
+}
+
+loop :: () {
     while true {
         for host->get_events(timeout=1000) {
             if it.type == .Connection {
                 printf("Connection from {}:{}\n", net.ipv4_to_str(it.peer.addr.addr), it.peer.addr.port);
-                packet := new(onet.Packet);
-                packet.flags |= .Reliable;
-                packet.data = aprintf("{}:{} connected", net.ipv4_to_str(it.peer.addr.addr), it.peer.addr.port); @LEAK
-                onet.host_broadcast(host, 0, packet);     @LEAK
             }
             
             if it.type == .Disconnection {
@@ -29,13 +128,14 @@ main :: (args) => {
             }
 
             if it.type == .Message {
-                printf("Message: {}\n", it.data);
-
-                packet := new(onet.Packet);
-                packet.flags |= .Reliable;
-                packet.data = string.alloc_copy(it.data); @LEAK
-                onet.host_broadcast(host, 0, packet);     @LEAK
+                handle_packet(it.peer, it.data);
             }
         }
     }
 }
+
+
+main :: (args) => {
+    setup_server();
+    loop();
+}
index fffaab5a130454345c94506fbfa67ff83974b365..4421cc2a22ecccb0a50e00a895b9c6599c96c192 100644 (file)
@@ -1,5 +1,5 @@
 
-package packet
+package packets
 
 Type :: enum (u8) {
     Connect;
@@ -7,6 +7,8 @@ Type :: enum (u8) {
     Connection_Rejected;
     Disconnect;
 
+    Chat_Message;
+
     Player_Joined;
     Player_Left;
     Player_Moved;
@@ -18,8 +20,9 @@ Packet_Base :: struct #pack {
 
 Connect :: struct #pack {
     use base: Packet_Base;
-    name: str;
     client_version: u32;
+    name_length: u16;
+    name_data: void;
 }
 
 Verify_Connect :: struct #pack {
@@ -31,10 +34,18 @@ Connection_Rejected :: struct #pack {
     use base: Packet_Base;
 
     Reason :: enum {
-        Server_Full     :: 1;
-        Outdated_Client :: 2;
+        Server_Full          :: 1;
+        Client_Version_Wrong :: 2;
     }
     reason: Reason;
 }
 
 
+
+Chat_Message :: struct #pack {
+    use base: Packet_Base;
+    sender_id: u16; // Should this be a Player ID?
+    message_length: u16;
+    message_data:   void;
+}
+