working on protocol architecture

This commit is contained in:
Vicente Ferrari Smith 2026-02-02 23:42:54 +01:00
parent 510c19fbd5
commit ee790cf700
9 changed files with 149 additions and 127 deletions

View File

@ -131,6 +131,7 @@ pub fn build(b: *std.Build) void {
.optimize = optimize, .optimize = optimize,
}); });
// shared.linkLibrary(enet.artifact("enet"));
client.root_module.linkLibrary(enet.artifact("enet")); client.root_module.linkLibrary(enet.artifact("enet"));
server.root_module.linkLibrary(enet.artifact("enet")); server.root_module.linkLibrary(enet.artifact("enet"));
} }

View File

@ -44,8 +44,8 @@
.path = "vendor/enet", .path = "vendor/enet",
}, },
.bufzilla = .{ .bufzilla = .{
.url = "https://github.com/theseyan/bufzilla/archive/refs/tags/v0.3.2.tar.gz", .url = "https://github.com/theseyan/bufzilla/archive/refs/tags/v0.3.3.tar.gz",
.hash = "bufzilla-0.3.0-gU6dXi67AQAg3WIFrNQ0iafrvexj3iBwVcczrVzrN3Ir", .hash = "bufzilla-0.3.0-gU6dXie7AQDTXB7ptRjLa1plj3txaUVKFvT39e3v-pop",
}, },
.tracy = .{ .tracy = .{
.path = "vendor/tracy", .path = "vendor/tracy",

View File

@ -134,10 +134,20 @@ pub fn main() !void {
while (enet.enet_host_service(host, &event, 0) > 0) { while (enet.enet_host_service(host, &event, 0) > 0) {
switch (event.type) { switch (event.type) {
enet.ENET_EVENT_TYPE_CONNECT => { enet.ENET_EVENT_TYPE_CONNECT => {
std.log.info("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 => { enet.ENET_EVENT_TYPE_RECEIVE => {
std.log.info("receive", .{}); std.log.info("A packet of length {d} containing {s} was received from {s} on channel {d}.", .{
event.packet.*.dataLength,
event.packet.*.data,
@as([*:0]const u8, @ptrCast(event.peer.*.data.?)),
event.channelID});
enet.enet_packet_destroy(event.packet);
}, },
enet.ENET_EVENT_TYPE_DISCONNECT => { enet.ENET_EVENT_TYPE_DISCONNECT => {
std.log.info("disconnect", .{}); std.log.info("disconnect", .{});
@ -304,7 +314,7 @@ pub fn main() !void {
rl.EndDrawing(); rl.EndDrawing();
drawing_zone.End(); drawing_zone.End();
const swap_zone = tracy.ZoneNC(@src(), "swapScreenBuffer", 0x00_ff_00_00); const swap_zone = tracy.ZoneNC(@src(), "SwapScreenBuffer", 0x00_ff_00_00);
rl.SwapScreenBuffer(); rl.SwapScreenBuffer();
swap_zone.End(); swap_zone.End();

View File

@ -3,6 +3,7 @@ const bufzilla = @import("bufzilla");
const shared = @import("shared"); const shared = @import("shared");
const server = @import("server.zig"); const server = @import("server.zig");
const enet = @import("c.zig").enet;
pub fn spawn(chunk: *shared.chunk.Chunk, comptime T: type, allocator: std.mem.Allocator, value: T) !void { pub fn spawn(chunk: *shared.chunk.Chunk, comptime T: type, allocator: std.mem.Allocator, value: T) !void {
std.debug.assert(value.id == shared.entity.INVALID_ID); std.debug.assert(value.id == shared.entity.INVALID_ID);
@ -14,12 +15,14 @@ pub fn spawn(chunk: *shared.chunk.Chunk, comptime T: type, allocator: std.mem.Al
entity.id = id; entity.id = id;
inline for (@typeInfo(shared.chunk.Chunk).@"struct".fields) |field| { inline for (@typeInfo(shared.chunk.Chunk).@"struct".fields) |field| {
if (field.type == shared.chunk.Storage(T)) { if (field.type == std.ArrayList(T)) {
try @field(chunk, field.name).items.append(allocator, entity); try @field(chunk, field.name).append(allocator, entity);
break; break;
} }
} }
std.log.info("{}", .{entity.to_message()});
// serialize entity // serialize entity
var buffer: [128]u8 = undefined; var buffer: [128]u8 = undefined;
var fixed = std.io.Writer.fixed(&buffer); var fixed = std.io.Writer.fixed(&buffer);
@ -40,3 +43,11 @@ pub fn spawn(chunk: *shared.chunk.Chunk, comptime T: type, allocator: std.mem.Al
// } // }
// } // }
} }
pub fn broadcastChanges(chunk: *shared.chunk.Chunk, host: *enet.ENetHost) !void {
_ = chunk;
const data = "packet";
const packet = enet.enet_packet_create(data, data.len + 1, enet.ENET_PACKET_FLAG_RELIABLE);
enet.enet_host_broadcast(host, 0, packet);
}

View File

@ -14,6 +14,8 @@ var accumulator : f32 = 0;
var k : f32 = 1.0; var k : f32 = 1.0;
var frame : i32 = 0; var frame : i32 = 0;
var send_accumulator : f32 = 0;
var dbg_allocator = std.heap.DebugAllocator(.{}){}; var dbg_allocator = std.heap.DebugAllocator(.{}){};
pub fn main() !void { pub fn main() !void {
@ -45,6 +47,13 @@ pub fn main() !void {
var the_chunk = try shared.chunk.initChunk(allocator); var the_chunk = try shared.chunk.initChunk(allocator);
defer shared.chunk.deinitChunk(&the_chunk, allocator); defer shared.chunk.deinitChunk(&the_chunk, allocator);
try chunk.spawn(&the_chunk, shared.entity.Soldier, allocator, .{
.pos = zm.f32x4(1, 0, 0, 0),
.vel = zm.f32x4(0, 0, 0, 0),
.hp = 10,
});
var old_time = std.time.nanoTimestamp(); var old_time = std.time.nanoTimestamp();
while (true) { while (true) {
@ -57,6 +66,7 @@ pub fn main() !void {
t += frame_time; t += frame_time;
accumulator += frame_time * k; accumulator += frame_time * k;
send_accumulator += frame_time;
var event = enet.ENetEvent{}; var event = enet.ENetEvent{};
while (enet.enet_host_service(host, &event, 0) > 0) { while (enet.enet_host_service(host, &event, 0) > 0) {
@ -70,7 +80,7 @@ pub fn main() !void {
enet.ENET_EVENT_TYPE_DISCONNECT => { enet.ENET_EVENT_TYPE_DISCONNECT => {
std.log.info("disconnect", .{}); std.log.info("disconnect", .{});
}, },
else => {} else => {},
} }
} }
@ -88,13 +98,6 @@ pub fn main() !void {
// const w = &writer.interface; // const w = &writer.interface;
// try shared.protocol.sendHello(w, .{ .msg = "Hello from server!" }); // try shared.protocol.sendHello(w, .{ .msg = "Hello from server!" });
try chunk.spawn(&the_chunk, shared.entity.Elf, allocator, .{
.pos = zm.f32x4(1, 0, 0, 0),
.vel = zm.f32x4(0, 0, 0, 0),
.hp = 10,
});
// try chunk.spawn(&the_chunk, shared.entity.Monster, allocator, .{ // try chunk.spawn(&the_chunk, shared.entity.Monster, allocator, .{
// .pos = zm.f32x4(1, 1, 0, 0), // .pos = zm.f32x4(1, 1, 0, 0),
// .hp = 20, // .hp = 20,
@ -105,7 +108,20 @@ pub fn main() !void {
// .vel = zm.f32x4(0.2, 0, 0, 0), // .vel = zm.f32x4(0.2, 0, 0, 0),
// }, w); // }, w);
while (accumulator > dt * k) {
shared.chunk.updateChunk(&the_chunk); shared.chunk.updateChunk(&the_chunk);
accumulator -= dt * k;
gt += dt * k;
}
if (send_accumulator > 1) {
try chunk.broadcastChanges(&the_chunk, host);
send_accumulator = 0;
}
frame += 1;
} }
} }

3
src/shared/c.zig Normal file
View File

@ -0,0 +1,3 @@
pub const enet = @cImport({
@cInclude("enet/enet.h");
});

View File

@ -2,29 +2,29 @@ const std = @import("std");
const entity = @import("entity.zig"); const entity = @import("entity.zig");
const misc = @import("misc.zig"); const misc = @import("misc.zig");
pub fn Storage(comptime T: type) type { // pub fn Storage(comptime T: type) type {
return struct { // return struct {
items: std.ArrayList(T), // items: std.ArrayList(T),
pub fn init(allocator: std.mem.Allocator) !@This() { // pub fn init(allocator: std.mem.Allocator) !@This() {
return .{ // return .{
.items = try std.ArrayList(T).initCapacity(allocator, 32), // .items = try std.ArrayList(T).initCapacity(allocator, 32),
}; // };
} // }
pub fn deinit(self: *@This(), allocator: std.mem.Allocator) void { // pub fn deinit(self: *@This(), allocator: std.mem.Allocator) void {
self.items.deinit(allocator); // self.items.deinit(allocator);
} // }
pub fn update(self: *@This()) void { // pub fn update(self: *@This()) void {
for (self.items.items, 0..) |*item, i| { // for (self.items.items, 0..) |*item, i| {
_ = i; // _ = i;
if (@hasDecl(T, "update")) // if (@hasDecl(T, "update"))
item.update(); // item.update();
} // }
} // }
}; // };
} // }
pub const Chunk = _Chunk(); pub const Chunk = _Chunk();
@ -36,10 +36,10 @@ fn _Chunk() type {
inline for (entity.EntityKinds, 0..) |T, i| { inline for (entity.EntityKinds, 0..) |T, i| {
fields[i] = .{ fields[i] = .{
.name = misc.short_type_name(T), .name = misc.short_type_name(T),
.type = Storage(T), .type = std.ArrayList(T),
.default_value_ptr = null, .default_value_ptr = null,
.is_comptime = false, .is_comptime = false,
.alignment = @alignOf(Storage(T)), .alignment = @alignOf(std.ArrayList(T)),
}; };
} }
@ -56,14 +56,9 @@ fn _Chunk() type {
pub fn initChunk(allocator: std.mem.Allocator) !Chunk { pub fn initChunk(allocator: std.mem.Allocator) !Chunk {
var chunk: Chunk = undefined; var chunk: Chunk = undefined;
switch (@typeInfo(Chunk)) { inline for (@typeInfo(Chunk).@"struct".fields) |field| {
.@"struct" => |s| { const ListT = field.type;
inline for (s.fields) |field| { @field(chunk, field.name) = try ListT.initCapacity(allocator, 32);
const StorageT = field.type;
@field(chunk, field.name) = try StorageT.init(allocator);
}
},
else => unreachable,
} }
return chunk; return chunk;
@ -81,14 +76,13 @@ pub fn deinitChunk(chunk: *Chunk, allocator: std.mem.Allocator) void {
} }
pub fn updateChunk(chunk: *Chunk) void { pub fn updateChunk(chunk: *Chunk) void {
const info = @typeInfo(Chunk); inline for (@typeInfo(Chunk).@"struct".fields) |field| {
const list = &@field(chunk, field.name);
switch (info) { for (list.items, 0..) |*item, i| {
.@"struct" => |s| { _ = i;
inline for (s.fields) |field| { if (@hasDecl(@TypeOf(item.*), "update"))
@field(chunk, field.name).update(); item.update();
} }
},
else => unreachable,
} }
} }

View File

@ -1,49 +1,61 @@
const std = @import("std"); const std = @import("std");
const zm = @import("zmath"); const zm = @import("zmath");
const protocol = @import("protocol.zig"); const protocol = @import("protocol.zig");
const misc = @import("misc.zig");
pub const id = u64; pub const id = u64;
pub const INVALID_ID: id = 0; pub const INVALID_ID: id = 0;
pub const EntityKinds = .{ pub const EntityKinds = .{
Elf, Soldier,
Cow, Alien,
Pig,
}; };
pub const Elf = struct { pub const Soldier = struct {
id: id = INVALID_ID, id: id = INVALID_ID,
pos: zm.Vec, pos: zm.Vec,
vel: zm.Vec, vel: zm.Vec,
hp: i32, hp: i32,
pub fn update(self: *Elf) void { pub fn update(self: *Soldier) void {
self.pos += self.pos + self.vel; self.pos += self.pos + self.vel;
} }
pub fn to_message(self: *Elf) protocol.Elf_v1 {
return protocol.Elf_v1.init(self);
}
}; };
pub const Cow = struct { pub const Alien = struct {
id: id = INVALID_ID, id: id = INVALID_ID,
pos: zm.Vec, pos: zm.Vec,
vel: zm.Vec, vel: zm.Vec,
hp: i32, hp: i32,
pub fn update(self: *Cow) void { pub fn update(self: *Alien) void {
self.pos = self.pos + self.vel; self.pos = self.pos + self.vel;
} }
}; };
pub const Pig = struct { fn makeUnion() type {
id: id = INVALID_ID, const FieldCount = EntityKinds.len;
pos: zm.Vec,
vel: zm.Vec,
hp: i32,
pub fn update(self: *Pig) void { var fields: [FieldCount]std.builtin.Type.UnionField = undefined;
self.pos = self.pos + self.vel;
} inline for (EntityKinds, 0..) |T, i| {
fields[i] = .{
.name = misc.short_type_name(T),
.type = *T,
.default_value_ptr = null,
.is_comptime = false,
.alignment = @alignOf(*T),
}; };
}
return @Type(.{
.@"union" = .{
.layout = .auto,
.fields = &fields,
.decls = &.{},
.tag_type = u8,
},
});
}
const EntityRef = makeUnion();

View File

@ -12,18 +12,15 @@ pub const Message = union(enum) {
}; };
pub const SpawnEntity = union(enum) { pub const SpawnEntity = union(enum) {
elf_v1: Elf_v1, soldier_v1: Soldier_v1,
cow_v1: Cow_v1, alien_v1: Alien_v1,
pig_v1: Pig_v1,
}; };
pub fn makeSpawnMessage(comptime T: type, e: T) Message { pub fn makeSpawnMessage(comptime T: type, e: T) Message {
if (T == entity.Elf) { if (T == entity.Soldier) {
return .{ .spawn_entity = .{ .elf_v1 = Elf_v1.init(e) } }; return .{ .spawn_entity = .{ .soldier_v1 = Soldier_v1.init(e) } };
} else if (T == entity.Cow) { } else if (T == entity.Alien) {
return .{ .spawn_entity = .{ .cow_v1 = Cow_v1.init(e) } }; return .{ .spawn_entity = .{ .alien_v1 = Alien_v1.init(e) } };
} else if (T == entity.Pig) {
return .{ .spawn_entity = .{ .pig_v1 = Pig_v1.init(e) } };
} }
} }
@ -51,20 +48,26 @@ pub fn makeSpawnMessage(comptime T: type, e: T) Message {
// }; // };
// } // }
pub const Elf_v1 = struct { pub const Soldier_v1 = struct {
id: entity.id = entity.INVALID_ID, id: entity.id = entity.INVALID_ID,
pos: zm.Vec, pos: zm.Vec,
hp: i32, hp: i32,
pub fn init(elf: entity.Elf) Elf_v1 { pub fn init(soldier: entity.Soldier) Soldier_v1 {
return .{ return .{
.id = elf.id, .id = soldier.id,
.pos = elf.pos, .pos = soldier.pos,
.hp = elf.hp, .hp = soldier.hp,
}; };
} }
pub fn decode(r: *bufzilla.Reader) !Elf_v1 { pub fn encode(self: Soldier_v1, w: *std.Io.Writer) !void {
try w.writeInt(u64, self.id, .little);
try writeVec4(w, self.pos);
try writeVec4(w, self.vel);
}
pub fn decode(r: *bufzilla.Reader) !Soldier_v1 {
_ = r; _ = r;
return .{ return .{
// .id = try r.readInt(u64, .little), // .id = try r.readInt(u64, .little),
@ -74,54 +77,26 @@ pub const Elf_v1 = struct {
} }
}; };
pub const Cow_v1 = struct { pub const Alien_v1 = struct {
id: entity.id = entity.INVALID_ID, id: entity.id = entity.INVALID_ID,
pos: zm.Vec, pos: zm.Vec,
hp: i32, hp: i32,
pub fn init(cow: entity.Cow) Cow_v1 { pub fn init(alien: entity.Alien) Alien_v1 {
return .{ return .{
.id = cow.id, .id = alien.id,
.pos = cow.pos, .pos = alien.pos,
.hp = cow.hp, .hp = alien.hp,
}; };
} }
// pub fn encode(self: Cow_v1, w: *std.Io.Writer) !void { // pub fn encode(self: Alien_v1, w: *std.Io.Writer) !void {
// try w.writeInt(u64, self.id, .little); // try w.writeInt(u64, self.id, .little);
// try writeVec4(w, self.pos); // try writeVec4(w, self.pos);
// try writeVec4(w, self.vel); // try writeVec4(w, self.vel);
// } // }
pub fn decode(r: *std.Io.Reader) !Cow_v1 { pub fn decode(r: *std.Io.Reader) !Alien_v1 {
return .{
.id = try r.readInt(u64, .little),
.pos = try readVec4(r),
.vel = try readVec4(r),
};
}
};
pub const Pig_v1 = struct {
id: entity.id = entity.INVALID_ID,
pos: zm.Vec,
vel: zm.Vec,
pub fn init(pig: entity.Pig) Pig_v1 {
return .{
.id = pig.id,
.pos = pig.pos,
.vel = pig.vel,
};
}
// pub fn encode(self: Pig_v1, w: *std.Io.Writer) !void {
// try w.writeInt(u64, self.id, .little);
// try writeVec4(w, self.pos);
// try writeVec4(w, self.vel);
// }
pub fn decode(r: *std.Io.Reader) !Pig_v1 {
return .{ return .{
.id = try r.readInt(u64, .little), .id = try r.readInt(u64, .little),
.pos = try readVec4(r), .pos = try readVec4(r),