"Boxed" entities; facility to bind tasks to entity lifetime

This commit is contained in:
Andrei Alexeyev 2019-07-16 08:07:30 +03:00
parent 6a8ed64adb
commit 1579082e30
No known key found for this signature in database
GPG key ID: 363707CD4C7FE8A4
5 changed files with 66 additions and 13 deletions

View file

@ -16,6 +16,7 @@
struct CoTask {
LIST_INTERFACE(CoTask);
koishi_coroutine_t ko;
BoxedEntity bound_ent;
};
struct CoSched {
@ -44,10 +45,20 @@ CoTask *cotask_new(CoTaskFunc func) {
}
void cotask_free(CoTask *task) {
memset(&task->bound_ent, 0, sizeof(task->bound_ent));
alist_push(&task_pool, task);
}
CoTask *cotask_active(void) {
return CASTPTR_ASSUME_ALIGNED((char*)koishi_active() - offsetof(CoTask, ko), CoTask);
}
void *cotask_resume(CoTask *task, void *arg) {
if(task->bound_ent.ent && !ent_unbox(task->bound_ent)) {
koishi_kill(&task->ko);
return NULL;
}
return koishi_resume(&task->ko, arg);
}
@ -59,6 +70,11 @@ CoStatus cotask_status(CoTask *task) {
return koishi_state(&task->ko);
}
void cotask_bind_to_entity(CoTask *task, EntityInterface *ent) {
assert(task->bound_ent.ent == 0);
task->bound_ent = ent_box(ent);
}
CoSched *cosched_new(void) {
CoSched *sched = calloc(1, sizeof(*sched));
return sched;

View file

@ -11,6 +11,7 @@
#include "taisei.h"
#include "entity.h"
#include <koishi.h>
typedef struct CoTask CoTask;
@ -34,6 +35,8 @@ void cotask_free(CoTask *task);
void *cotask_resume(CoTask *task, void *arg);
void *cotask_yield(void *arg);
CoStatus cotask_status(CoTask *task);
CoTask *cotask_active(void);
void cotask_bind_to_entity(CoTask *task, EntityInterface *ent);
CoSched *cosched_new(void);
void *cosched_new_task(CoSched *sched, CoTaskFunc func, void *arg); // creates and runs the task, returns whatever it yields, schedules it for resume on cosched_run_tasks if it's still alive
@ -64,4 +67,6 @@ static inline attr_must_inline void cosched_set_invoke_target(CoSched *sched) {
#define BYIELD do { YIELD; CHECK_BREAK; } while(0)
#define BWAIT(frames) do { WAIT(frames); CHECK_BREAK; } while(0)
#define TASK_BIND(ent) cotask_bind_to_entity(cotask_active(), &ent->entity_interface)
#endif // IGUARD_coroutine_h

View file

@ -92,7 +92,7 @@ void ent_register(EntityInterface *ent, EntityType type) {
// This is not really an error, but it may result in weird draw order
// in some corner cases. If this overflow ever actually occurs, though,
// then you've probably got much bigger problems.
log_debug("spawn_id just overflowed. You might be spawning stuff waaaay too often");
log_warn("spawn_id just overflowed. You might be spawning stuff waaaay too often");
}
if(entities.capacity < entities.num) {
@ -107,6 +107,7 @@ void ent_register(EntityInterface *ent, EntityType type) {
}
void ent_unregister(EntityInterface *ent) {
ent->spawn_id = 0;
EntityInterface *sub = entities.array[--entities.num];
assert(ent->index <= entities.num);
assert(entities.array[ent->index] == ent);
@ -242,3 +243,20 @@ void ent_hook_post_draw(EntityDrawHookCallback callback, void *arg) {
void ent_unhook_post_draw(EntityDrawHookCallback callback) {
remove_hook(&entities.hooks.post_draw, callback);
}
BoxedEntity ent_box(EntityInterface *ent) {
BoxedEntity h;
h.ent = (uintptr_t)ent;
h.spawn_id = ent->spawn_id;
return h;
}
EntityInterface *ent_unbox(BoxedEntity box) {
EntityInterface *e = (EntityInterface*)box.ent;
if(e->spawn_id == box.spawn_id) {
return e;
}
return NULL;
}

View file

@ -51,6 +51,7 @@ typedef enum EntityType {
typedef struct EntityInterface EntityInterface;
typedef struct EntityListNode EntityListNode;
typedef struct BoxedEntity BoxedEntity;
typedef enum DamageType {
DMG_UNDEFINED,
@ -109,6 +110,11 @@ struct EntityInterface {
ENTITY_INTERFACE_BASE(EntityInterface);
};
struct BoxedEntity {
uintptr_t ent; // not an actual pointer to avert the temptation to use it directly.
uint_fast32_t spawn_id;
};
INLINE const char* ent_type_name(EntityType type) {
switch(type) {
#define ENT_TYPE(typename, id) case id: return #id;
@ -152,4 +158,18 @@ void ent_unhook_pre_draw(EntityDrawHookCallback callback);
void ent_hook_post_draw(EntityDrawHookCallback callback, void *arg);
void ent_unhook_post_draw(EntityDrawHookCallback callback);
BoxedEntity ent_box(EntityInterface *ent);
EntityInterface *ent_unbox(BoxedEntity box);
#define ENT_BOX(ent) ent_box(&(ent)->entity_interface)
#if defined(USE_GNU_EXTENSIONS) && defined(DEBUG)
#define ENT_UNBOX(h, typename) (__extension__ ({ \
EntityInterface *_unboxed_ent = ent_unbox(h); \
_unboxed_ent ? ENT_CAST(_unboxed_ent, typename) : NULL; \
}))
#else
#define ENT_UNBOX(h, typename) CASTPTR_ASSUME_ALIGNED(ent_unbox(h), typename)
#endif
#endif // IGUARD_entity_h

View file

@ -18,21 +18,19 @@ static void cotest_stub_proc(void) { }
TASK(test_enemy, { double hp; complex pos; complex dir; }) {
Enemy *e = create_enemy1c(ARGS.pos, ARGS.hp, BigFairy, NULL, 0);
int eref = add_ref(e);
TASK_BIND(e);
#define BREAK_CONDITION !UPDATE_REF(eref, e)
BYIELD;
YIELD;
while(true) {
// wander around for a bit...
for(int i = 0; i < 20; ++i) {
e->pos += ARGS.dir;
BYIELD;
YIELD;
}
// stop and...
BWAIT(10);
WAIT(10);
// pew pew!!!
int pcount = 10 + 10 * frand();
@ -48,17 +46,13 @@ TASK(test_enemy, { double hp; complex pos; complex dir; }) {
.args = { aim },
);
BWAIT(3);
WAIT(3);
}
// keep wandering, randomly
ARGS.dir *= cexp(I*M_PI*nfrand());
BYIELD;
YIELD;
}
BREAK:
log_debug("enemy died!");
free_ref(eref);
}
TASK(stage_main, { int ignored; }) {