const std = @import("std"); const zm = @import("zmath"); // const bufzilla = @import("bufzilla"); const ft = @import("freetype"); const tracy = @import("tracy"); const kb = @import("kb"); const shared = @import("shared"); const c = @import("c.zig").c; const enet = @import("c.zig").enet; const rl = @import("c.zig").rl; const client = @import("client.zig"); const entity = @import("entity.zig"); const misc = @import("misc.zig"); const font = @import("font.zig"); const chunk = @import("chunk.zig"); const dt : f32 = 1.0 / 240.0; var t : f32 = 0; var gt : f32 = 0; var accumulator : f32 = 0; var k : f32 = 1; var frame : i32 = 0; const screen_width = 1280; const screen_height = 720; var running: bool = true; var dbg_allocator = std.heap.DebugAllocator(.{}){}; var the_chunk : shared.chunk.Chunk = undefined; pub fn main() !void { const tracy_zone = tracy.ZoneNC(@src(), "main", 0x00_ff_00_00); defer tracy_zone.End(); const allocator = dbg_allocator.allocator(); defer { const deinit_status = dbg_allocator.deinit(); if (deinit_status == .leak) { @panic("LEAKED!"); } } var stdout = std.fs.File.stdout(); _ = try stdout.write("Hello, Client!\n"); if (enet.enet_initialize() != 0) { std.log.info("Failed to load ENet", .{}); return; } defer enet.enet_deinitialize(); rl.SetConfigFlags(rl.FLAG_WINDOW_HIGHDPI); rl.InitWindow(screen_width, screen_height, "zzz"); defer rl.CloseWindow(); font.ft_lib = try ft.Library.init(); const sizes = [_]i32{ 30, }; var vollkorn: [sizes.len]font.Font = undefined; for (sizes, 0..) |pt, i| { vollkorn[i] = try font.Font.init( "assets/fonts/Vollkorn/static/Vollkorn-Regular.ttf", pt, allocator ); } defer { for (&vollkorn) |*f| f.deinit(allocator); } font.shader = rl.LoadShader(null, "assets/text.frag"); // const test_shader1 = try rl.LoadShader(null, "assets/test_1.frag"); // const test_shader2 = try rl.LoadShader(null, "assets/test_2.frag"); const host = enet.enet_host_create(null, 32, 2, 0, 0); defer enet.enet_host_destroy(host); var address = enet.ENetAddress{ .port = shared.protocol_v1.SERVER_PORT }; _ = enet.enet_address_set_host(&address, "localhost"); const peer = enet.enet_host_connect(host, &address, 2, 0); defer enet.enet_peer_reset(peer); the_chunk = try shared.chunk.Chunk.init(allocator); defer the_chunk.deinit(allocator); // const camera = rl.Camera{ // .fovy = 45, // .position = .{ .x = 0, .y = 0, .z = 10 }, // .projection = .perspective, // .target = .{ .x = 0, .y = 0, .z = 0 }, // .up = .{ .x = 0, .y = 1, .z = 0 } // }; // rl.initAudioDevice(); // if (rl.isAudioDeviceReady()) { // std.log.info("audio device is ready!", .{}); // } else { // std.log.info("audio device is NOT ready!", .{}); // } // const music = try rl.loadMusicStream("assets/romantic-piano-431010.mp3"); // std.log.info("{}", .{rl.isMusicValid(music)}); // rl.playMusicStream(music); // std.log.info("is music playing? {}", .{rl.isMusicStreamPlaying(music)}); var old_time : f32 = @floatCast(rl.GetTime()); while (!rl.WindowShouldClose()) { const tracy_main_zone = tracy.ZoneNC(@src(), "loop", 0x00_aa_33_10); defer tracy_main_zone.End(); const new_time : f32 = @floatCast(rl.GetTime()); var frame_time = new_time - old_time; old_time = new_time; if (frame_time > 0.25) frame_time = 0.25; t += frame_time; accumulator += frame_time * k; var event = enet.ENetEvent{}; while (enet.enet_host_service(host, &event, 0) > 0) { switch (event.type) { enet.ENET_EVENT_TYPE_CONNECT => { std.log.info("A new client connected from {d}:{d}.", .{ event.peer.*.address.host, event.peer.*.address.port }); event.peer.*.data = @constCast(@ptrCast("Client information")); }, enet.ENET_EVENT_TYPE_RECEIVE => { try on_packet(allocator, event.packet, event.peer, event.channelID); enet.enet_packet_destroy(event.packet); }, enet.ENET_EVENT_TYPE_DISCONNECT => { std.log.info("disconnect", .{}); }, else => {} } } rl.PollInputEvents(); // if (peer.state() == .connected) { // const packet = try znet.Packet.init("Hello, Server!", 0, .reliable); // try peer.send(packet); // } // // _ = peer; // rl.updateMusicStream(music); while (accumulator > dt * k) { the_chunk.update(dt * k); accumulator -= dt * k; gt += dt * k; } const drawing_zone = tracy.ZoneNC(@src(), "Drawing", 0x00_ff_00_00); rl.BeginDrawing(); rl.ClearBackground(rl.SKYBLUE); // const lorem = @embedFile("embeds/lorem.txt"); vollkorn[0].render_text( "The night is long and cold outside.", rl.Vector2{ .x = 0, .y = 0 }, true, rl.WHITE, rl.BLANK, false, true ); // rl.beginShaderMode(test_shader1); // rl.gl.rlBegin(rl.gl.rl_quads); // { // const topLeft : rl.Vector2 = .{ // .x = 0.0, // .y = 0.0 // }; // const bottomRight : rl.Vector2 = .{ // .x = @as(f32, @floatFromInt(rl.getScreenWidth())) / 2.0, // .y = @as(f32, @floatFromInt(rl.getScreenHeight())) // }; // rl.gl.rlVertex2f(topLeft.x, topLeft.y); // rl.gl.rlVertex2f(topLeft.x, bottomRight.y); // rl.gl.rlVertex2f(bottomRight.x, bottomRight.y); // rl.gl.rlVertex2f(bottomRight.x, topLeft.y); // } // rl.gl.rlEnd(); // rl.endShaderMode(); // rl.beginShaderMode(test_shader2); // rl.gl.rlBegin(rl.gl.rl_quads); // { // const topLeft : rl.Vector2 = .{ // .x = @as(f32, @floatFromInt(rl.getScreenWidth())) / 2.0, // .y = 0 // }; // const bottomRight : rl.Vector2 = .{ // .x = @floatFromInt(rl.getScreenWidth()), // .y = @floatFromInt(rl.getScreenHeight()) // }; // rl.gl.rlVertex2f(topLeft.x, topLeft.y); // rl.gl.rlVertex2f(topLeft.x, bottomRight.y); // rl.gl.rlVertex2f(bottomRight.x, bottomRight.y); // rl.gl.rlVertex2f(bottomRight.x, topLeft.y); // } // rl.gl.rlEnd(); // rl.endShaderMode(); // rl.beginShaderMode(test_shader2); // rl.gl.rlBegin(rl.gl.rl_quads); // { // const topLeft : rl.Vector2 = .{ .x = @as(f32, @floatFromInt(rl.getScreenWidth())) / 2.0, .y = 0 }; // const bottomRight : rl.Vector2 = .{ // .x = @floatFromInt(rl.getScreenWidth()), // .y = @floatFromInt(rl.getScreenHeight()) // }; // rl.gl.rlVertex2f(topLeft.x, topLeft.y); // rl.gl.rlVertex2f(topLeft.x, bottomRight.y); // rl.gl.rlVertex2f(bottomRight.x, bottomRight.y); // rl.gl.rlVertex2f(bottomRight.x, topLeft.y); // } // rl.gl.rlEnd(); // rl.endShaderMode(); // rl.drawRectangle(400, 0, 400, 450, rl.Color{ .r = 54, .g = 54, .b = 54, .a = 255 }); // f.texture.drawPro( // .{.x = 0, .y = 0, .width = 4096, .height = 4096}, // .{.x = 0, .y = 0, .width = 512, .height = 512 }, // .zero(), 0, .white); // const connected_text = "Connected"; //const not_connected_text = "Not Connected"; // switch (peer.state()) { // .connected => rl.drawText( // connected_text, // @divFloor(rl.getScreenWidth(), 2) - @divFloor(rl.measureText(connected_text, f.baseSize), 2), // 50, // 20, // .white // ), // else => rl.drawText( // not_connected_text, // @divFloor(rl.getScreenWidth(), 2) - @divFloor(rl.measureText(not_connected_text, f.baseSize), 1), // 50, // 20, // .white // ), // } //@divFloor(rl.getScreenWidth(), 2) - @divFloor(rl.measureText(connected_text, f.baseSize), 2), 50 // const font_size : i32 = 180; // const text_size = rl.Vector2{.x = @floatFromInt(rl.measureText(connected_text, font_size)), // .y = font_size};//rl.measureTextEx(try rl.getFontDefault(), connected_text, font_size, font_size / 10); // const pos = rl.Vector2{.x = 0, .y = 0}; // rl.drawText(connected_text, pos.x, pos.y, font_size, .white); // rl.drawRectangleLines(pos.x, pos.y, @intFromFloat(text_size.x), @intFromFloat(text_size.y), .red); // rl.drawRectangle(pos.x, pos.y, rl.getScreenWidth(), rl.getScreenHeight(), .white); // rl.drawTexturePro(tx, // rl.Rectangle{.x = 0, .y = 0, .width = 32, .height = 32}, // rl.Rectangle{.x = 100, .y = 100, .width = 500, .height = 500}, // .{.x = 0, .y = 0}, // 0, // .white); //rl.drawLineV(.{.x = @floatFromInt(@divFloor(rl.getScreenWidth(), 2)), .y = 0}, .{.x = @floatFromInt(@divFloor(rl.getScreenWidth(), 2)), // .y = @floatFromInt(rl.getScreenHeight())}, .red); //rl.drawText("Congrats! You created your first window!", rl.getMouseX(), rl.getMouseY(), 20, .white); //rl.drawRectangleLines(0, 0, 100, 100, .red); misc.drawFPS(0, 0, frame_time); //elf.draw(); // rl.beginMode3D(camera); // rl.drawSphere(.{ .x = 0, .y = 0, .z = 0 }, 1, .red); // rl.endMode3D(); rl.EndDrawing(); drawing_zone.End(); const swap_zone = tracy.ZoneNC(@src(), "SwapScreenBuffer", 0x00_ff_00_00); rl.SwapScreenBuffer(); swap_zone.End(); frame += 1; tracy.FrameMark(); } } fn on_packet(allocator: std.mem.Allocator, packet: *enet.ENetPacket, peer: *enet.ENetPeer, channelID: i32) !void { // std.log.info("A packet of length {d} containing {s} was received from {s} on channel {d}.", .{ // packet.*.dataLength, // packet.*.data, // @as([*:0]const u8, @ptrCast(peer.*.data.?)), // channelID}); _ = peer; _ = channelID; const bytes: []const u8 = packet.*.data[0 .. packet.*.dataLength]; var reader = shared.bits.BitReader.init(bytes); const msg : shared.protocol_v1.Message = try reader.deserialize(allocator, shared.protocol_v1.Message); try on_message(allocator, msg); } fn on_message(allocator: std.mem.Allocator, msg: shared.protocol_v1.Message) !void { switch (msg) { .spawn_entity => |se| { try on_spawn_entity(allocator, se); }, .update_entity => |ue| { on_update_entity(ue); }, .despawn_entity => |de| { on_despawn_entity(allocator, de); }, } } fn on_spawn_entity(allocator: std.mem.Allocator, se: shared.protocol_v1.SpawnEntity) !void { // _ = allocator; _ = se; switch (se) { .soldier => |s| { const soldier = shared.entity.Soldier{ .id = s.id, .hp = s.hp, .pos = s.pos, .vel = s.vel, }; _ = try chunk.spawn(&the_chunk, allocator, shared.entity.Soldier, soldier); }, .alien => |a| { const alien = shared.entity.Alien{ .id = a.id, .hp = a.hp, .pos = a.pos, }; _ = try chunk.spawn(&the_chunk, allocator, shared.entity.Alien, alien); }, } } fn on_update_entity(ue: shared.protocol_v1.UpdateEntity) void { switch (ue) { .soldier => |s| { if (chunk.findT(&the_chunk, shared.entity.Soldier, s.id)) |soldier| { soldier.hp = s.hp; soldier.pos = s.pos; soldier.vel = s.vel; } }, .alien => |a| { if (chunk.findT(&the_chunk, shared.entity.Alien, a.id)) |alien| { alien.hp = a.hp; alien.pos = a.pos; } }, } } fn on_despawn_entity(allocator: std.mem.Allocator, de: shared.protocol_v1.DespawnEntity) void { switch (de) { .soldier => |s| { _ = chunk.despawn(&the_chunk, allocator, s.id); }, .alien => |a| { _ = chunk.despawn(&the_chunk, allocator, a.id); }, } } fn connect() !void { // const address = try std.net.Address.parseIp4("127.0.0.1", shared.protocol_v1.SERVER_PORT); // connection = try std.net.tcpConnectToAddress(address); // std.log.info("Connected to server", .{}); } //fn handle_connection(connection: std.net.Server.Connection) !void {} // test "simple test" { // const gpa = std.testing.allocator; // var list: std.ArrayList(i32) = .empty; // defer list.deinit(gpa); // Try commenting this out and see if zig detects the memory leak! // try list.append(gpa, 42); // try std.testing.expectEqual(@as(i32, 42), list.pop()); // } // test "fuzz example" { // const Context = struct { // fn testOne(context: @This(), input: []const u8) anyerror!void { // _ = context; // // Try passing `--fuzz` to `zig build test` and see if it manages to fail this test case! // try std.testing.expect(!std.mem.eql(u8, "canyoufindme", input)); // } // }; // try std.testing.fuzz(Context{}, Context.testOne, .{}); // }