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,
});
// shared.linkLibrary(enet.artifact("enet"));
client.root_module.linkLibrary(enet.artifact("enet"));
server.root_module.linkLibrary(enet.artifact("enet"));
}

View File

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

View File

@ -134,10 +134,20 @@ pub fn main() !void {
while (enet.enet_host_service(host, &event, 0) > 0) {
switch (event.type) {
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 => {
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 => {
std.log.info("disconnect", .{});
@ -304,7 +314,7 @@ pub fn main() !void {
rl.EndDrawing();
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();
swap_zone.End();

View File

@ -3,6 +3,7 @@ const bufzilla = @import("bufzilla");
const shared = @import("shared");
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 {
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;
inline for (@typeInfo(shared.chunk.Chunk).@"struct".fields) |field| {
if (field.type == shared.chunk.Storage(T)) {
try @field(chunk, field.name).items.append(allocator, entity);
if (field.type == std.ArrayList(T)) {
try @field(chunk, field.name).append(allocator, entity);
break;
}
}
std.log.info("{}", .{entity.to_message()});
// serialize entity
var buffer: [128]u8 = undefined;
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 frame : i32 = 0;
var send_accumulator : f32 = 0;
var dbg_allocator = std.heap.DebugAllocator(.{}){};
pub fn main() !void {
@ -45,6 +47,13 @@ pub fn main() !void {
var the_chunk = try shared.chunk.initChunk(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();
while (true) {
@ -57,6 +66,7 @@ pub fn main() !void {
t += frame_time;
accumulator += frame_time * k;
send_accumulator += frame_time;
var event = enet.ENetEvent{};
while (enet.enet_host_service(host, &event, 0) > 0) {
@ -70,7 +80,7 @@ pub fn main() !void {
enet.ENET_EVENT_TYPE_DISCONNECT => {
std.log.info("disconnect", .{});
},
else => {}
else => {},
}
}
@ -88,13 +98,6 @@ pub fn main() !void {
// const w = &writer.interface;
// 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, .{
// .pos = zm.f32x4(1, 1, 0, 0),
// .hp = 20,
@ -105,7 +108,20 @@ pub fn main() !void {
// .vel = zm.f32x4(0.2, 0, 0, 0),
// }, w);
shared.chunk.updateChunk(&the_chunk);
while (accumulator > dt * k) {
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

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

View File

@ -1,49 +1,61 @@
const std = @import("std");
const zm = @import("zmath");
const std = @import("std");
const zm = @import("zmath");
const protocol = @import("protocol.zig");
const misc = @import("misc.zig");
pub const id = u64;
pub const INVALID_ID: id = 0;
pub const EntityKinds = .{
Elf,
Cow,
Pig,
Soldier,
Alien,
};
pub const Elf = struct {
pub const Soldier = struct {
id: id = INVALID_ID,
pos: zm.Vec,
vel: zm.Vec,
hp: i32,
pub fn update(self: *Elf) void {
pub fn update(self: *Soldier) void {
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,
pos: zm.Vec,
vel: zm.Vec,
hp: i32,
pub fn update(self: *Cow) void {
pub fn update(self: *Alien) void {
self.pos = self.pos + self.vel;
}
};
pub const Pig = struct {
id: id = INVALID_ID,
pos: zm.Vec,
vel: zm.Vec,
hp: i32,
fn makeUnion() type {
const FieldCount = EntityKinds.len;
pub fn update(self: *Pig) void {
self.pos = self.pos + self.vel;
var fields: [FieldCount]std.builtin.Type.UnionField = undefined;
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) {
elf_v1: Elf_v1,
cow_v1: Cow_v1,
pig_v1: Pig_v1,
soldier_v1: Soldier_v1,
alien_v1: Alien_v1,
};
pub fn makeSpawnMessage(comptime T: type, e: T) Message {
if (T == entity.Elf) {
return .{ .spawn_entity = .{ .elf_v1 = Elf_v1.init(e) } };
} else if (T == entity.Cow) {
return .{ .spawn_entity = .{ .cow_v1 = Cow_v1.init(e) } };
} else if (T == entity.Pig) {
return .{ .spawn_entity = .{ .pig_v1 = Pig_v1.init(e) } };
if (T == entity.Soldier) {
return .{ .spawn_entity = .{ .soldier_v1 = Soldier_v1.init(e) } };
} else if (T == entity.Alien) {
return .{ .spawn_entity = .{ .alien_v1 = Alien_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,
pos: zm.Vec,
hp: i32,
pub fn init(elf: entity.Elf) Elf_v1 {
pub fn init(soldier: entity.Soldier) Soldier_v1 {
return .{
.id = elf.id,
.pos = elf.pos,
.hp = elf.hp,
.id = soldier.id,
.pos = soldier.pos,
.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;
return .{
// .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,
pos: zm.Vec,
hp: i32,
pub fn init(cow: entity.Cow) Cow_v1 {
pub fn init(alien: entity.Alien) Alien_v1 {
return .{
.id = cow.id,
.pos = cow.pos,
.hp = cow.hp,
.id = alien.id,
.pos = alien.pos,
.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 writeVec4(w, self.pos);
// try writeVec4(w, self.vel);
// }
pub fn decode(r: *std.Io.Reader) !Cow_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 {
pub fn decode(r: *std.Io.Reader) !Alien_v1 {
return .{
.id = try r.readInt(u64, .little),
.pos = try readVec4(r),