From 989bd98ef2ad6e79ff82dfb6ed1646103ddbe339 Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Mon, 4 Apr 2022 07:48:08 -0500 Subject: [PATCH] starting to work on actual network protocol; code organization --- doc/design.md | 15 +++ src/client/build.onyx | 2 +- src/client/game.onyx | 177 ++++++++++++++++++++++++++++ src/client/globals.onyx | 5 + src/client/main.onyx | 222 ++++++------------------------------ src/client/utils/input.onyx | 150 ++++++++++++------------ src/server/server.onyx | 15 ++- src/shared/packets.onyx | 25 ++++ 8 files changed, 344 insertions(+), 267 deletions(-) create mode 100644 src/client/game.onyx create mode 100644 src/client/globals.onyx create mode 100644 src/shared/packets.onyx diff --git a/doc/design.md b/doc/design.md index e69de29..e80238d 100644 --- a/doc/design.md +++ b/doc/design.md @@ -0,0 +1,15 @@ + + +Networking Design +================= + +Packets (Client to Server): + - Connect + - Disconnect + - Player_Moved + +Packets (Server to Client): + - Verify_Connect + - Player_Joined + - Player_Left + - Player_Moved \ No newline at end of file diff --git a/src/client/build.onyx b/src/client/build.onyx index c8e2745..ed4e5ab 100644 --- a/src/client/build.onyx +++ b/src/client/build.onyx @@ -7,8 +7,8 @@ // Primary source files #load "./../src/config" -#load "main" +#load_all "./." #load_all "./gfx" #load_all "./utils" #load_all "./world" diff --git a/src/client/game.onyx b/src/client/game.onyx new file mode 100644 index 0000000..d173f1b --- /dev/null +++ b/src/client/game.onyx @@ -0,0 +1,177 @@ + +use package core +use package glfw3 +use package opengles +use package stb_truetype +use package core.intrinsics.onyx { __initialize } +#local onet :: package onyx_net + +#local { + world_shader: Shader; + + chat_font: Font; + + fog_color :: Vector3.{ 0.6, 0.6, 0.6 }; +} + +font: Font; +player: Player; +world: ^World; +selected_block: Vector3i; + + +@Temporary host: ^onet.Host; +@Temporary peer: ^onet.Peer; + + +game_init :: () { + world_shader = shader_make("assets/shaders/world.glsl"); + + shader_link_world_matrix_block(world_shader); + shader_set_uniform(world_shader, #cstr "u_fog_start", 50.0f); + shader_set_uniform(world_shader, #cstr "u_fog_range", 10.0f); + + font = font_lookup(.{"./assets/fonts/calibri.ttf", 32}); + chat_font = font_lookup(.{"./assets/fonts/calibri.ttf", 16}); + + shader_use(world_shader); + world = world_make(); + player = player_make(); + player.camera = ^camera; + + __initialize(^camera); + camera_set_fov(^camera, 75); + ww, wh: i32; + glfwGetWindowSize(window, ^ww, ^wh); + camera_set_window_size(^camera, ~~ww, ~~wh); + + addr: net.Socket_Address; + addr.addr = net.str_to_ipv4("127.0.0.1"); + addr.port = (package runtime.vars).Game_Port; + host', _ := onet.host_create(null, 1); + peer = onet.host_connect(host, ^addr, 2); +} + +game_update :: (dt: f32) { + for host->get_events(timeout=0) { + if it.type == .Connection { + chat_messages << "[Connected to server]"; + } + + if it.type == .Message { + chat_messages << string.alloc_copy(it.data); + } + } + + if is_key_just_down(GLFW_KEY_ESCAPE) { + if cursor_grabbed { + toggle_cursor_grabbed(); + } else { + glfwSetWindowShouldClose(window, true); + } + } + + if cursor_grabbed { + player_update_controls(^player, dt); + player_update(^player, dt); + } + + player_update_physics(^player, dt); + player_chunk := world_position_to_chunk(world, player.body.pos); + world_move_center(world, player_chunk); + world_update(world, dt); + + if is_key_just_down(GLFW_KEY_F7) { + debug_screen = !debug_screen; + } + + update_world_matrix(); +} + +game_draw :: () { + draw_scene(); + + ww, wh: i32; + glfwGetWindowSize(window, ^ww, ^wh); + font_print(font, ~~(ww / 2), ~~(wh / 2), "."); + + // + // A very hacky wait to have the click the capture mouse. + immediate_push_scissor(0, 0, 0, 0); + if !cursor_grabbed { + if draw_button(.{ 0, 0, ~~ww, ~~wh }, "") { + toggle_cursor_grabbed(); + } + } + immediate_pop_scissor(); + + draw_chat(); +} + +draw_scene :: () { + glClearColor(fog_color.x, fog_color.y, fog_color.z, 1); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + shader_use(world_shader); + shader_set_uniform(world_shader, #cstr "u_fog_color", fog_color); + shader_set_uniform(world_shader, #cstr "u_texture", 0); + glEnable(GL_DEPTH_TEST); + world_draw(world); + + glLineWidth(2); + chunk_highlight_block(~~selected_block.x, ~~selected_block.y, ~~selected_block.z); +} + + +chat_messages: [..] [] u8; +pending_message: [..] u8; + +draw_chat :: () { + glDisable(GL_DEPTH_TEST); + + ww, wh: i32; + glfwGetWindowSize(window, ^ww, ^wh); + immediate_set_color(.{0.2, 0.2, 0.2, 0.8}); + immediate_rectangle(0, ~~(wh - 300), 400, 300); + + font_set_color(.{1, 1, 1}); + y := cast(f32) (wh - 32); + while i := cast(i32) chat_messages.count - 1; i >= 0 { + defer i -= 1; + y -= 18; + font_draw(chat_font, 10, y, chat_messages[i]); + } + + if draw_textbox(.{0, ~~(wh - 32), 400, 32}, ^pending_message) == .Enter_Pressed { + if pending_message.count > 0 { + send_message(string.alloc_copy(pending_message)); + array.clear(^pending_message); + } + } + + send_message :: (message: [] u8) { + packet := new(onet.Packet); + packet.flags |= .Reliable; + packet.data = message; + onet.peer_send(peer, 0, packet); + } +} + +cursor_grabbed := false; +toggle_cursor_grabbed :: () { + cursor_grabbed = !cursor_grabbed; + + if cursor_grabbed { + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + + if glfwRawMouseMotionSupported() == GLFW_TRUE { + glfwSetInputMode(window, GLFW_RAW_MOUSE_MOTION, GLFW_TRUE); + } + } else { + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + + if glfwRawMouseMotionSupported() == GLFW_TRUE { + glfwSetInputMode(window, GLFW_RAW_MOUSE_MOTION, GLFW_FALSE); + } + } +} \ No newline at end of file diff --git a/src/client/globals.onyx b/src/client/globals.onyx new file mode 100644 index 0000000..3c56a03 --- /dev/null +++ b/src/client/globals.onyx @@ -0,0 +1,5 @@ +use package glfw3 + +@GlobalVariable window: GLFWwindow_p; + +debug_screen := false; diff --git a/src/client/main.onyx b/src/client/main.onyx index d55d151..3172252 100644 --- a/src/client/main.onyx +++ b/src/client/main.onyx @@ -2,72 +2,10 @@ use package core use package glfw3 use package opengles -use package stb_truetype -use package core.intrinsics.onyx { __initialize } -#local onet :: package onyx_net #local runtime :: package runtime -#local { - world_shader: Shader; - font: Font; - player: Player; - - chat_font: Font; - - fog_color :: Vector3.{ 0.6, 0.6, 0.6 }; -} - -world: ^World; -selected_block: Vector3i; - -@GlobalVariable -window: GLFWwindow_p; - -create_window :: () => { - #if runtime.compiler_os == .Linux { - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); - glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); - } else { - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); - } - window = glfwCreateWindow(1600, 900, #cstr "Voxel Shooter"); - - glfwMakeContextCurrent(window); - glfwSwapInterval(1); - glfwSetWindowSizeCallback(window, "on_resize"); - glfwSetKeyCallback(window, "on_key"); - glfwSetMouseButtonCallback(window, "on_mouse_button"); -} - -#export "on_resize" (window: GLFWwindow_p, width, height: u32) { - glViewport(0, 0, width, height); - camera_set_window_size(^camera, ~~width, ~~height); - update_view_matrix(); - update_window_matrix(); -} - -cursor_grabbed := false; -toggle_cursor_grabbed :: () { - cursor_grabbed = !cursor_grabbed; - - if cursor_grabbed { - glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); - - if glfwRawMouseMotionSupported() == GLFW_TRUE { - glfwSetInputMode(window, GLFW_RAW_MOUSE_MOTION, GLFW_TRUE); - } - } else { - glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); - - if glfwRawMouseMotionSupported() == GLFW_TRUE { - glfwSetInputMode(window, GLFW_RAW_MOUSE_MOTION, GLFW_FALSE); - } - } -} - -setup_opengl :: () { +init :: () { + window = create_window(); glInit(glfwGetLoadProcAddress()); input_bind_glfw_events(window); @@ -84,25 +22,7 @@ setup_opengl :: () { fonts_init(); immediate_init(); - world_shader = shader_make("assets/shaders/world.glsl"); - - shader_link_world_matrix_block(world_shader); - shader_set_uniform(world_shader, #cstr "u_fog_start", 50.0f); - shader_set_uniform(world_shader, #cstr "u_fog_range", 10.0f); - - font = font_lookup(.{"./assets/fonts/calibri.ttf", 32}); - chat_font = font_lookup(.{"./assets/fonts/calibri.ttf", 16}); - - shader_use(world_shader); - world = world_make(); - player = player_make(); - player.camera = ^camera; - - __initialize(^camera); - camera_set_fov(^camera, 75); - ww, wh: i32; - glfwGetWindowSize(window, ^ww, ^wh); - camera_set_window_size(^camera, ~~ww, ~~wh); + game_init(); update_view_matrix(); update_world_matrix(); @@ -110,54 +30,39 @@ setup_opengl :: () { update_window_matrix(); } -update :: (dt: f32) { - input_update(); - - if is_key_just_down(GLFW_KEY_ESCAPE) { - if cursor_grabbed { - toggle_cursor_grabbed(); - } else { - glfwSetWindowShouldClose(window, true); - } - } - - if cursor_grabbed { - player_update_controls(^player, dt); - player_update(^player, dt); +create_window :: () => { + #if runtime.compiler_os == .Linux { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); + } else { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); } + window := glfwCreateWindow(1600, 900, #cstr "Voxel Shooter"); - player_update_physics(^player, dt); - player_chunk := world_position_to_chunk(world, player.body.pos); - world_move_center(world, player_chunk); - world_update(world, dt); + glfwMakeContextCurrent(window); + glfwSwapInterval(1); + glfwSetWindowSizeCallback(window, "on_resize"); - if is_key_just_down(GLFW_KEY_F7) { - debug_screen = !debug_screen; - } + return window; +} - update_world_matrix(); +#export "on_resize" (window: GLFWwindow_p, width, height: u32) { + glViewport(0, 0, width, height); + camera_set_window_size(^camera, ~~width, ~~height); + update_view_matrix(); + update_window_matrix(); } -#local { - debug_screen := true; - game_fps: i32; +update :: (dt: f32) { + input_update(); + + game_update(dt); } draw :: () { - defer ui_end_frame(); - defer input_post_update(); - draw_scene(); - draw_chat(); - - ww, wh: i32; - glfwGetWindowSize(window, ^ww, ^wh); - font_print(font, ~~(ww / 2), ~~(wh / 2), "."); - - if !cursor_grabbed { - if draw_button(.{ 200, 200, 200, 200 }, "Resume") { - toggle_cursor_grabbed(); - } - } + game_draw(); if debug_screen { immediate_set_color(.{1, 0, 1, 0.5}); @@ -175,59 +80,10 @@ draw :: () { immediate_flush(); glfwSwapBuffers(window); + input_post_update(); + ui_end_frame(); } -draw_scene :: () { - glClearColor(fog_color.x, fog_color.y, fog_color.z, 1); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - shader_use(world_shader); - shader_set_uniform(world_shader, #cstr "u_fog_color", fog_color); - shader_set_uniform(world_shader, #cstr "u_texture", 0); - glEnable(GL_DEPTH_TEST); - world_draw(world); - - glLineWidth(2); - chunk_highlight_block(~~selected_block.x, ~~selected_block.y, ~~selected_block.z); -} - -chat_messages: [..] [] u8; -pending_message: [..] u8; - -draw_chat :: () { - glDisable(GL_DEPTH_TEST); - - ww, wh: i32; - glfwGetWindowSize(window, ^ww, ^wh); - immediate_set_color(.{0.2, 0.2, 0.2, 0.8}); - immediate_rectangle(0, ~~(wh - 300), 400, 300); - - font_set_color(.{1, 1, 1}); - y := cast(f32) (wh - 32); - while i := cast(i32) chat_messages.count - 1; i >= 0 { - defer i -= 1; - y -= 18; - font_draw(chat_font, 10, y, chat_messages[i]); - } - - if draw_textbox(.{0, ~~(wh - 32), 400, 32}, ^pending_message) == .Enter_Pressed { - if pending_message.count > 0 { - send_message(string.alloc_copy(pending_message)); - array.clear(^pending_message); - } - } - - send_message :: (message: [] u8) { - packet := new(onet.Packet); - packet.flags |= .Reliable; - packet.data = message; - onet.peer_send(peer, 0, packet); - } -} - -host: ^onet.Host; -peer: ^onet.Peer; - main_loop :: () { last := glfwGetTime(); now := last; @@ -236,12 +92,6 @@ main_loop :: () { frame_count := 0; while !glfwWindowShouldClose(window) { - for host->get_events(timeout=0) { - if it.type == .Message { - chat_messages << string.alloc_copy(it.data); - } - } - glfwPollEvents(); now = glfwGetTime(); @@ -268,14 +118,10 @@ main :: (args) => { os.exit(1); } - addr: net.Socket_Address; - addr.addr = net.str_to_ipv4("127.0.0.1"); - addr.port = 5123; - host', _ := onet.host_create(null, 1); - println(_); - peer = onet.host_connect(host, ^addr, 2); - - create_window(); - setup_opengl(); + init(); main_loop(); } + + + +#local game_fps: i32; diff --git a/src/client/utils/input.onyx b/src/client/utils/input.onyx index f3bc20b..8e7143b 100644 --- a/src/client/utils/input.onyx +++ b/src/client/utils/input.onyx @@ -9,29 +9,29 @@ Mouse_Offset_Function :: immediate_get_scroll #local { - keys_this_frame : Set(Key_Descriptor) // Keys currently being pressed this frame - keys_last_frame : Set(Key_Descriptor) // Keys being pressed in the last frame + keys_this_frame : Set(Key_Descriptor) // Keys currently being pressed this frame + keys_last_frame : Set(Key_Descriptor) // Keys being pressed in the last frame - key_codepoints : [..] u32 + key_codepoints : [..] u32 - buttons_this_frame: [8] bool // Mouse buttons being pressed this frame - buttons_last_frame: [8] bool // Mouse buttons being pressed last frame + buttons_this_frame: [8] bool // Mouse buttons being pressed this frame + buttons_last_frame: [8] bool // Mouse buttons being pressed last frame - scroll_x: f64 - scroll_y: f64 + scroll_x: f64 + scroll_y: f64 - character_mode := false; + character_mode := false; } #init () { - set.init(^keys_this_frame); - set.init(^keys_last_frame); + set.init(^keys_this_frame); + set.init(^keys_last_frame); } Key_Descriptor :: struct { - key: u32; - scancode: u32 = 0; - mod: u32 = 0; + key: u32; + scancode: u32 = 0; + mod: u32 = 0; } #match hash.to_u32 (use x: Key_Descriptor) => hash.to_u32(key); #operator == macro (x, y: Key_Descriptor) => x.key == y.key; @@ -41,21 +41,21 @@ input_update :: () { } input_post_update :: () { - set.clear(^keys_last_frame); - for keys_this_frame.entries do keys_last_frame << it.value; + set.clear(^keys_last_frame); + for keys_this_frame.entries do keys_last_frame << it.value; - for 8 do buttons_last_frame[it] = buttons_this_frame[it]; + for 8 do buttons_last_frame[it] = buttons_this_frame[it]; - last_mouse_x = mouse_x; - last_mouse_y = mouse_y; - scroll_x = 0; - scroll_y = 0; + last_mouse_x = mouse_x; + last_mouse_y = mouse_y; + scroll_x = 0; + scroll_y = 0; - array.clear(^key_codepoints); + array.clear(^key_codepoints); } input_get_chars_this_frame :: () -> [] u32 { - return key_codepoints; + return key_codepoints; } input_get_keys_this_frame :: () -> Iterator(Key_Descriptor) { @@ -74,15 +74,15 @@ input_get_keys_this_frame :: () -> Iterator(Key_Descriptor) { } index = 0; - return .{ null, next }; + return .{ null, next }; } input_capture_keys :: () { - character_mode = true; + character_mode = true; } input_release_keys :: () { - character_mode = false; + character_mode = false; } is_key_down :: (key) => !character_mode && set.has(^keys_this_frame, .{key}); @@ -94,94 +94,94 @@ is_button_just_down :: (button) => buttons_this_frame[button] && !buttons_last_f is_button_just_up :: (button) => !buttons_this_frame[button] && buttons_last_frame[button]; #local { - last_mouse_x: f64; - last_mouse_y: f64; + last_mouse_x: f64; + last_mouse_y: f64; - mouse_x: f64; - mouse_y: f64; + mouse_x: f64; + mouse_y: f64; } mouse_get_delta :: () -> (f64, f64) { - return mouse_x - last_mouse_x, mouse_y - last_mouse_y; + return mouse_x - last_mouse_x, mouse_y - last_mouse_y; } mouse_get_delta_vector :: () -> Vector2 { - dmx, dmy := mouse_get_delta(); - return .{ ~~dmx, ~~dmy }; + dmx, dmy := mouse_get_delta(); + return .{ ~~dmx, ~~dmy }; } mouse_get_position :: () -> (f64, f64) { - mx, my := mouse_x, mouse_y; - #if #defined(Mouse_Offset_Function) { - scroll := Mouse_Offset_Function(); - mx -= ~~ scroll.x; - my -= ~~ scroll.y; - } - return mx, my; + mx, my := mouse_x, mouse_y; + #if #defined(Mouse_Offset_Function) { + scroll := Mouse_Offset_Function(); + mx -= ~~ scroll.x; + my -= ~~ scroll.y; + } + return mx, my; } mouse_get_position_vector :: () -> Vector2 { - mx, my := mouse_x, mouse_y; - #if #defined(Mouse_Offset_Function) { - scroll := Mouse_Offset_Function(); - mx -= ~~ scroll.x; - my -= ~~ scroll.y; - } - return .{ ~~mx, ~~my }; + mx, my := mouse_x, mouse_y; + #if #defined(Mouse_Offset_Function) { + scroll := Mouse_Offset_Function(); + mx -= ~~ scroll.x; + my -= ~~ scroll.y; + } + return .{ ~~mx, ~~my }; } mouse_get_scroll :: () -> (f64, f64) { - return scroll_x, scroll_y; + return scroll_x, scroll_y; } mouse_get_scroll_vector :: () -> Vector2 { - return .{ ~~scroll_x, ~~scroll_y }; + return .{ ~~scroll_x, ~~scroll_y }; } input_bind_glfw_events :: (window: GLFWwindow_p) { - glfwSetKeyCallback(window, INPUT_KEY_EVENT); - glfwSetCharCallback(window, INPUT_CHAR_EVENT); - glfwSetMouseButtonCallback(window, INPUT_BUTTON_EVENT); - glfwSetScrollCallback(window, INPUT_SCROLL_EVENT); + glfwSetKeyCallback(window, INPUT_KEY_EVENT); + glfwSetCharCallback(window, INPUT_CHAR_EVENT); + glfwSetMouseButtonCallback(window, INPUT_BUTTON_EVENT); + glfwSetScrollCallback(window, INPUT_SCROLL_EVENT); } #local { - INPUT_BUTTON_EVENT :: "__input_button_event" - INPUT_KEY_EVENT :: "__input_key_event" - INPUT_SCROLL_EVENT :: "__input_scroll_event" - INPUT_CHAR_EVENT :: "__input_char_event" + INPUT_BUTTON_EVENT :: "__input_button_event" + INPUT_KEY_EVENT :: "__input_key_event" + INPUT_SCROLL_EVENT :: "__input_scroll_event" + INPUT_CHAR_EVENT :: "__input_char_event" } #export INPUT_BUTTON_EVENT (window: GLFWwindow_p, button, action, mod: u32) { - if action == GLFW_PRESS { - buttons_this_frame[button] = true; - } + if action == GLFW_PRESS { + buttons_this_frame[button] = true; + } - if action == GLFW_RELEASE { - buttons_this_frame[button] = false; - } + if action == GLFW_RELEASE { + buttons_this_frame[button] = false; + } } #export INPUT_KEY_EVENT (window: GLFWwindow_p, key, scancode, action, mod: u32) { - if action == GLFW_PRESS { - keys_this_frame << .{ key, scancode, mod }; - } + if action == GLFW_PRESS { + keys_this_frame << .{ key, scancode, mod }; + } - if action == GLFW_REPEAT { - set.remove(^keys_last_frame, .{key}); - } + if action == GLFW_REPEAT { + set.remove(^keys_last_frame, .{key}); + } - if action == GLFW_RELEASE { - set.remove(^keys_this_frame, .{ key }); - } + if action == GLFW_RELEASE { + set.remove(^keys_this_frame, .{ key }); + } } #export INPUT_CHAR_EVENT (window: GLFWwindow_p, codepoint: u32) { - if !character_mode do return; - key_codepoints << codepoint; + if !character_mode do return; + key_codepoints << codepoint; } #export INPUT_SCROLL_EVENT (window: GLFWwindow_p, xoff, yoff: f64) { - scroll_x = xoff; - scroll_y = yoff; + scroll_x = xoff; + scroll_y = yoff; } diff --git a/src/server/server.onyx b/src/server/server.onyx index 6af2a8e..c25f9a0 100644 --- a/src/server/server.onyx +++ b/src/server/server.onyx @@ -7,7 +7,7 @@ use package core main :: (args) => { addr: net.Socket_Address; - addr.port = 5123; + addr.port = (package runtime.vars).Game_Port; host, err := onet.host_create(^addr, 16); if err != .None { @@ -16,8 +16,17 @@ main :: (args) => { while true { for host->get_events(timeout=1000) { - if it.type == .Connection do printf("{*}\n", it); - if it.type == .Disconnection do printf("{*}\n", it); + 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 { + printf("Disconnection from {}:{}\n", net.ipv4_to_str(it.peer.addr.addr), it.peer.addr.port); + } if it.type == .Message { printf("Message: {}\n", it.data); diff --git a/src/shared/packets.onyx b/src/shared/packets.onyx new file mode 100644 index 0000000..dd39eca --- /dev/null +++ b/src/shared/packets.onyx @@ -0,0 +1,25 @@ + +package packet + +Type :: enum (u8) { + Connect; + Verify_Connect; + Disconnect; + + Player_Joined; + Player_Left; + Player_Moved; +} + +Packet_Base :: struct #pack { + type: Type; +} + +Connect :: struct #pack { + use base: Packet_Base; +} + + + + + -- 2.25.1