restructing / cleaning up client side code
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 22 Apr 2022 14:38:21 +0000 (09:38 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 22 Apr 2022 14:38:21 +0000 (09:38 -0500)
src/client/build.onyx
src/client/connect_menu.onyx [deleted file]
src/client/game.onyx
src/client/main.onyx
src/client/menu/connect_menu.onyx [new file with mode: 0644]
src/client/net.onyx [deleted file]
src/client/net/handlers.onyx [new file with mode: 0644]
src/client/net/net.onyx [new file with mode: 0644]
src/client/net/outbound.onyx [new file with mode: 0644]
src/client/world/player.onyx
src/client/world/player_manager.onyx [new file with mode: 0644]

index 3c7bdc5c172dcbb72be57345906424f152c1a294..788adfcfe276599b32b439061c64569b0588ddc6 100644 (file)
@@ -10,6 +10,8 @@
 
 #load_all "./."
 #load_all "./gfx"
+#load_all "./menu"
+#load_all "./net"
 #load_all "./utils"
 #load_all "./world"
 
diff --git a/src/client/connect_menu.onyx b/src/client/connect_menu.onyx
deleted file mode 100644 (file)
index 52d9fbf..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-
-use package core
-use package glfw3
-use package opengles
-
-#local {
-    ip_addr: [..] u8;
-    port:    [..] u8;
-    name:    [..] u8;
-
-    title_font: Font;
-}
-
-Connect_Menu :: struct {
-    init :: (_: rawptr) {
-        array.clear(^port);
-        port_buffer: [8] u8;
-        string.concat(^port, conv.format(port_buffer, "{}", (package runtime.vars).Game_Port));
-
-        title_font = font_lookup(.{"./assets/fonts/calibri.ttf", 64});
-    }
-
-    join :: (_: rawptr) {
-        net_connect(ip_addr, ~~ conv.str_to_i64(port));
-
-        pop_game_state();
-        push_game_state(Connecting_Menu, null);
-    }
-
-    draw :: (_: rawptr) {
-        if is_key_just_down(GLFW_KEY_ESCAPE) {
-            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;
-
-        font_draw_centered(title_font, 0, (wh - 70) / 3, ww, "Untitled Shooter Game");
-
-        tx := (ww - 200) / 2;
-        ty := (wh - 160) / 2;
-        ui.textbox_list_start();
-        ui.draw_textbox(.{tx, ty+0, 200, 40},  ^ip_addr, "IP Address");
-        ui.draw_textbox(.{tx, ty+40, 200, 40}, ^port, "Port");
-        ui.draw_textbox(.{tx, ty+80, 200, 40}, ^name, "Name");
-        ui.textbox_list_end();
-
-        if ui.draw_button(.{tx, ty+120, 200, 40}, "Join") {
-            join(_);
-        }
-    }
-}
-
-
-
-Connecting_Menu :: struct {
-    #persist font: Font;
-    #persist connection_timeout: f32;
-
-    init :: (_: rawptr) {
-        font = font_lookup(.{"./assets/fonts/calibri.ttf", 32});
-        net_set_on_connect_callback(.{ _, on_connect });
-
-        connection_timeout = 10.0; // 10 second timeout
-    }
-
-    on_connect :: (_: rawptr) {
-        net_send_connect(name);
-    }
-
-    leave :: (_: rawptr) {
-        delete(^ip_addr);
-        delete(^port);
-        delete(^name);
-    }
-
-    update :: (_: rawptr, dt: f32) {
-        net_pulse();
-
-        connection_timeout -= dt;
-        if connection_timeout < 0 {
-            pop_game_state();
-            push_game_state(Connect_Menu, null);
-        }
-    }
-
-    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;
-
-        font_draw_centered(title_font, 0, (wh - 70) / 3, ww, "Untitled Shooter Game");
-
-        tx := (ww - 200) / 2;
-        ty := (wh - 16) / 2;
-        font_draw_centered(font, tx, ty, 200, "Connecting...");
-    }
-}
index d2f200f0c4dbbf159fb714bb9b69a59affd65eea..b29d17dde3f72788f3156c691ab20c9f6e17971b 100644 (file)
@@ -12,9 +12,11 @@ use package stb_truetype
     fog_color :: Vector3.{ 0.6, 0.6, 0.6 };
 }
 
+@Relocate // this global variables
 font: Font;
 player: Player;
 world: ^World;
+player_manager: ^Player_Manager;
 selected_block: Vector3i;
 
 player_shader: Shader;
