#load_all "./utils"
#load_all "./world"
+#load_all "./../shared"
+
// Onyx modules
#load "glfw3/module"
#load "opengles/module"
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) {
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
chunk_highlight_block(~~selected_block.x, ~~selected_block.y, ~~selected_block.z);
}
+@Temporary
chat_messages: [..] [] u8;
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);
}
}
-
net_connect :: (ip_addr: [] u8, port: u16) {
addr: net.Socket_Address;
addr.addr = net.str_to_ipv4(ip_addr);
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 };
}
#load "./../src/config"
#load "server"
+#load "./../shared/packets"
+
#load "onyx-net/src/module"
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 {
}
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();
+}
-package packet
+package packets
Type :: enum (u8) {
Connect;
Connection_Rejected;
Disconnect;
+ Chat_Message;
+
Player_Joined;
Player_Left;
Player_Moved;
Connect :: struct #pack {
use base: Packet_Base;
- name: str;
client_version: u32;
+ name_length: u16;
+ name_data: void;
}
Verify_Connect :: 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;
+}
+