Made a big mess of the codebase
This commit is contained in:
11
archive/dusk/ecs/CMakeLists.txt
Normal file
11
archive/dusk/ecs/CMakeLists.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
# Copyright (c) 2025 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
# Sources
|
||||
target_sources(${DUSK_TARGET_NAME}
|
||||
PRIVATE
|
||||
ecssystem.c
|
||||
ecscomponent.c
|
||||
)
|
11
archive/dusk/ecs/ecs.h
Normal file
11
archive/dusk/ecs/ecs.h
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "dusk.h"
|
||||
|
||||
typedef int_fast16_t ecsid_t;
|
124
archive/dusk/ecs/ecscomponent.c
Normal file
124
archive/dusk/ecs/ecscomponent.c
Normal file
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "ecscomponent.h"
|
||||
#include "ecssystem.h"
|
||||
#include "assert/assert.h"
|
||||
#include "util/memory.h"
|
||||
|
||||
void ecsComponentInitialize(ecscomponent_t *cmp) {
|
||||
assertNotNull(cmp, "Component pointer cannot be NULL.");
|
||||
assertTrue(
|
||||
ECS_SYSTEM.componentCount < ECS_SYSTEM_ECS_COMPONENTS_MAX,
|
||||
"ECS Component count exceeded maximum limit."
|
||||
);
|
||||
|
||||
memoryZero(cmp->data, sizeof(ecscomponent_t));
|
||||
|
||||
if(cmp->callbacks.init) cmp->callbacks.init();
|
||||
cmp->componentFlags |= ECS_COMPONENT_FLAG_INITIALIZED;
|
||||
|
||||
ECS_SYSTEM.components[ECS_SYSTEM.componentCount++] = cmp;
|
||||
}
|
||||
|
||||
bool_t ecsComponentIsInitialized(const ecscomponent_t *cmp) {
|
||||
assertNotNull(cmp, "Component pointer cannot be NULL.");
|
||||
return (cmp->componentFlags & ECS_COMPONENT_FLAG_INITIALIZED) != 0;
|
||||
}
|
||||
|
||||
|
||||
bool_t ecsComponentDataHas(const ecscomponent_t *cmp, const ecsid_t id) {
|
||||
assertTrue(
|
||||
id >= 0 && id < ECS_ENTITY_COUNT_MAX,
|
||||
"Invalid entity ID."
|
||||
);
|
||||
if(!ecsComponentIsInitialized(cmp)) return false;
|
||||
|
||||
return cmp->entityFlags[id] & ECS_COMPONENT_ENTITY_FLAG_USED;
|
||||
}
|
||||
|
||||
void * ecsComponentDataGet(const ecscomponent_t *cmp, const ecsid_t id) {
|
||||
assertTrue(ecsComponentDataHas(cmp, id), "No data for entity ID.");
|
||||
return (void *)((uint8_t *)cmp->data + (id * cmp->dataSize));
|
||||
}
|
||||
|
||||
void * ecsComponentDataAdd(ecscomponent_t *cmp, const ecsid_t id) {
|
||||
if(!ecsComponentIsInitialized(cmp)) ecsComponentInitialize(cmp);
|
||||
|
||||
assertTrue(id >= 0 && id < ECS_ENTITY_COUNT_MAX, "Invalid entity ID.");
|
||||
assertFalse(ecsComponentDataHas(cmp, id), "Entity already has data.");
|
||||
|
||||
memoryZero(
|
||||
(uint8_t *)cmp->data + (id * cmp->dataSize),
|
||||
cmp->dataSize
|
||||
);
|
||||
cmp->entityFlags[id] |= ECS_COMPONENT_ENTITY_FLAG_USED;
|
||||
if(cmp->callbacks.entityAdd) cmp->callbacks.entityAdd(id);
|
||||
cmp->entitiesWithData[cmp->entitiesWithDataCount++] = id;
|
||||
|
||||
return ecsComponentDataGet(cmp, id);
|
||||
}
|
||||
|
||||
void ecsComponentDataRemove(ecscomponent_t *cmp, const ecsid_t id) {
|
||||
assertTrue(
|
||||
ecsComponentIsInitialized(cmp), "Component was never initialized."
|
||||
);
|
||||
assertTrue(ecsComponentDataHas(cmp, id), "Entity does not have data.");
|
||||
|
||||
cmp->entityFlags[id] = 0;
|
||||
|
||||
// Remove entity from the entitiesWithData array by finding its index and
|
||||
// shifting the rest of the array down. Use memoryCopy to avoid
|
||||
// unnecessary loops.
|
||||
uint32_t index = 0;
|
||||
for(; index < cmp->entitiesWithDataCount; index++) {
|
||||
if(cmp->entitiesWithData[index] == id) break;
|
||||
}
|
||||
assertTrue(
|
||||
index < cmp->entitiesWithDataCount,
|
||||
"Entity not found in entitiesWithData?"
|
||||
);
|
||||
memoryCopy(
|
||||
&cmp->entitiesWithData[index],
|
||||
&cmp->entitiesWithData[index + 1],
|
||||
sizeof(ecsid_t) * (cmp->entitiesWithDataCount - index - 1)
|
||||
);
|
||||
|
||||
if(cmp->callbacks.entityRemove) cmp->callbacks.entityRemove(id);
|
||||
}
|
||||
|
||||
uint32_t ecsComponentGetAll(const ecscomponent_t *cmp, ecsid_t *out) {
|
||||
assertTrue(
|
||||
ecsComponentIsInitialized(cmp),
|
||||
"Component was never initialized."
|
||||
);
|
||||
|
||||
if(!out) return cmp->entitiesWithDataCount;
|
||||
|
||||
memoryCopy(
|
||||
out,
|
||||
cmp->entitiesWithData,
|
||||
sizeof(ecsid_t) * cmp->entitiesWithDataCount
|
||||
);
|
||||
return cmp->entitiesWithDataCount;
|
||||
}
|
||||
|
||||
void ecsComponentDispose(ecscomponent_t *cmp) {
|
||||
assertNotNull(cmp, "Component pointer cannot be NULL.");
|
||||
assertTrue(
|
||||
ecsComponentIsInitialized(cmp),
|
||||
"Component was never initialized."
|
||||
);
|
||||
|
||||
for(uint32_t i = 0; i < cmp->entitiesWithDataCount; i++) {
|
||||
if(cmp->callbacks.entityRemove)
|
||||
cmp->callbacks.entityRemove(cmp->entitiesWithData[i]);
|
||||
}
|
||||
memoryZero(cmp->entityFlags, sizeof(cmp->entityFlags));
|
||||
cmp->entitiesWithDataCount = 0;
|
||||
cmp->componentFlags = 0;
|
||||
}
|
111
archive/dusk/ecs/ecscomponent.h
Normal file
111
archive/dusk/ecs/ecscomponent.h
Normal file
@@ -0,0 +1,111 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "ecs/ecsentity.h"
|
||||
|
||||
#define ECS_COMPONENT_ENTITY_FLAG_USED (1 << 0)
|
||||
|
||||
#define ECS_COMPONENT_FLAG_INITIALIZED (1 << 0)
|
||||
|
||||
typedef struct {
|
||||
void (*init)();
|
||||
void (*entityAdd)(const ecsid_t id);
|
||||
void (*entityRemove)(const ecsid_t id);
|
||||
} ecscomponentcallbacks_t;
|
||||
|
||||
typedef struct {
|
||||
void *data;
|
||||
size_t dataSize;
|
||||
uint8_t entityFlags[ECS_ENTITY_COUNT_MAX];
|
||||
uint8_t componentFlags;
|
||||
ecscomponentcallbacks_t callbacks;
|
||||
|
||||
ecsid_t entitiesWithData[ECS_ENTITY_COUNT_MAX];
|
||||
uint32_t entitiesWithDataCount;;
|
||||
} ecscomponent_t;
|
||||
|
||||
/**
|
||||
* Initializes an ECS Component.
|
||||
*
|
||||
* @param dPointer Pointer to the data that the component owns.
|
||||
* @param cbs Callback functions for the component.
|
||||
*/
|
||||
#define ecsComponentInit(dPointer, cbs) \
|
||||
(ecscomponent_t){ \
|
||||
.data = dPointer, \
|
||||
.dataSize = sizeof(*dPointer), \
|
||||
.entityFlags = 0, \
|
||||
.componentFlags = 0, \
|
||||
.callbacks = (cbs) \
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes an ECS Component.
|
||||
*
|
||||
* @param cmp Pointer to the ecscomponent_t to initialize.
|
||||
*/
|
||||
void ecsComponentInitialize(ecscomponent_t *cmp);
|
||||
|
||||
/**
|
||||
* Checks if the component is initialized.
|
||||
*
|
||||
* @param cmp Pointer to the ecscomponent_t.
|
||||
* @return True if the component is initialized, false otherwise.
|
||||
*/
|
||||
bool_t ecsComponentIsInitialized(const ecscomponent_t *cmp);
|
||||
|
||||
/**
|
||||
* Checks if the component has data for a specific entity.
|
||||
*
|
||||
* @param cmp Pointer to the ecscomponent_t.
|
||||
* @param id The ID of the entity to check.
|
||||
* @return True if the component has data for the entity, false otherwise.
|
||||
*/
|
||||
bool_t ecsComponentDataHas(const ecscomponent_t *cmp, const ecsid_t id);
|
||||
|
||||
/**
|
||||
* Gets the data for a specific entity in the component.
|
||||
*
|
||||
* @param cmp Pointer to the ecscomponent_t.
|
||||
* @param id The ID of the entity to get data for.
|
||||
* @return Pointer to the data for the entity.
|
||||
*/
|
||||
void * ecsComponentDataGet(const ecscomponent_t *cmp, const ecsid_t id);
|
||||
|
||||
/**
|
||||
* Adds data for a specific entity in the component.
|
||||
*
|
||||
* @param cmp Pointer to the ecscomponent_t.
|
||||
* @param id The ID of the entity to add data for.
|
||||
*/
|
||||
void * ecsComponentDataAdd(ecscomponent_t *cmp, const ecsid_t id);
|
||||
|
||||
/**
|
||||
* Removes data for a specific entity in the component.
|
||||
*
|
||||
* @param cmp Pointer to the ecscomponent_t.
|
||||
* @param id The ID of the entity to remove data for.
|
||||
*/
|
||||
void ecsComponentDataRemove(ecscomponent_t *cmp, const ecsid_t id);
|
||||
|
||||
/**
|
||||
* Gets all entities that have data in the component. Passing NULL for out will
|
||||
* just return the count of entities.
|
||||
*
|
||||
* @param cmp Pointer to the ecscomponent_t.
|
||||
* @param out Pointer to an array to store the entity IDs.
|
||||
* @return The number of entities with data.
|
||||
*/
|
||||
uint32_t ecsComponentGetAll(const ecscomponent_t *cmp, ecsid_t *out);
|
||||
|
||||
/**
|
||||
* Disposes the component, freeing any resources it holds.
|
||||
*
|
||||
* @param cmp Pointer to the ecscomponent_t to dispose.
|
||||
*/
|
||||
void ecsComponentDispose(ecscomponent_t *cmp);
|
18
archive/dusk/ecs/ecsentity.h
Normal file
18
archive/dusk/ecs/ecsentity.h
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "ecs.h"
|
||||
|
||||
#define ECS_ENTITY_FLAG_USED (1 << 0)
|
||||
|
||||
#define ECS_ENTITY_COUNT_MAX 2048
|
||||
|
||||
typedef struct ecsentity_s {
|
||||
ecsid_t id;
|
||||
uint8_t flags;
|
||||
} ecsentity_t;
|
66
archive/dusk/ecs/ecssystem.c
Normal file
66
archive/dusk/ecs/ecssystem.c
Normal file
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "ecssystem.h"
|
||||
#include "util/memory.h"
|
||||
#include "assert/assert.h"
|
||||
|
||||
ecssystem_t ECS_SYSTEM;
|
||||
|
||||
void ecsSystemInit() {
|
||||
memoryZero(&ECS_SYSTEM, sizeof(ecssystem_t));
|
||||
|
||||
// Fill ECS ids
|
||||
for(uint32_t i = 0; i < ECS_ENTITY_COUNT_MAX; i++) {
|
||||
ECS_SYSTEM.entities[i].id = i;
|
||||
}
|
||||
|
||||
// Fill the available array.
|
||||
for(uint32_t i = 0; i < ECS_ENTITY_COUNT_MAX; i++) {
|
||||
ECS_SYSTEM.available[i] = &ECS_SYSTEM.entities[i];
|
||||
}
|
||||
ECS_SYSTEM.availableCount = ECS_ENTITY_COUNT_MAX;
|
||||
}
|
||||
|
||||
ecsid_t ecsEntityAdd() {
|
||||
assertTrue(ECS_SYSTEM.availableCount > 0, "No available entities to create");
|
||||
|
||||
// Pop off the last available entity.
|
||||
ecsentity_t *entity = ECS_SYSTEM.available[--ECS_SYSTEM.availableCount];
|
||||
assertTrue((entity->flags & ECS_ENTITY_FLAG_USED) == 0, "Entity is used.");
|
||||
assertTrue(entity->id >= 0, "Entity is invalid.");
|
||||
assertTrue(entity->id < ECS_ENTITY_COUNT_MAX, "Entity ID out of bounds");
|
||||
|
||||
entity->flags |= ECS_ENTITY_FLAG_USED;
|
||||
|
||||
return entity->id;
|
||||
}
|
||||
|
||||
void ecsEntityRemove(const ecsid_t id) {
|
||||
assertTrue(id < ECS_ENTITY_COUNT_MAX, "Invalid entity ID");
|
||||
|
||||
ecsentity_t *entity = &ECS_SYSTEM.entities[id];
|
||||
assertTrue(entity->id >= 0, "Entity is invalid.");
|
||||
assertTrue((entity->flags & ECS_ENTITY_FLAG_USED) != 0, "Entity is not used.");
|
||||
|
||||
// Mark the entity as available.
|
||||
ECS_SYSTEM.available[ECS_SYSTEM.availableCount++] = entity;
|
||||
assertTrue(
|
||||
ECS_SYSTEM.availableCount <= ECS_ENTITY_COUNT_MAX,
|
||||
"Available count exceeded maximum limit"
|
||||
);
|
||||
}
|
||||
|
||||
void ecsSystemDispose() {
|
||||
for(uint32_t i = 0; i < ECS_SYSTEM.componentCount; i++) {
|
||||
ecscomponent_t *cmp = ECS_SYSTEM.components[i];
|
||||
ecsComponentDispose(cmp);
|
||||
assertTrue(cmp->entitiesWithDataCount == 0, "Component still has data.");
|
||||
}
|
||||
|
||||
memoryZero(&ECS_SYSTEM, sizeof(ecssystem_t));
|
||||
}
|
50
archive/dusk/ecs/ecssystem.h
Normal file
50
archive/dusk/ecs/ecssystem.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "ecsentity.h"
|
||||
#include "ecscomponent.h"
|
||||
|
||||
#define ECS_SYSTEM_ECS_COMPONENTS_MAX 64
|
||||
|
||||
typedef struct {
|
||||
ecsentity_t entities[ECS_ENTITY_COUNT_MAX];
|
||||
|
||||
ecsentity_t *available[ECS_ENTITY_COUNT_MAX];
|
||||
uint32_t availableCount;
|
||||
|
||||
ecscomponent_t *components[ECS_SYSTEM_ECS_COMPONENTS_MAX];
|
||||
uint32_t componentCount;
|
||||
} ecssystem_t;
|
||||
|
||||
extern ecssystem_t ECS_SYSTEM;
|
||||
|
||||
/**
|
||||
* Initialize the ECS system.
|
||||
*/
|
||||
void ecsSystemInit();
|
||||
|
||||
/**
|
||||
* Create a new entity in the ECS. This locks an id and gives it to the caller,
|
||||
* who will be responsible for managing the entity's lifecycle.
|
||||
*
|
||||
* @return The ID of the newly created entity.
|
||||
*/
|
||||
ecsid_t ecsEntityAdd();
|
||||
|
||||
/**
|
||||
* Disposes an entity in the ECS. This will free the entity's ID and make it
|
||||
* available for reuse.
|
||||
*
|
||||
* @param id The ID of the entity to destroy.
|
||||
*/
|
||||
void ecsEntityRemove(const ecsid_t id);
|
||||
|
||||
/**
|
||||
* Dispose the ECS system, freeing all resources.
|
||||
*/
|
||||
void ecsSystemDispose();
|
Reference in New Issue
Block a user