summaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorMichael Smith <mikesmiffy128@gmail.com>2022-07-23 23:47:26 +0100
committerMichael Smith <mikesmiffy128@gmail.com>2022-07-23 23:47:26 +0100
commitc8d7588251fd4fe63ac6afe2a90ca7066c786609 (patch)
treef547831424d20c6f1b3f2b6bdea97c2f53502deb /src
parent8f5673d5f4e63b158b1cd2a2aef874ac83b3662d (diff)
Add event system
Diffstat (limited to 'src')
-rw-r--r--src/build/cmeta.c32
-rw-r--r--src/build/cmeta.h12
-rw-r--r--src/build/codegen.c120
-rw-r--r--src/event.h35
-rw-r--r--src/fov.c4
-rw-r--r--src/fov.h6
-rw-r--r--src/sst.c8
-rw-r--r--src/sst.h26
8 files changed, 234 insertions, 9 deletions
diff --git a/src/build/cmeta.c b/src/build/cmeta.c
index bf18903..3d49927 100644
--- a/src/build/cmeta.c
+++ b/src/build/cmeta.c
@@ -266,4 +266,36 @@ void cmeta_conmacros(const struct cmeta *cm,
}
}
+void cmeta_evdefmacros(const struct cmeta *cm, void (*cb_def)(const char *name)) {
+ Token *tp = (Token *)cm;
+ if (!tp || !tp->next || !tp->next->next) return; // DEF_EVENT, (, name
+ while (tp) {
+ if (equal(tp, "DEF_EVENT") && equal(tp->next, "(")) {
+ tp = tp->next->next;
+ char *name = malloc(tp->len + 1);
+ if (!name) die1("couldn't allocate memory");
+ memcpy(name, tp->loc, tp->len);
+ name[tp->len] = '\0';
+ cb_def(name);
+ }
+ tp = tp->next;
+ }
+}
+
+void cmeta_evhandlermacros(const struct cmeta *cm, const char *modname,
+ void (*cb_handler)(const char *evname, const char *modname)) {
+ Token *tp = (Token *)cm;
+ while (tp) {
+ if (equal(tp, "HANDLE_EVENT") && equal(tp->next, "(")) {
+ tp = tp->next->next;
+ char *name = malloc(tp->len + 1);
+ if (!name) die1("couldn't allocate memory");
+ memcpy(name, tp->loc, tp->len);
+ name[tp->len] = '\0';
+ cb_handler(name, modname);
+ }
+ tp = tp->next;
+ }
+}
+
// vi: sw=4 ts=4 noet tw=80 cc=80
diff --git a/src/build/cmeta.h b/src/build/cmeta.h
index 4757654..125ce2c 100644
--- a/src/build/cmeta.h
+++ b/src/build/cmeta.h
@@ -39,6 +39,18 @@ void cmeta_includes(const struct cmeta *cm,
void cmeta_conmacros(const struct cmeta *cm,
void (*cb)(const char *name, bool isvar, bool unreg));
+/*
+ * Iterates through all event-related macros and takes note of which events are
+ * defined, giving a callback for each.
+ */
+void cmeta_evdefmacros(const struct cmeta *cm, void (*cb)(const char *name));
+
+/*
+ * Iterates through all event-related macros and gives a callback for each event
+ * that is handled by the given module.
+ */
+void cmeta_evhandlermacros(const struct cmeta *cm, const char *modname,
+ void (*cb)(const char *evname, const char *modname));
#endif
// vi: sw=4 ts=4 noet tw=80 cc=80
diff --git a/src/build/codegen.c b/src/build/codegen.c
index d96059d..1dee6a1 100644
--- a/src/build/codegen.c
+++ b/src/build/codegen.c
@@ -20,6 +20,8 @@
#include "../os.h"
#include "cmeta.h"
+#include "skiplist.h"
+#include "vec.h"
#define MAXENT 65536 // arbitrary limit!
static struct ent {
@@ -47,6 +49,51 @@ static void oncondef(const char *name, bool isvar, bool unreg) {
PUT(name, isvar, unreg);
}
+struct vec_str VEC(const char *);
+DECL_SKIPLIST(static, event, struct event, const char *, 4)
+struct event {
+ const char *name;
+ struct vec_str handlers;
+ struct skiplist_hdr_event hdr;
+};
+static inline int cmp_event(struct event *e, const char *s) {
+ return strcmp(e->name, s);
+}
+static inline struct skiplist_hdr_event *hdr_event(struct event *e) {
+ return &e->hdr;
+}
+DEF_SKIPLIST(static, event, cmp_event, hdr_event)
+struct skiplist_hdr_event events = {0};
+
+static void onevdef(const char *name) {
+ struct event *e = skiplist_get_event(&events, name);
+ if (!e) {
+ struct event *e = malloc(sizeof(*e));
+ if (!e) die("couldn't allocate memory");
+ e->name = name;
+ e->handlers = (struct vec_str){0};
+ e->hdr = (struct skiplist_hdr_event){0};
+ skiplist_insert_event(&events, name, e);
+ }
+ else {
+ fprintf(stderr, "codegen: error: duplicate event definition `%s`\n",
+ name);
+ exit(2);
+ }
+}
+
+static void onevhandler(const char *evname, const char *modname) {
+ struct event *e = skiplist_get_event(&events, evname);
+ if (!e) {
+ fprintf(stderr, "codegen: error: module `%s` trying to handle "
+ "non-existent event `%s`\n", modname, evname);
+ exit(2);
+ }
+ // NOTE: not bothering to check for more than one handler in a file.
+ // compiler will get that anyway.
+ if (!vec_push(&e->handlers, modname)) die("couldn't allocate memory");
+}
+
#define _(x) \
if (fprintf(out, "%s\n", x) < 0) die("couldn't write to file");
#define F(f, ...) \
@@ -55,10 +102,67 @@ static void oncondef(const char *name, bool isvar, bool unreg) {
_( "/* This file is autogenerated by src/build/codegen.c. DO NOT EDIT! */") \
_( "")
+struct passinfo {
+ const struct cmeta *cm;
+ const os_char *path;
+};
+struct vec_passinfo VEC(struct passinfo) pass2 = {0};
+
int OS_MAIN(int argc, os_char *argv[]) {
for (++argv; *argv; ++argv) {
const struct cmeta *cm = cmeta_loadfile(*argv);
cmeta_conmacros(cm, &oncondef);
+ cmeta_evdefmacros(cm, &onevdef);
+ if (!vec_push(&pass2, ((struct passinfo){cm, *argv}))) {
+ die("couldn't allocate memory");
+ }
+ }
+
+ // we have to a second pass for event handlers. also, there's a bunch of
+ // terrible garbage here. don't stare for too long...
+ for (struct passinfo *pi = pass2.data; pi - pass2.data < pass2.sz; ++pi) {
+ // XXX: I guess we should cache these by name or something!
+ const struct cmeta *cm = pi->cm;
+#ifdef _WIN32
+ int arglen = wcslen(pi->path);
+ char *p = malloc(arglen + 1);
+ if (!p) die("couldn't allocate string");
+ // XXX: Unicode isn't real, it can't hurt you.
+ for (const ushort *q = pi->path; q - pi->path < arglen; ++q) {
+ p[q - pi->path] = *q; // ugh this is stupid
+ }
+ p[arglen] = '\0';
+#else
+ const char *p = p->path;
+#endif
+ const char *lastslash = p - 1;
+ for (; *p; ++p) {
+#ifdef _WIN32
+ if (*p == '/' || *p == '\\') {
+#else
+ if (*p == '/') {
+#endif
+ lastslash = p;
+ }
+ }
+ int len = strlen(lastslash + 1);
+ if (len <= 3 || lastslash[len - 1] != '.' || lastslash[len] != 'c') {
+ fprintf(stderr, "filenames should end in .c probably\n");
+ exit(2);
+ }
+ char *modname = malloc(len - 1);
+ if (!modname) die("couldn't allocate string");
+ memcpy(modname, lastslash + 1, len - 2);
+ modname[len - 2] = '\0';
+ // ugh. same dumb hacks from compile scripts
+ if (!strcmp(modname, "con_")) {
+ free(modname); // might as well
+ modname = "con";
+ }
+ else if (!strcmp(modname, "sst")) {
+ continue; // I guess???
+ }
+ cmeta_evhandlermacros(cm, modname, &onevhandler);
}
FILE *out = fopen(".build/include/cmdinit.gen.h", "wb");
@@ -86,7 +190,21 @@ F( " extfree(%s->strval);", p->name);
}
}
_( "}")
- if (fflush(out) == EOF) die("couldn't fully write cmdinit.gen.h");
+ if (fclose(out) == EOF) die("couldn't fully write cmdinit.gen.h");
+
+ out = fopen(".build/include/evglue.gen.h", "wb");
+ if (!out) die("couldn't open evglue.gen.h");
+ H()
+ for (const struct event *e = events.x[0]; e; e = e->hdr.x[0]) {
+F( "void _evemit_%s(void) {", e->name)
+ for (const char **pp = e->handlers.data;
+ pp - e->handlers.data < e->handlers.sz; ++pp) {
+F( " void _evhandler_%s_%s(void);", *pp, e->name); // blegh.
+F( " _evhandler_%s_%s();", *pp, e->name);
+ }
+_( "}")
+ }
+ if (fclose(out) == EOF) die("couldn't fully write evglue.gen.h");
return 0;
}
diff --git a/src/event.h b/src/event.h
new file mode 100644
index 0000000..41965e9
--- /dev/null
+++ b/src/event.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright © 2022 Michael Smith <mikesmiffy128@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef INC_EVENT_H
+#define INC_EVENT_H
+
+#define _EVENT_CAT4_(a, b, c, d) a##b##c##d
+#define _EVENT_CAT4(a, b, c, d) _EVENT_CAT4_(a, b, c, d)
+
+#define DECL_EVENT(evname) void _evemit_##evname(void);
+#define DEF_EVENT(evname) \
+ DECL_EVENT(evname) \
+ static inline void _evown_##evname(void) { _evemit_##evname(); }
+#define EMIT_EVENT(evname) _evown_##evname();
+
+#define HANDLE_EVENT(evname) \
+ void _EVENT_CAT4(_evhandler_, MODULE_NAME, _, evname)(void) \
+ /* function body here */
+
+#endif
+
+// vi: sw=4 ts=4 noet tw=80 cc=80
diff --git a/src/fov.c b/src/fov.c
index c090651..4ea9b2d 100644
--- a/src/fov.c
+++ b/src/fov.c
@@ -23,10 +23,12 @@
#include "engineapi.h"
#include "errmsg.h"
#include "ent.h"
+#include "event.h"
#include "gametype.h"
#include "hook.h"
#include "intdefs.h"
#include "mem.h"
+#include "sst.h"
#include "vcall.h"
#include "x86.h"
@@ -74,7 +76,7 @@ static void fovcb(struct con_var *v) {
}
// called by sst.c in ClientActive to ensure fov is applied on load
-void fov_onload(void) {
+HANDLE_EVENT(ClientActive) {
if (real_fov_desired == fov_desired) {
void *player = ent_get(1); // "
if (player) orig_SetDefaultFOV(player, con_getvari(fov_desired));
diff --git a/src/fov.h b/src/fov.h
index a59bbc0..a057fb7 100644
--- a/src/fov.h
+++ b/src/fov.h
@@ -19,15 +19,9 @@
#include <stdbool.h>
-#include "engineapi.h"
-
bool fov_init(bool has_ent);
void fov_end(void);
-// annoying spaghetti, from sst.c. maybe one day there could be some proper
-// event system...
-void fov_onload(void);
-
#endif
// vi: sw=4 ts=4 noet tw=80 cc=80
diff --git a/src/sst.c b/src/sst.c
index 4182b09..d2576f6 100644
--- a/src/sst.c
+++ b/src/sst.c
@@ -31,6 +31,7 @@
#include "engineapi.h"
#include "errmsg.h"
#include "ent.h"
+#include "event.h"
#include "fov.h"
#include "fixes.h"
#include "gameinfo.h"
@@ -473,11 +474,13 @@ DECL_VFUNC_DYN(void, ServerCommand, const char *)
DEF_CVAR(_sst_onload_echo, "EXPERIMENTAL! Don't rely on this existing!", "",
CON_HIDDEN)
+DEF_EVENT(ClientActive)
+
static void VCALLCONV ClientActive(void *this, struct edict *player) {
// XXX: it's kind of dumb that we get handed the edict here then go look it
// up again in fov.c but I can't be bothered refactoring any further now
// that this finally works, do something later lol
- if (has_fov) fov_onload();
+ EMIT_EVENT(ClientActive)
// continuing dumb portal hack. didn't even seem worth adding a feature for
if (has_vtidx_ServerCommand && con_getvarstr(_sst_onload_echo)[0]) {
@@ -530,4 +533,7 @@ EXPORT const void *CreateInterface(const char *name, int *ret) {
return 0;
}
+// no better place to put this lol
+#include <evglue.gen.h>
+
// vi: sw=4 ts=4 noet tw=80 cc=80
diff --git a/src/sst.h b/src/sst.h
new file mode 100644
index 0000000..247346b
--- /dev/null
+++ b/src/sst.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright © 2022 Michael Smith <mikesmiffy128@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef INC_SST_H
+#define INC_SST_H
+
+#include "event.h"
+
+DECL_EVENT(ClientActive)
+
+#endif
+
+// vi: sw=4 ts=4 noet tw=80 cc=80