/*
* This file is part of Deliantra, the Roguelike Realtime MMORPG.
*
* Copyright (©) 2018 Marc Alexander Lehmann / the Deliantra team
* Copyright (©) 2006,2007,2008,2009,2010,2012,2015 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"
#define CFPERL 1
#if HAVE_EXECINFO_H
# include
#endif
#include
#include
#include "global.h"
#include "rmg.h"
#include "noise.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, *sv_server_tick;
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,
*stash_cf_layout_wrap,
*stash_ext_map_world;
static SV
*cv_cf_do_invoke,
*cv_cf__can_merge,
*cv_cf_client_send_msg,
*cv_cf_ix,
*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
ecb_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 ();
object *specimen = is_a