eventbus WIP

This commit is contained in:
Enno Rehling 2010-03-05 09:22:33 +01:00
parent efeeba13c2
commit cc7172f812
13 changed files with 348 additions and 21 deletions

View File

@ -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"

View File

@ -224,6 +224,14 @@
<Filter
Name="util"
>
<File
RelativePath=".\util\argstack.c"
>
</File>
<File
RelativePath=".\util\argstack.h"
>
</File>
<File
RelativePath=".\util\attrib.c"
>
@ -276,6 +284,14 @@
RelativePath=".\util\event.h"
>
</File>
<File
RelativePath=".\util\eventbus.c"
>
</File>
<File
RelativePath=".\util\eventbus.h"
>
</File>
<File
RelativePath=".\util\filereader.c"
>

View File

@ -0,0 +1,66 @@
#include "argstack.h"
#include <assert.h>
#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->n<ARGSTKSIZE);
arg->stack[arg->n].type = type;
arg->stack[arg->n].value.data = data;
++arg->n;
}
void arg_pushstring(struct arguments * arg, const char * str) {
assert(arg->n<ARGSTKSIZE);
arg->stack[arg->n].type = ARG_TSTRING;
arg->stack[arg->n].value.str = str;
++arg->n;
}
void arg_pushnumber(struct arguments * arg, double number) {
assert(arg->n<ARGSTKSIZE);
arg->stack[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;
}

View File

@ -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

View File

@ -0,0 +1,56 @@
#include <config.h>
#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;
}

View File

@ -0,0 +1,29 @@
/* vi: set ts=2:
+-------------------+
| | Enno Rehling <enno@eressea.de>
| Eressea PBEM host | Katja Zedel <katze@felidae.kn-bremen.de>
| (c) 1998 - 2010 | Christian Schlittchen <corwin@amber.kn-bremen.de>
| |
+-------------------+
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

View File

@ -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

View File

@ -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");
{

View File

@ -1,5 +1,12 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<messages>
<message name="alp_destroyed" section="events">
<type>
<arg name="region" type="region"/>
</type>
<text locale="de">Ein Alp starb in $region($region), ohne sein Ziel zu erreichen.</text>
<text locale="en">An alp died in $region($region) before reaching its target.</text>
</message>
<message name="nr_claims" section="nr">
<type>
<arg name="items" type="items"/>

11
src/scripts/callbacks.lua Normal file
View File

@ -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

View File

@ -1,3 +1,5 @@
callbacks = {}
function change_locales(localechange)
for loc, flist in pairs(localechange) do
for index, name in pairs(flist) do

View File

@ -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

View File

@ -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)