From cc7172f8121dc64c7516b35cd5d19942bdb3f595 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Fri, 5 Mar 2010 09:22:33 +0100 Subject: [PATCH] eventbus WIP --- src/combined/util.c | 2 + src/common/util.vcproj | 16 ++++++++ src/common/util/argstack.c | 66 +++++++++++++++++++++++++++++++ src/common/util/argstack.h | 27 +++++++++++++ src/common/util/eventbus.c | 56 ++++++++++++++++++++++++++ src/common/util/eventbus.h | 29 ++++++++++++++ src/eressea/tolua/bind_attrib.c | 15 +++++++ src/eressea/tolua/bindings.c | 70 +++++++++++++++++++++++++++++++++ src/res/messages.xml | 7 ++++ src/scripts/callbacks.lua | 11 ++++++ src/scripts/default.lua | 2 + src/scripts/eressea/alp.lua | 54 ++++++++++++++++--------- src/scripts/tests/common.lua | 14 ++++++- 13 files changed, 348 insertions(+), 21 deletions(-) create mode 100644 src/common/util/argstack.c create mode 100644 src/common/util/argstack.h create mode 100644 src/common/util/eventbus.c create mode 100644 src/common/util/eventbus.h create mode 100644 src/scripts/callbacks.lua diff --git a/src/combined/util.c b/src/combined/util.c index 0ec07a9de..899f85524 100644 --- a/src/combined/util.c +++ b/src/combined/util.c @@ -4,11 +4,13 @@ #include "common/iniparser/iniparser.c" #include "common/util/attrib.c" +#include "common/util/argstack.c" #include "common/util/base36.c" #include "common/util/crmessage.c" #include "common/util/cvector.c" #include "common/util/dice.c" #include "common/util/event.c" +#include "common/util/eventbus.c" #include "common/util/filereader.c" #include "common/util/functions.c" #include "common/util/goodies.c" diff --git a/src/common/util.vcproj b/src/common/util.vcproj index 849600b29..acc3d2d8b 100644 --- a/src/common/util.vcproj +++ b/src/common/util.vcproj @@ -224,6 +224,14 @@ + + + + @@ -276,6 +284,14 @@ RelativePath=".\util\event.h" > + + + + diff --git a/src/common/util/argstack.c b/src/common/util/argstack.c new file mode 100644 index 000000000..614a18def --- /dev/null +++ b/src/common/util/argstack.c @@ -0,0 +1,66 @@ +#include "argstack.h" +#include + +#define ARGSTKSIZE 16 +typedef struct arguments { + int n; + struct value { + int type; + union { + double number; + const char * str; + void * data; + } value; + } stack[ARGSTKSIZE]; +} arguments; + +void arg_init(struct arguments * arg) { + arg->n = 0; +} + +void arg_done(struct arguments * arg) { + arg = arg; +} + +void arg_pushuserdata(struct arguments * arg, int type, void * data) { + assert(arg->nstack[arg->n].type = type; + arg->stack[arg->n].value.data = data; + ++arg->n; +} + +void arg_pushstring(struct arguments * arg, const char * str) { + assert(arg->nstack[arg->n].type = ARG_TSTRING; + arg->stack[arg->n].value.str = str; + ++arg->n; +} + +void arg_pushnumber(struct arguments * arg, double number) { + assert(arg->nstack[arg->n].type = ARG_TSTRING; + arg->stack[arg->n].value.number = number; + ++arg->n; +} + +int arg_size(struct arguments * arg) { + return arg->n; +} + +void * arg_touserdata(struct arguments * arg, int idx, int type) { + assert(arg->n>idx && idx>=0); + assert(arg->stack[arg->n].type==type); + return arg->stack[arg->n].value.data; +} + +double arg_tonumber(struct arguments * arg, int idx) { + assert(arg->n>idx && idx>=0); + assert(arg->stack[arg->n].type==ARG_TNUMBER); + return arg->stack[arg->n].value.number; +} + +const char * arg_tostring(struct arguments * arg, int idx) { + assert(arg->n>idx && idx>=0); + assert(arg->stack[arg->n].type==ARG_TSTRING); + return arg->stack[arg->n].value.str; +} diff --git a/src/common/util/argstack.h b/src/common/util/argstack.h new file mode 100644 index 000000000..b4a0dc420 --- /dev/null +++ b/src/common/util/argstack.h @@ -0,0 +1,27 @@ +#ifndef UTIL_ARG_H +#define UTIL_ARG_H +#ifdef __cplusplus +extern "C" { +#endif + +struct arguments; + +#define ARG_TNUMBER -1 +#define ARG_TSTRING -2 + +void arg_init(struct arguments * arg); +void arg_done(struct arguments * arg); + +void arg_push(struct arguments * arg, int type, void * data); +void arg_pushstring(struct arguments * arg, const char * str); +void arg_pushnumber(struct arguments * arg, double number); + +int arg_size(struct arguments * arg); +void * arg_touserdata(struct arguments * arg, int idx, int type); +double arg_tonumber(struct arguments * arg, int idx); +const char * arg_tostring(struct arguments * arg, int idx); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/common/util/eventbus.c b/src/common/util/eventbus.c new file mode 100644 index 000000000..71087ec9b --- /dev/null +++ b/src/common/util/eventbus.c @@ -0,0 +1,56 @@ +#include +#include "eventbus.h" + +/* +** first iteration. it is slow, and a proof of the concept - the simplest +** thing that would work, a.k.a. should be refectored when required. +*/ + +typedef struct listener { + struct listener * next; + event_handler callback; + event_arg_free destroy; + void * sender; + char * event; + void * arguments; +} listener; + +static listener * listeners; + +void +eventbus_fire(void * sender, const char * event, void * args) +{ + listener * lst = listeners; + while (lst) { + int i = strcmp(lst->event, event); + if (i>0) break; + if (i==0) { + if (!lst->sender || lst->sender==sender) { + lst->callback(sender, event, args); + } + } + lst = lst->next; + } +} + +void +eventbus_register(void * sender, const char * event, event_handler cb, event_arg_free arg_free, void * args) +{ + listener * lst; + listener ** lstp = &listeners; + while (*lstp) { + lst = *lstp; + if (strcmp(lst->event, event)>=0) { + break; + } + lstp=&lst->next; + } + lst = malloc(sizeof(listener)); + lst->sender = sender; + lst->arguments = args; + lst->callback = cb; + lst->destroy = arg_free; + lst->event = strdup(event); + lst->next = *lstp; + *lstp = lst; +} diff --git a/src/common/util/eventbus.h b/src/common/util/eventbus.h new file mode 100644 index 000000000..b217c686c --- /dev/null +++ b/src/common/util/eventbus.h @@ -0,0 +1,29 @@ +/* vi: set ts=2: + +-------------------+ + | | Enno Rehling + | Eressea PBEM host | Katja Zedel + | (c) 1998 - 2010 | Christian Schlittchen + | | + +-------------------+ + + This program may not be used, modified or distributed + without prior permission by the authors of Eressea. +*/ + +#ifndef H_UTIL_EVTBUS +#define H_UTIL_EVTBUS + + +#ifdef __cplusplus +extern "C" { +#endif + + typedef void (*event_handler)(void *, const char *, void *); + typedef void (*event_arg_free)(void *); + void eventbus_fire(void * sender, const char * event, void * args); + void eventbus_register(void * sender, const char * event, event_handler callback, event_arg_free arg_free, void * args); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/eressea/tolua/bind_attrib.c b/src/eressea/tolua/bind_attrib.c index dac89dbbe..e72ce2dc6 100644 --- a/src/eressea/tolua/bind_attrib.c +++ b/src/eressea/tolua/bind_attrib.c @@ -32,6 +32,21 @@ without prior permission by the authors of Eressea. static void init_ext(attrib * a) { + lua_State * L = (lua_State *)global.vm_state; + + lua_pushstring(L, "callbacks"); + lua_rawget(L, LUA_GLOBALSINDEX); + if (lua_istable(L, -1)) { + lua_pushstring(L, "attrib_init"); + lua_rawget(L, LUA_GLOBALSINDEX); + if (lua_isfunction(L, -1)) { + lua_rawgeti(L, LUA_REGISTRYINDEX, a->data.i); + if (lua_pcall(L, 1, 0, 0)!=0) { + const char* error = lua_tostring(L, -1); + log_error(("attrib_init '%d': %s.\n", a->data.i, error)); + } + } + } } static void diff --git a/src/eressea/tolua/bindings.c b/src/eressea/tolua/bindings.c index 58180d875..c780b48f0 100644 --- a/src/eressea/tolua/bindings.c +++ b/src/eressea/tolua/bindings.c @@ -993,6 +993,68 @@ int tolua_process_produce(lua_State* L) { return 0; } +typedef struct event_args { + int hfunction; + int hargs; + const char * sendertype; +} event_args; + +static void args_free(void * udata) +{ + free(udata); +} + +static void event_cb(void * sender, const char * event, void * udata) { + event_args * args = (event_args *)udata; + int nargs = 2; + lua_rawgeti(L, LUA_REGISTRYINDEX, args->hfunction); + if (sender && args->sendertype) { + tolua_pushusertype(L, sender, args->sendertype); + } else { + lua_pushnil(L); + } + tolua_pushstring(L, event); + if (args->hargs) { + lua_rawgeti(L, LUA_REGISTRYINDEX, args->hfunction); + ++nargs; + } + lua_pcall(L, nargs, 0, 0); +} + +static int +tolua_eventbus_register(lua_State * L) +{ + /* parameters: + ** 1: sender (usertype) + ** 2: event (string) + ** 3: handler (function) + ** 4: arguments (any, *optional*) + */ + void * sender = tolua_tousertype(L, 1, 0); + const char * event = tolua_tostring(L, 2, 0); + event_args * args = malloc(sizeof(event_args)); + + args->sendertype = sender?tolua_typename(L, 1):NULL; + lua_pushvalue(L, 3); + args->hfunction = luaL_ref(L, LUA_REGISTRYINDEX); + if (lua_type(L, 4)!=LUA_TNONE) { + lua_pushvalue(L, 4); + args->hargs = luaL_ref(L, LUA_REGISTRYINDEX); + } else { + args->hargs = 0; + } + eventbus_register(sender, event, &event_cb, &args_free, args); + return 0; +} + +static int +tolua_eventbus_fire(lua_State * L) +{ + void * sender = tolua_tousertype(L, 1, 0); + const char * event = tolua_tostring(L, 2, 0); + eventbus_fire(sender, event, args); +} + static void parse_inifile(lua_State* L, dictionary * d, const char * section) { @@ -1061,6 +1123,14 @@ tolua_eressea_open(lua_State* L) } tolua_endmodule(L); + tolua_module(L, TOLUA_CAST "eventbus", 1); + tolua_beginmodule(L, TOLUA_CAST "eventbus"); + { + tolua_function(L, TOLUA_CAST "register", &tolua_eventbus_register); + tolua_function(L, TOLUA_CAST "fire", &tolua_eventbus_fire); + } + tolua_endmodule(L); + tolua_module(L, TOLUA_CAST "config", 1); tolua_beginmodule(L, TOLUA_CAST "config"); { diff --git a/src/res/messages.xml b/src/res/messages.xml index 8951328fc..c4c6b839d 100644 --- a/src/res/messages.xml +++ b/src/res/messages.xml @@ -1,5 +1,12 @@ + + + + + Ein Alp starb in $region($region), ohne sein Ziel zu erreichen. + An alp died in $region($region) before reaching its target. + diff --git a/src/scripts/callbacks.lua b/src/scripts/callbacks.lua new file mode 100644 index 000000000..33d34e4b5 --- /dev/null +++ b/src/scripts/callbacks.lua @@ -0,0 +1,11 @@ +callbacks = {} + +callbacks["attrib_init"] = function(attr) + if attr.name ~= nil then + local init = callbacks["init_" .. attr.name] + if init ~=nil then + init(attr) + end + end +end + diff --git a/src/scripts/default.lua b/src/scripts/default.lua index 94dd9a222..52e75fc5e 100644 --- a/src/scripts/default.lua +++ b/src/scripts/default.lua @@ -1,3 +1,5 @@ +callbacks = {} + function change_locales(localechange) for loc, flist in pairs(localechange) do for index, name in pairs(flist) do diff --git a/src/scripts/eressea/alp.lua b/src/scripts/eressea/alp.lua index 7c28869ac..1f33c8681 100644 --- a/src/scripts/eressea/alp.lua +++ b/src/scripts/eressea/alp.lua @@ -1,21 +1,37 @@ -function summon_alp(r, mage, level, force, params) - local alp = unit.create(mage.faction, r, 1, "alp") - local target = params[1] - alp:set_skill("stealth", 7) - alp.status = 5 -- FLEE - a = attrib.create(alp, { type='alp', target=target }) +require "callbacks" +require "dumptable" - - { - /* Wenn der Alp stirbt, den Magier nachrichtigen */ - add_trigger(&alp->attribs, "destroy", trigger_unitmessage(mage, - "trigger_alp_destroy", MSG_EVENT, ML_INFO)); - /* Wenn Opfer oder Magier nicht mehr existieren, dann stirbt der Alp */ - add_trigger(&mage->attribs, "destroy", trigger_killunit(alp)); - add_trigger(&opfer->attribs, "destroy", trigger_killunit(alp)); - } - msg = msg_message("summon_alp_effect", "mage alp target", mage, alp, opfer); - r_addmessage(r, mage->faction, msg); - msg_release(msg); - +local function trigger_alp_destroyed(alp, event) + m = message.create("alp_destroyed") + m:set_region("region", alp.region) + m:send_faction(alp.faction) +end + +local function trigger_alp_dissolve(u, event, attr) + local alp = attr.alp + attr.alp.number = 0 -- kills the alp +end + +local function init_alp(attr) + -- dumptable(attr) + eventbus.register(attr.alp, "destroy", trigger_alp_destroyed) + eventbus.register(attr.mage, "destroy", trigger_alp_dissolve, attr) + eventbus.register(attr.target, "destroy", trigger_alp_dissolve, attr) +end + +callbacks["init_alp"] = init_alp + +-- Spell: summon alp +function summon_alp(r, mage, level, force, params) + local alp = unit.create(mage.faction, r, 1, "alp") + local target = params[1] + alp:set_skill("stealth", 7) + alp.status = 5 -- FLEE + attr = attrib.create(alp, { ['name'] = 'alp', ['target'] = target, ['alp'] = alp, ['mage'] = mage }) + init_alp(attr) + msg = message.create("summon_alp_effect") + m:set_unit("mage", mage) + m:set_unit("alp", alp) + m:set_unit("target", target) + m:send_faction(mage.faction) end diff --git a/src/scripts/tests/common.lua b/src/scripts/tests/common.lua index cc91c45f3..a18426bfa 100644 --- a/src/scripts/tests/common.lua +++ b/src/scripts/tests/common.lua @@ -25,8 +25,19 @@ end module( "common", package.seeall, lunit.testcase ) +function test_eventbus_fire() + local r = region.create(0, 0, "plain") + local f = faction.create("noreply@eressea.de", "human", "de") + local u = unit.create(f, r) + + function compare_f(u, event, f) + assert_equal(u.faction, f) + end + eventbus.register(u, "weird", compare_f) + eventbus.fire(u, "weird", f) +end + function test_fleeing_units_can_be_transported() - free_game() local r = region.create(0, 0, "plain") local r1 = region.create(1, 0, "plain") local f1, f2 = two_factions() @@ -65,7 +76,6 @@ function test_plane() end function test_rename() - free_game() local r = region.create(0, 0, "plain") local f = faction.create("noreply@eressea.de", "human", "de") local u = unit.create(f, r)