@@ -60,12 +62,12 @@ game_init :: (_: rawptr) {
 }
 
 game_deinit :: (_: rawptr) {
-    net_send_disconnect();
-    net_disconnect();
+    game_net.send_disconnect();
+    game_net.disconnect();
 }
 
 game_update :: (_: rawptr, dt: f32) {
-    net_pulse();
+    game_net.pulse();
 
     if is_key_just_down(GLFW_KEY_ESCAPE) {
         if cursor_grabbed {
@@ -88,13 +90,10 @@ game_update :: (_: rawptr, dt: f32) {
     send_movement_timeout -= dt;
     if send_movement_timeout < 0 {
         send_movement_timeout = 0.1f;
-        net_send_movement_update(player.body.pos, player.body.vel, .{1,0,0}, player.body.on_ground);
+        game_net.send_movement_update(player.body.pos, player.body.vel, .{1,0,0}, player.body.on_ground);
     }
 
-    for ^other_players.entries {
-        if it.value.on_ground do it.value.velocity.y = 0;
-        it.value.position += it.value.velocity * dt;
-    }
+    player_manager->move_players(dt);
 
     if is_key_just_down(GLFW_KEY_F7) {
         debug_screen = !debug_screen;
@@ -140,14 +139,7 @@ draw_scene :: () {
     glLineWidth(2);
     chunk_highlight_block(~~selected_block.x, ~~selected_block.y, ~~selected_block.z);
 
-    shader_use(player_shader);
-    shader_set_uniform(player_shader, #cstr "color", Color.{1,0,0});
-    for^ other_players.entries {
-        if (it.value.position - player.body.pos)->square_mag() >= 70 * 70 do continue;
-
-        update_model_matrix(it.value.position - .{0,0.8,0}, .{0.3, 1, 0.3});
-        mesh_draw(Meshes.white_box);
-    }
+    player_manager->draw_players();
 }
 
 @Temporary
@@ -172,7 +164,7 @@ draw_chat :: () {
     #persist pending_message: [..] u8;
     if ui.draw_textbox(.{0, ~~(wh - 32), 400, 32}, ^pending_message) == .Enter_Pressed {
         if pending_message.count > 0 {
-            net_send_chat_message(string.alloc_copy(pending_message));
+            game_net.send_chat_message(string.alloc_copy(pending_message));
             array.clear(^pending_message);
         }
     }
index 99eece89998d302df7279a94344d7133d861e002..be9fb522bc543477eea2279632aadc290d775580 100644 (file)
@@ -6,7 +6,8 @@ use package core.intrinsics.onyx { __initialize }
 #local runtime :: package runtime
 
 #package {
-    ui :: package ui
+    ui       :: package ui
+    game_net :: package game_net
 }
 
 State :: struct {
@@ -67,7 +68,7 @@ init :: () {
     shaders_init();
     fonts_init();
     immediate_init();
-    net_register_handles();
+    game_net.register_handles();
     
     __initialize(^camera);
     camera_set_fov(^camera, 75);
diff --git a/src/client/menu/connect_menu.onyx b/src/client/menu/connect_menu.onyx
new file mode 100644 (file)
index 0000000..cb7cdbb
--- /dev/null
@@ -0,0 +1,111 @@
+
+use package core
+use package glfw3
+use package opengles
+
+#local {
+    ip_addr: [..] u8;
+    port:    [..] u8;
+    name:    [..] u8;
+
+    title_font: Font;
+}
+
+Connect_Menu :: struct {
+    init :: (_: rawptr) {
+        array.clear(^port);
+        port_buffer: [8] u8;
+        string.concat(^port, conv.format(port_buffer, "{}", (package runtime.vars).Game_Port));
+
+        title_font = font_lookup(.{"./assets/fonts/calibri.ttf", 64});
+
+        @HACK @HACK @HACK
+        player_manager = new(Player_Manager);
+    }
+
+    join :: (_: rawptr) {
+        game_net.connect(ip_addr, ~~ conv.str_to_i64(port));
+
+        pop_game_state();
+        push_game_state(Connecting_Menu, null);
+    }
+
+    draw :: (_: rawptr) {
+        if is_key_just_down(GLFW_KEY_ESCAPE) {
+            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;
+
+        font_draw_centered(title_font, 0, (wh - 70) / 3, ww, "Untitled Shooter Game");
+
+        tx := (ww - 200) / 2;
+        ty := (wh - 160) / 2;
+        ui.textbox_list_start();
+        ui.draw_textbox(.{tx, ty+0, 200, 40},  ^ip_addr, "IP Address");
+        ui.draw_textbox(.{tx, ty+40, 200, 40}, ^port, "Port");
+        ui.draw_textbox(.{tx, ty+80, 200, 40}, ^name, "Name");
+        ui.textbox_list_end();
+
+        if ui.draw_button(.{tx, ty+120, 200, 40}, "Join") {
+            join(_);
+        }
+    }
+}
+
+
+
+Connecting_Menu :: struct {
+    #persist font: Font;
+    #persist connection_timeout: f32;
+
+    init :: (_: rawptr) {
+        font = font_lookup(.{"./assets/fonts/calibri.ttf", 32});
+        game_net.set_on_connect_callback(.{ _, on_connect });
+
+        connection_timeout = 10.0; // 10 second timeout
+    }
+
+    on_connect :: (_: rawptr) {
+        game_net.send_connect(name);
+    }
+
+    leave :: (_: rawptr) {
+        delete(^ip_addr);
+        delete(^port);
+        delete(^name);
+    }
+
+    update :: (_: rawptr, dt: f32) {
+        game_net.pulse();
+
+        connection_timeout -= dt;
+        if connection_timeout < 0 {
+            pop_game_state();
+            push_game_state(Connect_Menu, null);
+        }
+    }
+
+    draw :: (_: rawptr) {
+        if is_key_just_down(GLFW_KEY_ESCAPE) {
+            game_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;
+
+        font_draw_centered(title_font, 0, (wh - 70) / 3, ww, "Untitled Shooter Game");
+
+        tx := (ww - 200) / 2;
+        ty := (wh - 16) / 2;
+        font_draw_centered(font, tx, ty, 200, "Connecting...");
+    }
+}
diff --git a/src/client/net.onyx b/src/client/net.onyx
deleted file mode 100644 (file)
index 8244f7c..0000000
+++ /dev/null
@@ -1,251 +0,0 @@
-
-net_register_handles :: () {
-    use type_info;
-
-    for_all_types() {
-        if struct_inherits(type_idx, Packet_Handler) {
-            func := get_struct_method(type_idx, "handle");
-            if func == null do continue;
-
-            struct_type_info := cast(^Type_Info_Struct) type_info;
-            packet_type := *cast(^packets.Type) struct_type_info.members[0].default;
-            packet_handles[packet_type] = *cast(^(rawptr) -> void) func.data;
-        }
-    }
-
-    println(packet_handles);
-}
-
-net_connect :: (ip_addr: [] u8, port: u16) {
-    addr: net.Socket_Address;
-    addr.addr = net.str_to_ipv4(ip_addr);
-    addr.port = port;
-    host', _ := onet.host_create(null, 1);
-    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 {
-            if on_connect_callback.func != null_proc {
-                on_connect_callback.func(on_connect_callback.data);
-            }
-        }
-
-        if it.type == .Message {
-            net_handle_packet(it.data);
-        }
-    }
-}
-
-#local packet_handles: Map(packets.Type, (rawptr) -> void);
-
-Packet_Handler :: struct {
-    type: packets.Type;
-}
-
-#local Verify_Connect_Handle :: struct {
-    use base := Packet_Handler.{ .Verify_Connect };
-
-    handle :: (packet: ^packets.Verify_Connect) {
-        player_id = packet.player_id;
-
-        push_game_state(Game_State, null);
-    }
-}
-
-#local Player_Joined_Handle :: struct {
-    use base := Packet_Handler.{ .Player_Joined };
-
-    handle :: (packet: ^packets.Player_Joined) {
-        name := string.alloc_copy(packet->name());
-
-        other_players[~~ packet.player_id] = .{
-            name,
-            .{0, 0, 0}, .{0, 0, 0}, .{0, 0, 0},
-            false
-        };
-
-        if packet.player_id != player_id {
-            buf: [1024] u8;
-            chat_messages << (conv.format(buf, "{} joined the server!", name) |> string.alloc_copy());
-        }
-    }
-}
-
-#local Connection_Rejected_Handle :: struct {
-    use base := Packet_Handler.{ .Connection_Rejected };
-
-    handle :: (packet: ^packets.Connection_Rejected) {
-        @NetworkErrors // Handle this case.
-        printf("Connection refused: {}\n", packet.reason);
-
-        pop_game_state();
-        push_game_state(Connect_Menu, null);
-    }
-}
-
-#local Chat_Message_Handle :: struct {
-    use base := Packet_Handler.{ .Chat_Message };
-
-    handle :: (packet: ^packets.Chat_Message) {
-        buf: [1024] u8;
-        msg: str;
-
-        sender := map.get_ptr(^other_players, ~~ packet.sender_id);
-        if sender != null {
-            msg = conv.format(buf, "[{}] {}\n", sender.name, packet->message());
-        } else {
-            msg = packet->message();
-        }
-
-        chat_messages << string.alloc_copy(msg);
-    }
-}
-
-#local Player_Moved_Handle :: struct {
-    use base := Packet_Handler.{ .Player_Moved };
-
-    handle :: (packet: ^packets.Player_Moved) {
-        if packet.player_id == player_id do return;
-
-        player := map.get_ptr(^other_players, ~~ packet.player_id);
-        player.position  = packet.position;
-        player.velocity  = packet.velocity;
-        player.facing    = packet.facing;
-        player.on_ground = packet.on_ground;
-    }
-}
-
-#local Block_Updates_Handle :: struct {
-    use base := Packet_Handler.{ .Block_Updates };
-
-    handle :: (packet: ^packets.Block_Updates) {
-        updates := packet->updates();
-        for updates {
-            pos := it.position;
-            world_set_block(world, pos.x, pos.y, pos.z, it.new_block);
-        }
-    }
-}
-
-net_handle_packet :: (packet_data: [] u8) {
-    packet := cast(^packets.Packet_Base) packet_data.data;
-    if packet_handles[~~ packet.type] != null_proc {
-        packet_handles[~~ packet.type](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 };
-    p.free_data = true;
-    onet.peer_send(peer, 0, p);
-}
-
-net_send_disconnect :: () {
-    disconnect := new(packets.Disconnect);
-    disconnect.player_id = player_id;
-
-    p := new(onet.Packet);
-    p.data = .{ ~~disconnect, sizeof typeof *disconnect };
-    p.free_data = true;
-    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 };
-    p.free_data = true;
-    onet.peer_send(peer, 0, p);
-}
-
-net_send_movement_update :: (position: Vector3, velocity: Vector3, facing: Vector3, on_ground: bool) {
-    movement := new(packets.Player_Moved);
-    movement.player_id = player_id;
-    movement.position = position;
-    movement.velocity = velocity;
-    movement.facing = facing;
-    movement.on_ground = on_ground;
-
-    p := new(onet.Packet);
-    p.data = .{ ~~movement, sizeof typeof *movement };
-    p.free_data = true;
-    onet.peer_send(peer, 0, p);
-}
-
-net_send_block_updates :: (updates: [] packets.Block_Updates.Update) {
-    size := sizeof packets.Block_Updates + updates.count * sizeof packets.Block_Updates.Update;
-    block_updates := cast(^packets.Block_Updates) calloc(size);
-    block_updates.type = .Block_Updates;
-    block_updates.update_count = ~~updates.count;
-
-    block_update := cast(^packets.Block_Updates.Update) ^block_updates.update_data;
-    for updates.count {
-        block_update[it].position  = updates[it].position;
-        block_update[it].new_block = updates[it].new_block;
-    }
-
-    p := new(onet.Packet);
-    p.data = .{ ~~block_updates, size };
-    p.free_data = true;
-    onet.peer_send(peer, 0, p);
-}
-
-Remote_Player :: struct {
-    name: str;
-
-    position:  Vector3;
-    velocity:  Vector3;
-    facing:    Vector3;
-    on_ground: bool;
-}
-
-@TEMPORARY
-other_players: Map(u32, Remote_Player);
-
-#local {
-    use package core
-    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 };
-}
-
-
diff --git a/src/client/net/handlers.onyx b/src/client/net/handlers.onyx
new file mode 100644 (file)
index 0000000..c1c02c3
--- /dev/null
@@ -0,0 +1,88 @@
+package game_net
+
+#local {
+    use package core
+    use package main   
+    onet    :: package onyx_net
+    packets :: package packets
+}
+
+#local Verify_Connect_Handle :: struct {
+    use base := Packet_Handler.{ .Verify_Connect };
+
+    handle :: (packet: ^packets.Verify_Connect) {
+        player_manager.player_id = packet.player_id;
+
+        push_game_state(Game_State, null);
+    }
+}
+
+#local Player_Joined_Handle :: struct {
+    use base := Packet_Handler.{ .Player_Joined };
+
+    handle :: (packet: ^packets.Player_Joined) {
+        name := packet->name();
+        player_manager->add_player(packet.player_id, name);
+
+        if packet.player_id != player_manager.player_id {
+            buf: [1024] u8;
+            chat_messages << (conv.format(buf, "{} joined the server!", name) |> string.alloc_copy());
+        }
+    }
+}
+
+#local Connection_Rejected_Handle :: struct {
+    use base := Packet_Handler.{ .Connection_Rejected };
+
+    handle :: (packet: ^packets.Connection_Rejected) {
+        @NetworkErrors // Handle this case.
+        printf("Connection refused: {}\n", packet.reason);
+
+        pop_game_state();
+        push_game_state(Connect_Menu, null);
+    }
+}
+
+#local Chat_Message_Handle :: struct {
+    use base := Packet_Handler.{ .Chat_Message };
+
+    handle :: (packet: ^packets.Chat_Message) {
+        buf: [1024] u8;
+        msg: str;
+
+        sender := player_manager->get_player(packet.sender_id);
+        if sender != null {
+            msg = conv.format(buf, "[{}] {}\n", sender.name, packet->message());
+        } else {
+            msg = packet->message();
+        }
+
+        chat_messages << string.alloc_copy(msg);
+    }
+}
+
+#local Player_Moved_Handle :: struct {
+    use base := Packet_Handler.{ .Player_Moved };
+
+    handle :: (packet: ^packets.Player_Moved) {
+        if packet.player_id == player_manager.player_id do return;
+
+        player := player_manager->get_player(packet.player_id);
+        player.position  = packet.position;
+        player.velocity  = packet.velocity;
+        player.facing    = packet.facing;
+        player.on_ground = packet.on_ground;
+    }
+}
+
+#local Block_Updates_Handle :: struct {
+    use base := Packet_Handler.{ .Block_Updates };
+
+    handle :: (packet: ^packets.Block_Updates) {
+        updates := packet->updates();
+        for updates {
+            pos := it.position;
+            world_set_block(world, pos.x, pos.y, pos.z, it.new_block);
+        }
+    }
+}
diff --git a/src/client/net/net.onyx b/src/client/net/net.onyx
new file mode 100644 (file)
index 0000000..9fd9c48
--- /dev/null
@@ -0,0 +1,82 @@
+package game_net
+
+#local {
+    use package core
+    use package main   
+    onet    :: package onyx_net
+    packets :: package packets
+}
+
+register_handles :: () {
+    use type_info;
+
+    for_all_types() {
+        if struct_inherits(type_idx, Packet_Handler) {
+            func := get_struct_method(type_idx, "handle");
+            if func == null do continue;
+
+            struct_type_info := cast(^Type_Info_Struct) type_info;
+            packet_type := *cast(^packets.Type) struct_type_info.members[0].default;
+            packet_handles[packet_type] = *cast(^(rawptr) -> void) func.data;
+        }
+    }
+
+    println(packet_handles);
+}
+
+connect :: (ip_addr: [] u8, port: u16) {
+    addr: net.Socket_Address;
+    addr.addr = net.str_to_ipv4(ip_addr);
+    addr.port = port;
+    host', _ := onet.host_create(null, 1);
+    peer = onet.host_connect(host, ^addr, 2);
+}
+
+disconnect :: () {
+    onet.peer_send_disconnect(peer);
+    onet.peer_flush_outgoing_commands(peer);
+    onet.peer_disconnect(peer);
+}
+
+set_on_connect_callback :: (callback: On_Connect_Callback) {
+    on_connect_callback = callback;
+}
+
+pulse :: () {
+    for host->get_events(timeout=0) {
+        if it.type == .Connection {
+            if on_connect_callback.func != null_proc {
+                on_connect_callback.func(on_connect_callback.data);
+            }
+        }
+
+        if it.type == .Message {
+            handle_packet(it.data);
+        }
+    }
+}
+
+#package packet_handles: Map(packets.Type, (rawptr) -> void);
+Packet_Handler :: struct {
+    type: packets.Type;
+}
+
+#local handle_packet :: (packet_data: [] u8) {
+    packet := cast(^packets.Packet_Base) packet_data.data;
+    if packet_handles[~~ packet.type] != null_proc {
+        packet_handles[~~ packet.type](packet);
+    }
+}
+
+#package {
+    host: ^onet.Host;
+    peer: ^onet.Peer;
+
+    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 };
+}
\ No newline at end of file
diff --git a/src/client/net/outbound.onyx b/src/client/net/outbound.onyx
new file mode 100644 (file)
index 0000000..c2d58df
--- /dev/null
@@ -0,0 +1,78 @@
+package game_net
+
+#local {
+    use package core
+    use package main
+    onet    :: package onyx_net
+    packets :: package packets
+}
+
+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 };
+    p.free_data = true;
+    onet.peer_send(peer, 0, p);
+}
+
+send_disconnect :: () {
+    disconnect := new(packets.Disconnect);
+    disconnect.player_id = player_manager.player_id;
+
+    p := new(onet.Packet);
+    p.data = .{ ~~disconnect, sizeof typeof *disconnect };
+    p.free_data = true;
+    onet.peer_send(peer, 0, p);
+}
+
+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_manager.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 };
+    p.free_data = true;
+    onet.peer_send(peer, 0, p);
+}
+
+send_movement_update :: (position: Vector3, velocity: Vector3, facing: Vector3, on_ground: bool) {
+    movement := new(packets.Player_Moved);
+    movement.player_id = player_manager.player_id;
+    movement.position = position;
+    movement.velocity = velocity;
+    movement.facing = facing;
+    movement.on_ground = on_ground;
+
+    p := new(onet.Packet);
+    p.data = .{ ~~movement, sizeof typeof *movement };
+    p.free_data = true;
+    onet.peer_send(peer, 0, p);
+}
+
+send_block_updates :: (updates: [] packets.Block_Updates.Update) {
+    size := sizeof packets.Block_Updates + updates.count * sizeof packets.Block_Updates.Update;
+    block_updates := cast(^packets.Block_Updates) calloc(size);
+    block_updates.type = .Block_Updates;
+    block_updates.update_count = ~~updates.count;
+
+    block_update := cast(^packets.Block_Updates.Update) ^block_updates.update_data;
+    for updates.count {
+        block_update[it].position  = updates[it].position;
+        block_update[it].new_block = updates[it].new_block;
+    }
+
+    p := new(onet.Packet);
+    p.data = .{ ~~block_updates, size };
+    p.free_data = true;
+    onet.peer_send(peer, 0, p);
+}
index 28141074553a03a73c750eed9e1c690f36fde708..01fd581add9f31e5fdd965d62dd8a85bd6f2f144 100644 (file)
@@ -92,12 +92,12 @@ player_update :: (use player: ^Player, dt: f32) {
     }
 
     if is_button_just_down(GLFW_MOUSE_BUTTON_LEFT) {
-        net_send_block_updates(.[.{selected_block, Block_Empty}]);
+        game_net.send_block_updates(.[.{selected_block, Block_Empty}]);
     }
 
     if is_button_just_down(GLFW_MOUSE_BUTTON_RIGHT) {
         target := selected_block + dir;
-        net_send_block_updates(.[.{target, block_make(1,0,0,1)}]);
+        game_net.send_block_updates(.[.{target, block_make(1,0,0,1)}]);
     }
 }
 
