initial commit
						commit
						2875fc285d
					
				|  | @ -0,0 +1 @@ | ||||||
|  | zig-cache | ||||||
|  | @ -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); | ||||||
|  | } | ||||||
|  | @ -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); | ||||||
|  | } | ||||||
|  | @ -0,0 +1,2 @@ | ||||||
|  | pub const Entity = @import("entity.zig").Entity; | ||||||
|  | pub const ComponentStore = @import("component_store.zig").ComponentStore; | ||||||
|  | @ -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); | ||||||
|  | } | ||||||
		Loading…
	
		Reference in New Issue