initial commit

main
cosmonaut 2020-12-04 23:37:07 -08:00
commit 2875fc285d
5 changed files with 224 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
zig-cache

18
build.zig Normal file
View File

@ -0,0 +1,18 @@
const Builder = @import("std").build.Builder;
pub fn build(b: *Builder) void {
const mode = b.standardReleaseOptions();
const lib = b.addStaticLibrary("encompass", "src/encompass.zig");
lib.setBuildMode(mode);
lib.install();
var entity_test = b.addTest("src/entity.zig");
entity_test.setBuildMode(mode);
var component_store_test = b.addTest("src/component_store.zig");
component_store_test.setBuildMode(mode);
const test_step = b.step("test", "Run library tests");
test_step.dependOn(&entity_test.step);
test_step.dependOn(&component_store_test.step);
}

185
src/component_store.zig Normal file
View File

@ -0,0 +1,185 @@
const std = @import("std");
const mem = std.mem;
const testing = std.testing;
const Allocator = mem.Allocator;
pub fn ComponentStore(comptime TComponent: type) type {
return struct {
const Self = @This();
nextIndex: usize = 0,
allocator: *Allocator,
entityIDToIndex: std.AutoArrayHashMap(usize, usize),
indexToEntityID: std.ArrayList(usize),
components: std.ArrayList(TComponent),
pub fn init(allocator: *Allocator) Self {
return Self {
.allocator = allocator,
.entityIDToIndex = std.AutoArrayHashMap(usize, usize).init(allocator),
.indexToEntityID = std.ArrayList(usize).init(allocator),
.components = std.ArrayList(TComponent).init(allocator),
};
}
pub fn deinit(self: *Self) void {
self.entityIDToIndex.deinit();
self.indexToEntityID.deinit();
self.components.deinit();
}
pub fn has(self: Self, entityID: usize) bool {
return self.entityIDToIndex.contains(entityID);
}
pub fn set(self: *Self, entityID: usize, component: TComponent) void {
if (!self.entityIDToIndex.contains(entityID)) {
const index = self.nextIndex;
self.nextIndex += 1;
// FIXME: actually handle OOM
self.components.append(component) catch unreachable;
self.entityIDToIndex.put(entityID, index) catch unreachable;
self.indexToEntityID.append(entityID) catch unreachable;
}
else
{
self.components.items[self.entityIDToIndex.get(entityID).?] = component;
}
}
pub fn count(self: Self) usize {
return self.components.items.len;
}
pub fn get(self: Self, entityID: usize) !TComponent {
var index = self.entityIDToIndex.get(entityID) orelse error.NoComponentError;
return self.components.items[try index];
}
pub fn remove(self: *Self, entityID: usize) void {
if (self.entityIDToIndex.contains(entityID)) {
var storageIndex = self.entityIDToIndex.get(entityID).?;
var lastEntity = self.indexToEntityID.items[self.nextIndex - 1];
self.entityIDToIndex.put(lastEntity, storageIndex) catch unreachable;
var removedComponent = self.components.swapRemove(storageIndex);
var removedEntityID = self.indexToEntityID.swapRemove(storageIndex);
var removedEntry = self.entityIDToIndex.remove(entityID);
self.nextIndex -= 1;
}
}
pub fn all_components(self: Self) []TComponent {
return self.components.items;
}
pub fn all_entities(self: Self) []usize {
return self.indexToEntityID.items();
}
};
}
const TestComponent = struct {
number: i32,
};
test "component store set item" {
const component = TestComponent {
.number = 3
};
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const allocator = &arena.allocator;
var component_store = ComponentStore(TestComponent).init(allocator);
component_store.set(12, component);
testing.expect(component_store.count() == 1);
testing.expect(component_store.has(12));
testing.expect((try component_store.get(12)).number == component.number);
}
test "iterate items" {
const component = TestComponent {
.number = 3
};
const componentTwo = TestComponent {
.number = 10
};
const componentThree = TestComponent {
.number = 15
};
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const allocator = &arena.allocator;
var component_store = ComponentStore(TestComponent).init(allocator);
component_store.set(3, component);
component_store.set(5, componentTwo);
component_store.set(8, componentThree);
var all_components = component_store.all_components();
testing.expect(all_components[0].number == 3);
testing.expect(all_components[1].number == 10);
testing.expect(all_components[2].number == 15);
}
test "has item" {
const component = TestComponent {
.number = 3
};
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const allocator = &arena.allocator;
var component_store = ComponentStore(TestComponent).init(allocator);
component_store.set(5, component);
testing.expect(component_store.has(5));
testing.expect(!component_store.has(6));
}
test "remove item" {
const component = TestComponent {
.number = 5
};
const componentTwo = TestComponent {
.number = 87
};
const componentThree = TestComponent {
.number = 124
};
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const allocator = &arena.allocator;
var component_store = ComponentStore(TestComponent).init(allocator);
component_store.set(5, component);
component_store.set(10, componentTwo);
component_store.set(15, componentThree);
component_store.remove(10);
testing.expect(component_store.has(5));
testing.expect(component_store.has(15));
testing.expect(!component_store.has(10));
testing.expect((try component_store.get(5)).number == 5);
testing.expect((try component_store.get(15)).number == 124);
}

2
src/encompass.zig Normal file
View File

@ -0,0 +1,2 @@
pub const Entity = @import("entity.zig").Entity;
pub const ComponentStore = @import("component_store.zig").ComponentStore;

18
src/entity.zig Normal file
View File

@ -0,0 +1,18 @@
const std = @import("std");
const testing = std.testing;
pub const Entity = struct {
ID: u32,
pub fn init(id: u32) Entity {
return .{ .ID = id };
}
};
test "entity init" {
const e1 = Entity.init(12);
const e2 = Entity.init(21);
testing.expect(e1.ID == 12);
testing.expect(e2.ID == 21);
}