/*
* This file is part of Deliantra, the Roguelike Realtime MMORPG.
*
* Copyright (©) 2006,2007,2008,2009,2010 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
*
* Deliantra is free software: you can redistribute it and/or modify it under
* the terms of the Affero GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the Affero GNU General Public License
* and the GNU General Public License along with this program. If not, see
* .
*
* The authors can be reached via e-mail to
*/
#include "autoconf.h"
#if HAVE_EXECINFO_H
# include
#endif
#include
#include
#include "global.h"
#include "../random_maps/random_map.h"
#include "evthread.h"
#include "sproto.h"
#include
#if _POSIX_MEMLOCK
# include
#endif
#if HAVE_MALLOC_H
# include
#endif
#if !__GLIBC__
# define malloc_trim(pad) -1
#endif
#include
#include
#include
#include "CoroAPI.h"
#include "perlxsi.c"
typedef object_thawer &object_thawer_ref;
typedef object_freezer &object_freezer_ref;
typedef std::string std__string;
static PerlInterpreter *perl;
tstamp NOW, runtime;
static int tick_inhibit;
static int tick_pending;
global gbl_ev;
static AV *cb_global, *cb_attachable, *cb_object, *cb_player, *cb_client, *cb_type, *cb_map;
static SV *sv_runtime, *sv_tick_start, *sv_next_tick, *sv_now;
static AV *av_reflect;
bitset ev_want_event;
bitset ev_want_type;
static HV
*stash_cf,
*stash_cf_object_wrap,
*stash_cf_object_player_wrap,
*stash_cf_player_wrap,
*stash_cf_map_wrap,
*stash_cf_mapspace_wrap,
*stash_cf_client_wrap,
*stash_cf_arch_wrap,
*stash_cf_party_wrap,
*stash_cf_region_wrap,
*stash_cf_living_wrap;
static SV
*cv_cf_do_invoke,
*cv_cf__can_merge,
*cv_cf_client_send_msg,
*cv_cf_tick,
*cv_cf_match_match;
#ifndef newSVpv_utf8
static SV *
newSVpv_utf8 (const char *s)
{
if (!s)
return newSV (0);
SV *sv = newSVpv (s, 0);
SvUTF8_on (sv);
return sv;
}
#endif
#ifndef newSVpvn_utf8
static SV *
newSVpvn_utf8 (const char *s, STRLEN l, int utf8)
{
if (!s)
return newSV (0);
SV *sv = newSVpvn (s, l);
if (utf8)
SvUTF8_on (sv);
return sv;
}
#endif
static noinline utf8_string
cfSvPVutf8_nolen (SV *sv)
{
SvGETMAGIC (sv);
if (SvPOK (sv))
{
if (!SvUTF8 (sv))
sv_utf8_upgrade_nomg (sv);
return SvPVX (sv);
}
return SvPV_nolen (sv);
}
// helper cast function, returns super class * or 0
template
static super *
is_a (attachable *at)
{
//return dynamic_cast(at); // slower, safer
if (typeid (*at) == typeid (super))
return static_cast(at);
else
return 0;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
unordered_vector attachable::mortals;
attachable::~attachable ()
{
assert (!self);
assert (!cb);
}
int
attachable::refcnt_cnt () const
{
return refcnt + (self ? SvREFCNT (self) - 1 : 0);
}
void
attachable::sever_self ()
{
if (HV *self = this->self)
{
// keep a refcount because sv_unmagic might call attachable_free,
// which might clear self, causing sv_unmagic to crash on a now
// invalid object.
SvREFCNT_inc (self);
hv_clear (self);
sv_unmagic ((SV *)self, PERL_MAGIC_ext);
SvREFCNT_dec (self);
// self *must* be null now because that's sv_unmagic's job.
assert (!this->self);
}
}
void
attachable::optimise ()
{
if (self
&& SvREFCNT (self) == 1
&& !HvTOTALKEYS (self))
sever_self ();
}
// check wether the object really is dead
void
attachable::do_check ()
{
if (refcnt_cnt () > 0)
return;
destroy ();
}
void
attachable::do_destroy ()
{
INVOKE_ATTACHABLE (DESTROY, this);
if (cb)
{
SvREFCNT_dec (cb);
cb = 0;
}
mortals.push_back (this);
}
void
attachable::destroy ()
{
if (destroyed ())
return;
attachable_flags |= F_DESTROYED;
do_destroy ();
sever_self ();
}
void
attachable::do_delete ()
{
delete this;
}
void
attachable::check_mortals ()
{
static int i = 0;
for (;;)
{
if (i >= mortals.size ())
{
i = 0;
if (mortals.size () >= 512)
{
static int last_mortalcount;
if (mortals.size () != last_mortalcount)
{
last_mortalcount = mortals.size ();
LOG (llevInfo, "%d mortals.\n", (int)mortals.size ());
if (0)
{
for (int j = 0; j < mortals.size (); ++j)//D
fprintf (stderr, "%d:%s %p ", j, &((object *)mortals[j])->name, mortals[j]);//D
fprintf (stderr, "\n");//D
}
}
}
break;
}
attachable *obj = mortals [i];
#if 0
if (obj->self)//D make this an assert later
{
LOG (llevError, "check_mortals: object '%s' still has self\n", typeid (obj).name ());
obj->sever_self ();
}
#endif
if (obj->refcnt)
{
++i; // further delay freeing
if (!(i & 0x3ff))
break;
}
else
{
mortals.erase (i);
obj->sever_self ();
obj->do_delete ();
}
}
}
void
attachable::set_key (const char *key, const char *value, bool is_utf8)
{
if (!self)
self = newHV ();
if (value)
hv_store (self, key, strlen (key), is_utf8 ? newSVpv_utf8 (value) : newSVpv (value, 0), 0);
else
hv_delete (self, key, strlen (key), G_DISCARD);
}
attachable &
attachable::operator =(const attachable &src)
{
//if (self || cb)
//INVOKE_OBJECT (CLONE, this, ARG_OBJECT (dst));
attach = src.attach;
return *this;
}
#if 0
template
static bool
find_backref (void *ptr, T *obj)
{
char *s = (char *)obj;
while (s < (char *)obj + sizeof (T))
{
if (ptr == *(void **)s)
return true;
s += sizeof (void *); // assume natural alignment
}
return false;
}
// for debugging, find "live" objects containing this ptr
static void
find_backref (void *ptr)
{
for_all_objects (op)
if (find_backref (ptr, op))
fprintf (stderr, "O %p %d:'%s'\n", op, op->count, &op->name);
for_all_players (pl)
if (find_backref (ptr, pl))
fprintf (stderr, "P %p\n", pl);
for_all_clients (ns)
if (find_backref (ptr, ns))
fprintf (stderr, "C %p\n", ns);
}
#endif
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static SV *
newSVptr (void *ptr, HV *stash, HV *hv = newHV ())
{
SV *sv;
if (!ptr)
return newSV (0);
sv_magicext ((SV *)hv, 0, PERL_MAGIC_ext, 0, (char *)ptr, 0);
return sv_bless (newRV_noinc ((SV *)hv), stash);
}
static int
attachable_free (pTHX_ SV *sv, MAGIC *mg)
{
attachable *at = (attachable *)mg->mg_ptr;
//TODO: check if transaction behaviour is really required here
if (SV *self = (SV *)at->self)
{
at->self = 0;
SvREFCNT_dec (self);
}
// next line makes sense, but most objects still have refcnt 0 by default
//at->refcnt_chk ();
return 0;
}
MGVTBL attachable::vtbl = {0, 0, 0, 0, attachable_free};
static SV *
newSVattachable (attachable *obj, HV *stash)
{
if (!obj)
return newSV (0);
if (!obj->self)
obj->self = newHV ();
if (!SvOBJECT (obj->self))
{
sv_magicext ((SV *)obj->self, 0, PERL_MAGIC_ext, &attachable::vtbl, (char *)obj, 0);
// now bless the object _once_
//TODO: create a class registry with c++ type<=>perl name<=>stash and use it here and elsewhere
return sv_bless (newRV_inc ((SV *)obj->self), stash);
}
else
{
SV *sv = newRV_inc ((SV *)obj->self);
if (Gv_AMG (stash)) // handle overload correctly, as the perl core does not
SvAMAGIC_on (sv);
return sv;
}
}
#if 0 // unused
static void
clearSVptr (SV *sv)
{
if (SvROK (sv))
sv = SvRV (sv);
hv_clear ((HV *)sv);
sv_unmagic (sv, PERL_MAGIC_ext);
}
#endif
static long
SvPTR_nc (SV *sv)
{
sv = SvRV (sv);
// very important shortcut
if (expect_true (SvMAGIC (sv) && SvMAGIC (sv)->mg_type == PERL_MAGIC_ext))
return (long)SvMAGIC (sv)->mg_ptr;
if (MAGIC *mg = mg_find (sv, PERL_MAGIC_ext))
return (long)mg->mg_ptr;
croak ("perl code used object, but C object is already destroyed, caught");
}
static long
SvPTR (SV *sv, const char *klass)
{
if (!sv_derived_from (sv, klass))
croak ("object of type %s expected", klass);
return SvPTR_nc (sv);
}
static long noinline
SvPTR_ornull (SV *sv, const char *klass)
{
if (expect_false (!SvOK (sv))) return 0;
return SvPTR (sv, klass);
}
static long noinline
SvPTR_ornull_client (SV *sv)
{
if (expect_false (!SvOK (sv))) return 0;
if (!SvROK (sv)
|| (SvSTASH (SvRV (sv)) != stash_cf_client_wrap
&& !sv_derived_from (sv, "cf::client")))
croak ("object of type cf::client expected");
return SvPTR_nc (sv);
}
static long noinline
SvPTR_ornull_object (SV *sv)
{
if (expect_false (!SvOK (sv))) return 0;
if (!SvROK (sv)
|| (SvSTASH (SvRV (sv)) != stash_cf_object_wrap
&& SvSTASH (SvRV (sv)) != stash_cf_object_player_wrap
&& SvSTASH (SvRV (sv)) != stash_cf_arch_wrap
&& !sv_derived_from (sv, "cf::object")))
croak ("object of type cf::object expected");
return SvPTR_nc (sv);
}
static long noinline
SvPTR_ornull_player (SV *sv)
{
if (expect_false (!SvOK (sv))) return 0;
if (!SvROK (sv)
|| (SvSTASH (SvRV (sv)) != stash_cf_player_wrap
&& !sv_derived_from (sv, "cf::player")))
croak ("object of type cf::player expected");
return SvPTR_nc (sv);
}
static inline SV *to_sv (const shstr & v) { return newSVpvn_utf8 ((const char *)v, v.length (), 1); }
static inline SV *to_sv (const char * v) { return v ? newSVpv (v, 0) : newSV (0); }
static inline SV *to_sv (bool v) { return newSViv (v); }
static inline SV *to_sv ( signed char v) { return newSViv (v); }
static inline SV *to_sv (unsigned char v) { return newSViv (v); }
static inline SV *to_sv ( signed short v) { return newSViv (v); }
static inline SV *to_sv (unsigned short v) { return newSVuv (v); }
static inline SV *to_sv ( signed int v) { return newSViv (v); }
static inline SV *to_sv (unsigned int v) { return newSVuv (v); }
static inline SV *to_sv ( signed long v) { return newSViv (v); }
static inline SV *to_sv (unsigned long v) { return newSVuv (v); }
static inline SV *to_sv ( signed long long v) { return newSVval64 (v); }
static inline SV *to_sv (unsigned long long v) { return newSVval64 (v); }
static inline SV *to_sv (float v) { return newSVnv (v); }
static inline SV *to_sv (double v) { return newSVnv (v); }
static inline SV *to_sv (client * v) { return newSVattachable (v, stash_cf_client_wrap); }
static inline SV *to_sv (player * v) { return newSVattachable (v, stash_cf_player_wrap); }
static inline SV *to_sv (object * v) { return newSVattachable (v, v && v->type == PLAYER ? stash_cf_object_player_wrap : stash_cf_object_wrap); }
static inline SV *to_sv (maptile * v) { return newSVattachable (v, stash_cf_map_wrap); }
static inline SV *to_sv (archetype * v) { return newSVattachable (v, stash_cf_arch_wrap); }
static inline SV *to_sv (region * v) { return newSVattachable (v, stash_cf_region_wrap); }
static inline SV *to_sv (partylist * v) { return newSVptr (v, stash_cf_party_wrap); }
static inline SV *to_sv (living * v) { return newSVptr (v, stash_cf_living_wrap); }
static inline SV *to_sv (mapspace * v) { return newSVptr (v, stash_cf_mapspace_wrap); }
static inline SV *to_sv (object & v) { return to_sv (&v); }
static inline SV *to_sv (living & v) { return to_sv (&v); }
static inline SV *to_sv (const std::string & v) { return newSVpvn (v.data (), v.size ()); }
static inline SV *to_sv (const treasurelist *v) { return to_sv (v->name); }
static inline SV *to_sv (UUID v) { return newSVpv (v.c_str (), 0); }
static inline SV *to_sv (dynbuf * v)
{
SV *sv = newSV (0);
sv_upgrade (sv, SVt_PV);
SvGROW (sv, v->size () + 1);
SvPOK_only (sv);
v->linearise (SvPVX (sv));
SvCUR_set (sv, v->size ());
*SvEND (sv) = 0;
return sv;
}
static inline SV *to_sv (dynbuf_text * v)
{
SV *sv = to_sv (static_cast (v));
SvUTF8_on (sv);
return sv;
}
static inline void sv_to (SV *sv, shstr &v) { v = SvOK (sv) ? cfSvPVutf8_nolen (sv) : 0; }
static inline void sv_to (SV *sv, char * &v) { free (v); v = SvOK (sv) ? strdup (SvPV_nolen (sv)) : 0; }
static inline void sv_to (SV *sv, bool &v) { v = SvIV (sv); }
static inline void sv_to (SV *sv, signed char &v) { v = SvIV (sv); }
static inline void sv_to (SV *sv, unsigned char &v) { v = SvIV (sv); }
static inline void sv_to (SV *sv, signed short &v) { v = SvIV (sv); }
static inline void sv_to (SV *sv, unsigned short &v) { v = SvIV (sv); }
static inline void sv_to (SV *sv, signed int &v) { v = SvIV (sv); }
static inline void sv_to (SV *sv, unsigned int &v) { v = SvUV (sv); }
static inline void sv_to (SV *sv, signed long &v) { v = SvIV (sv); }
static inline void sv_to (SV *sv, unsigned long &v) { v = SvUV (sv); }
static inline void sv_to (SV *sv, signed long long &v) { v = ( signed long long)SvVAL64 (sv); }
static inline void sv_to (SV *sv, unsigned long long &v) { v = (unsigned long long)SvVAL64 (sv); }
static inline void sv_to (SV *sv, float &v) { v = SvNV (sv); }
static inline void sv_to (SV *sv, double &v) { v = SvNV (sv); }
static inline void sv_to (SV *sv, client * &v) { v = (client *) (attachable *)SvPTR_ornull_client (sv); }
static inline void sv_to (SV *sv, player * &v) { v = (player *) (attachable *)SvPTR_ornull_player (sv); }
static inline void sv_to (SV *sv, object * &v) { v = (object *) (attachable *)SvPTR_ornull_object (sv); }
static inline void sv_to (SV *sv, archetype * &v) { v = (archetype *)(attachable *)SvPTR_ornull (sv, "cf::arch"); }
static inline void sv_to (SV *sv, maptile * &v) { v = (maptile *) (attachable *)SvPTR_ornull (sv, "cf::map"); }
static inline void sv_to (SV *sv, region * &v) { v = (region *) (attachable *)SvPTR_ornull (sv, "cf::region"); }
static inline void sv_to (SV *sv, attachable * &v) { v = (attachable *)SvPTR_ornull (sv, "cf::attachable"); }
static inline void sv_to (SV *sv, partylist * &v) { v = (partylist *) SvPTR_ornull (sv, "cf::party"); }
static inline void sv_to (SV *sv, living * &v) { v = (living *) SvPTR_ornull (sv, "cf::living"); }
static inline void sv_to (SV *sv, mapspace * &v) { v = (mapspace *) SvPTR_ornull (sv, "cf::mapspace"); }
static inline void sv_to (SV *sv, object_freezer * &v) { v = (object_freezer *) SvPTR_ornull (sv, "cf::object::freezer"); }
static inline void sv_to (SV *sv, object_thawer * &v) { v = (object_thawer *) SvPTR_ornull (sv, "cf::object::thawer" ); }
//static inline void sv_to (SV *sv, faceinfo * &v) { v = &faces [face_find (SvPV_nolen (sv), 0)]; }
static inline void sv_to (SV *sv, treasurelist * &v) { v = treasurelist::find (SvPV_nolen (sv)); }
template
static inline void sv_to (SV *sv, refptr &v) { T *tmp; sv_to (sv, tmp); v = tmp; }
template
static inline void sv_to (SV *sv, char (&v)[N]) { assign (v, SvPV_nolen (sv)); }
static inline void sv_to (SV *sv, bowtype_t &v) { v = (bowtype_t) SvIV (sv); }
static inline void sv_to (SV *sv, petmode_t &v) { v = (petmode_t) SvIV (sv); }
static inline void sv_to (SV *sv, usekeytype &v) { v = (usekeytype) SvIV (sv); }
static inline void sv_to (SV *sv, unapplymode &v) { v = (unapplymode) SvIV (sv); }
static inline void sv_to (SV *sv, std::string &v)
{
STRLEN len;
char *data = SvPVbyte (sv, len);
v.assign (data, len);
}
static inline void sv_to (SV *sv, UUID &v)
{
if (!v.parse (SvPV_nolen (sv)))
croak ("unparsable uuid: %s", SvPV_nolen (sv));
}
static inline void sv_to (SV *sv, object::flags_t::reference v) { v = SvTRUE (sv); }
static SV *
newSVdt_va (va_list &ap, data_type type)
{
SV *sv;
switch (type)
{
case DT_INT:
sv = newSViv (va_arg (ap, int));
break;
case DT_INT64:
sv = newSVval64 ((val64)va_arg (ap, sint64));
break;
case DT_DOUBLE:
sv = newSVnv (va_arg (ap, double));
break;
case DT_STRING:
{
char *str = (char *)va_arg (ap, const char *);
sv = str ? newSVpv (str, 0) : newSV (0);
}
break;
case DT_DATA:
{
char *str = (char *)va_arg (ap, const void *);
int len = va_arg (ap, int);
sv = str ? newSVpv (str, len) : newSV (0);
}
break;
case DT_OBJECT:
sv = to_sv (va_arg (ap, object *));
break;
case DT_MAP:
// va_arg (object *) when void * is passed is an XSI extension
sv = to_sv (va_arg (ap, maptile *));
break;
case DT_CLIENT:
sv = to_sv (va_arg (ap, client *));
break;
case DT_PLAYER:
sv = to_sv (va_arg (ap, player *));
break;
case DT_ARCH:
sv = to_sv (va_arg (ap, archetype *));
break;
case DT_PARTY:
sv = to_sv (va_arg (ap, partylist *));
break;
case DT_REGION:
sv = to_sv (va_arg (ap, region *));
break;
default:
assert (("unhandled type in newSVdt_va", 0));
}
return sv;
}
static SV *
newSVdt (data_type type, ...)
{
va_list ap;
va_start (ap, type);
SV *sv = newSVdt_va (ap, type);
va_end (ap);
return sv;
}
// typemap support, mostly to avoid excessive inlining
template
static void noinline
cf_obj_to (SV *arg, type &var)
{
sv_to (arg, var);
if (!var)
croak ("must not pass invalid/null cf_obj here");
}
template
static void noinline
cf_obj_ornull_to (SV *arg, object *&var)
{
if (SvOK (arg))
{
sv_to (arg, var);
if (!var)
croak ("unable to convert perl object to C++ object");
}
else
var = 0;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static SV *
registry (attachable *ext)
{
if (!ext->cb)
ext->cb = newAV ();
return newRV_inc ((SV *)ext->cb);
}
/////////////////////////////////////////////////////////////////////////////
void
cfperl_init ()
{
extern char **environ;
PERL_SYS_INIT3 (&settings.argc, &settings.argv, &environ);
perl = perl_alloc ();
perl_construct (perl);
PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
const char *argv[] = {
settings.argv [0],
"-e0"
};
if (perl_parse (perl, xs_init, 2, (char **)argv, environ)
|| perl_run (perl))
{
printf ("unable to initialize perl-interpreter, aborting.\n");
exit (EXIT_FAILURE);
}
eval_pv (
"#line 1 'cfperl init'\n"
"use EV ();\n"
"use Coro ();\n"
"cf->bootstrap;\n"
"unshift @INC, cf::datadir ();\n"
"require cf;\n",
0
);
if (SvTRUE (ERRSV))
{
printf ("unable to bootstrap perl, aborting:\n%s", SvPV_nolen (ERRSV));
exit (EXIT_FAILURE);
}
}
void
cfperl_main ()
{
dSP;
PUSHMARK (SP);
PUTBACK;
call_pv ("cf::main", G_DISCARD | G_VOID);
}
void
attachable::instantiate ()
{
if (attach)
{
INVOKE_ATTACHABLE (INSTANTIATE, this, ARG_STRING (attach));
attach = 0;
}
}
void
attachable::reattach ()
{
optimise ();
//TODO: check for _attachment's, very important for restarts
INVOKE_ATTACHABLE (REATTACH, this);
}
static event_klass klass_of[NUM_EVENT_TYPES] = {
# define def(type,name) KLASS_ ## type,
# include "eventinc.h"
# undef def
};
#define KLASS_OF(event) (((unsigned int)event) < NUM_EVENT_TYPES ? klass_of [event] : KLASS_NONE)
static void noinline
gather_callbacks (AV *&callbacks, AV *registry, event_type event)
{
// event must be in array
if (event >= 0 && event <= AvFILLp (registry))
{
SV *cbs_ = AvARRAY (registry)[event];
// element must be list of callback entries
if (cbs_ && SvROK (cbs_) && SvTYPE (SvRV (cbs_)) == SVt_PVAV)
{
AV *cbs = (AV *)SvRV (cbs_);
// no callback entries, no callbacks to call
if (AvFILLp (cbs) >= 0)
{
if (!callbacks)
{
callbacks = newAV ();
av_extend (callbacks, 16);
}
// never use SvREFCNT_inc to copy values, but its ok here :)
for (int i = 0; i <= AvFILLp (cbs); ++i)
av_push (callbacks, SvREFCNT_inc (AvARRAY (cbs)[i]));
}
}
}
}
void
attachable::gather_callbacks (AV *&callbacks, event_type event) const
{
::gather_callbacks (callbacks, cb_attachable, event);
if (cb)
::gather_callbacks (callbacks, cb, event);
}
void
global::gather_callbacks (AV *&callbacks, event_type event) const
{
::gather_callbacks (callbacks, cb_global, event);
}
void
object::gather_callbacks (AV *&callbacks, event_type event) const
{
if (subtype && type + subtype * NUM_TYPES <= AvFILLp (cb_type))
{
SV *registry = AvARRAY (cb_type)[type + subtype * NUM_TYPES];
if (registry && SvROK (registry) && SvTYPE (SvRV (registry)) == SVt_PVAV)
::gather_callbacks (callbacks, (AV *)SvRV (registry), event);
}
if (type <= AvFILLp (cb_type))
{
SV *registry = AvARRAY (cb_type)[type];
if (registry && SvROK (registry) && SvTYPE (SvRV (registry)) == SVt_PVAV)
::gather_callbacks (callbacks, (AV *)SvRV (registry), event);
}
attachable::gather_callbacks (callbacks, event);
::gather_callbacks (callbacks, cb_object, event);
}
void
archetype::gather_callbacks (AV *&callbacks, event_type event) const
{
attachable::gather_callbacks (callbacks, event);
//TODO//::gather_callbacks (callbacks, cb_archetype, event);
}
void
client::gather_callbacks (AV *&callbacks, event_type event) const
{
attachable::gather_callbacks (callbacks, event);
::gather_callbacks (callbacks, cb_client, event);
}
void
player::gather_callbacks (AV *&callbacks, event_type event) const
{
attachable::gather_callbacks (callbacks, event);
::gather_callbacks (callbacks, cb_player, event);
}
void
maptile::gather_callbacks (AV *&callbacks, event_type event) const
{
attachable::gather_callbacks (callbacks, event);
::gather_callbacks (callbacks, cb_map, event);
}
static void noinline
_recalc_want (bitset &set, AV *registry)
{
for (int event = 0; event <= AvFILLp (registry); ++event)
{
SV *cbs_ = AvARRAY (registry)[event];
// element must be list of callback entries
if (cbs_ && SvROK (cbs_) && SvTYPE (SvRV (cbs_)) == SVt_PVAV)
{
AV *cbs = (AV *)SvRV (cbs_);
// no callback entries, no callbacks to call
if (AvFILLp (cbs) >= 0)
set.set (event);
}
}
}
// very slow and inefficient way to recalculate the global want bitsets
static void
_recalc_want ()
{
ev_want_event.reset ();
_recalc_want (ev_want_event, cb_global);
_recalc_want (ev_want_event, cb_attachable);
_recalc_want (ev_want_event, cb_object);
_recalc_want (ev_want_event, cb_client);
_recalc_want (ev_want_event, cb_player);
_recalc_want (ev_want_event, cb_map);
ev_want_type.reset ();
for (int type = 0; type <= AvFILLp (cb_type); ++type)
{
SV *cbs_ = AvARRAY (cb_type)[type];
// element must be list of callback entries
if (cbs_ && SvROK (cbs_) && SvTYPE (SvRV (cbs_)) == SVt_PVAV)
{
AV *cbs = (AV *)SvRV (cbs_);
// no callback entries, no callbacks to call
if (AvFILLp (cbs) >= 0)
ev_want_type.set (type % NUM_TYPES);
}
}
}
bool
attachable::invoke (event_type event, ...)
{
data_type dt;
// callback call ordering should be:
// 1. per-object callback
// 2. per-class object
// 3. per-type callback
// 4. global callbacks
AV *callbacks = 0;
gather_callbacks (callbacks, event);
// short-circuit processing if no callbacks found/defined
if (!callbacks)
return 0;
va_list ap;
va_start (ap, event);
CALL_BEGIN (3);
CALL_ARG_SV (newSViv (event)); // only used for debugging nowadays
CALL_ARG_SV (newRV_noinc ((SV *)callbacks));
//TODO: unhack
if (object *op = is_a