diff --git a/src/client/world/player_manager.onyx b/src/client/world/player_manager.onyx
new file mode 100644 (file)
index 0000000..c0260c2
--- /dev/null
@@ -0,0 +1,47 @@
+use package core
+
+Remote_Player :: struct {
+    name: str;
+
+    position:  Vector3;
+    velocity:  Vector3;
+    facing:    Vector3;
+    on_ground: bool;
+}
+
+Player_Manager :: struct {
+    // Player ID of the local player
+    player_id: u16;
+
+    other_players: Map(u16, Remote_Player);
+
+    add_player :: (use this: ^Player_Manager, id: u16, name: str) {
+        other_players[id] = .{
+            string.alloc_copy(name),
+            .{0, 0, 0}, .{0, 0, 0}, .{0, 0, 0},
+            false
+        };
+    }
+
+    get_player :: (use this: ^Player_Manager, id: u16) -> ^Remote_Player {
+        return ^other_players[id];
+    }
+
+    move_players :: (use this: ^Player_Manager, dt: f32) {
+        for ^other_players.entries {
+            if it.value.on_ground do it.value.velocity.y = 0;
+            it.value.position += it.value.velocity * dt;
+        }
+    }
+
+    draw_players :: (use this: ^Player_Manager) {
+        shader_use(player_shader);
+        shader_set_uniform(player_shader, #cstr "color", Color.{1,0,0});
+        for^ player_manager.other_players.entries {
+            if (it.value.position - player.body.pos)->square_mag() >= 70 * 70 do continue;
+
+            update_model_matrix(it.value.position - .{0,0.8,0}, .{0.3, 1, 0.3});
+            mesh_draw(Meshes.white_box);
+        }
+    }
+}
\ No newline at end of file