1
0
Fork 0
mirror of git://git.psyced.org/git/psyced synced 2024-08-15 03:25:10 +00:00

let the past begone in cvs land. welcome to igit igit!

This commit is contained in:
PSYC 2009-01-26 20:21:29 +01:00
commit 4e601cf1c7
509 changed files with 77963 additions and 0 deletions

373
world/net/group/master.c Normal file
View file

@ -0,0 +1,373 @@
// vim:foldmethod=marker:syntax=lpc:noexpandtab
// $Id: master.c,v 1.142 2008/02/24 16:36:59 lynx Exp $
//
// this is a simpler version of group/master, and it is actually in use
// [actual as in effectively, not current, which is the german meaning]
//
// part of the implementation of PSYC multicasting.. send stuff back where
// the _enter came from, send just one copy no matter how many have entered
// from there. the group/slave will take care of the other side.
//
// group/master and group/slave are used for smaller size rooms, where each
// person present should be visible. if you need a large scale multicast
// solution, build up a link network using place/master and place/slave.
//
// see http://about.psyc.eu/Routing for the big picture on routing.
//
// should we rename the directories into net/context/master and slave, since a
// context sounds more abstract than a group? i wouldn't say that a user's
// context delivering presence subscriptions is a "group", but it certainly
// is a "context."
#include <net.h>
#include <person.h>
#include <status.h>
#include <url.h>
#ifdef CONTEXT_STATE // {{{
# define HEADER_ONLY
# include "../state.c"
# undef HEADER_ONLY
#endif // }}}
inherit NET_PATH "entity";
#ifdef HISTORY_COUNT
int _ccount; // persistent!
#endif
// these data structures are allocated for every single place and user
// as route is used by all of them to do presence or general smarticasting.
#ifdef PERSISTENT_MASTERS
mapping _routes;
#else
volatile mapping _routes, _u;
#endif
#ifdef CONTEXT_STATE // {{{
volatile mapping _costate, _cmemory;
volatile mapping ctemp, cunused;
#endif // }}}
#ifdef PERSISTENT_SLAVES
int revision; // persistent revision counter
#endif
stoned();
// used by /routes and /reload
routes(recover) {
if (recover) {
PT(("%O recovering routes %O\n", ME, recover))
ASSERT("routes() recovering", mappingp(recover), recover)
_routes = recover;
}
return _routes;
}
create() {
_routes = ([ ]);
#ifdef HISTORY_COUNT
_ccount = 0;
#endif
#ifdef CONTEXT_STATE // {{{
_costate = ([ ]);
ctemp = ([ ]);
_cmemory = m_allocate(0, 2);
#endif // }}}
::create();
}
// der castmsg muss irgendwie anders.. sonst kriegen wir den state nicht hin.
castmsg(source, mc, data, vars) {
mixed route;
mixed noa; // Nickname of local user Or Amount of people on route
// in TIDILY mode should be Mapping of people on route
#ifdef FORK // {{{
string buf;
// as ldmud isn't a threading enviroment there shouldn't be a chance of
// psycd getting destructed while we're residing in this function.
// so we're ensuring its existence only once.
#endif // }}}
P2(("%O castmsg(%O,%O,%O..) for %O\n", ME, source,mc,data, _routes))
D3(P2(("%O vars = %O\n", ME, vars)))
// _context is an MMP variable, so we use it internally with objectp
vars["_context"] = ME;
#ifdef PERSISTENT_SLAVES
vars["_number_revision"] = revision;
#endif
// in theory.. m_delete(vars, "_source") but not necessary
m_delete(vars, "_target");
#ifdef HISTORY_COUNT
vars["_count"] = _ccount++; // wraps at MAXINT.. right? hehe
#endif
foreach (route, noa : _routes) {
#if defined(TIDILY) && defined(MEMBERS_BY_SOURCE)
P3(("place:each(%O,t=%O,s=%O,mc=%O,d=%O,v=%O)\n",
mappingp(noa) ? sizeof(noa) : noa, route, source, mc,
data, vars))
#else
P3(("place:each(%O,t=%O,s=%O,mc=%O,d=%O,v=%O)\n",
noa, route, source, mc, data, vars))
#endif
// if (route == source) return; // skip sender
if (!route) {
if (stringp(noa)) {
// object has been destructed
// maybe there's a new one
if (route = find_person(noa)) {
#ifndef PERSISTENT_MASTERS
#ifdef MEMBERS_BY_NICK
_u[noa] = route;
#else
_u[route] = noa;
#endif
#else
P1(("%O lost route to %O\n", ME, noa))
#endif
} else {
// why source? that's silly!
call_out(#'stoned, 1, source, noa);
continue; // oops! why was this a return; !???
}
} else { // if (mappingp/intp(noa)) // noa contains (_amount_)members
monitor_report("_failure_destruct_circuit",
psyctext("[_nick_place] detected the loss "
"of a circuit with [_amount_members] "
"members.",
([ "_nick_place" : MYNICK,
#if defined(TIDILY) && defined(MEMBERS_BY_SOURCE)
"_amount_members" : sizeof(noa),
#else
"_amount_members" : noa,
#endif
])));
m_delete(_routes, route);
}
}
P4(("group/master sees vars as %O\n", vars))
// btw, fippo, could you please resume what happens if we
// try not to copy(vars) ? i like docs in the middle of source
// that give me a reason not to break it ;)
if (objectp(route)) {
route -> msg(source, mc, data, copy(vars));
} else {
// gateways to other schemes please make their own
// copy of the vars if they need to mess with them.
sendmsg(route, mc, data, vars, source);
}
}
return 1;
}
#if 0 //def PERSISTENT_MASTERS
protected load(file) {
::load(file);
DT(if (sizeof(_routes))
PP(("Found routes in %O: %O\n", ME, _routes || "nothing"));)
}
#endif
#if defined(TIDILY) && defined(MEMBERS_BY_SOURCE)
# define RM(SOURCE, ORIGIN) \
m_delete(_routes[ORIGIN], SOURCE); \
unless (sizeof(_routes[ORIGIN])) m_delete(_routes, ORIGIN);
#else
# define RM(SOURCE, ORIGIN) \
if (_routes[ORIGIN]) \
unless (--_routes[ORIGIN]) m_delete(_routes, ORIGIN);
#endif
remove_member(source, origin) {
mixed t;
P2(("%O remove_member(%O, %O)\n", ME, source, origin))
if (origin && (
#if 1 //defined(TIDILY) && defined(MEMBERS_BY_SOURCE)
stringp(t = origin) ||
#endif
t = origin->qOrigin())
#ifdef FORK
&& t += "/$cslave"
#endif
&& member(_routes, t)) {
RM(source, t);
} else if (member(_routes, source)) {
// local object or xmpp user
m_delete(_routes, source);
} else if (stringp(source)) {
// guessing route from hostname of psyc address
// try to avoid using remove_member() without origin
// but this becomes necessary when we want to
// remove an object or route manually
mixed u;
if (u = find_target_handler(source)) {
t = u->qOrigin();
RM(source, t);
P1((
"%O guessing origin %O for %O, successful? routes are %O\n",
ME, t, source, _routes))
} else {
t = 0;
if (u = parse_uniform(source)) t = u[URoot];
unless (t) t = source;
/*
<fippo> das hier geschah, als ich bei frischgestartetem server mit einem remote
jabberisten die freundschaft zum user#fippo entfernte:
EXCEPTION at line 185 of net/group/master.c in object net/psyc/user#fippo:
Bad arg 1 to m_delete(): got 'number', expected 'mapping'.
<lynX> die datenstruktur war nicht initialisiert? da muss doch vorher schon
an anderer stelle was schiefgelaufen sein...?
*/
RM(source, t);
P1((
"%O parsing origin %O from %O, successful? routes are %O\n",
ME, t, source, _routes))
}
} else {
// happens when doing /unfr. must be a bug in user.c
P0(("%O encountered unnecessary remove of %O from %O\n",
ME, source, _routes))
}
}
insert_member(source, origin) {
mixed t;
P2(("%O insert_member(%O, %O)\n", ME, source, origin))
if (objectp(origin) && (t = origin->qOrigin())) {
#ifdef FORK
t = t + "/$cslave";
#endif
#if defined(TIDILY) && defined(MEMBERS_BY_SOURCE)
unless (member(_routes, t)) _routes[t] = m_allocate(1, 0);
m_add(_routes[t], source);
#else
_routes[t]++;
#endif
} else if (stringp(origin)) {
#ifdef FORK
origin = origin + "/$cslave";
#endif
#if defined(TIDILY) && defined(MEMBERS_BY_SOURCE)
unless (member(_routes, origin)) _routes[origin] = m_allocate(1, 0);
m_add(_routes[origin], source);
#else
_routes[origin]++;
#endif
} else {
#ifdef PERSISTENT_MASTERS
_routes[source] = 1; // gimme the nick
#else
_routes[source] = 1; // castmsg expects the nick here. duh.
// should we do that for objectp(source)?
#endif
}
#ifdef PERSISTENT_SLAVES
// would be nice to do it here, but that's not correct
//revision += 1;
#endif
}
// code duplicaton is faster than others
#ifdef CONTEXT_STATE // {{{
//
// <lynX> code duplication is okay for enhanced efficiency, but please not
// by mouse pasting. use #include "shared_code.i" please. TODO.
// you can even ifdef small differences of the two versions!
//
int outstate(string target, string key, mixed value, int hascontext) {
int mod;
unless (hascontext)
return ::outstate(target, key, value, hascontext);
if (key[0] == '_') {
mod = ':';
} else {
mod = key[0];
key = key[1..];
}
unless (ctemp) ctemp = ([ ]);
unless (cunused) cunused = copy(_costate);
m_delete(cunused, key);
switch(mod) {
case ':':
if (_cmemory[key, 1] == -1) break;
if (member(_cmemory, key)) {
if (_cmemory[key] == value) {
if (_cmemory[key, 1] == STATE_MAX2) {
break;
}
if (_cmemory[key, 1] == STATE_MIN2) {
ctemp["="+key] = value;
_costate[key] = value;
return 0;
}
_cmemory[key, 1]++;
} else if (_cmemory[key, 1] - 1 == STATE_MIN2) {
ctemp["="+key] = "";
m_add(_cmemory, key, value, 1);
} else if (_cmemory[key, 1] <= STATE_MIN2) {
m_add(_cmemory, key, value, 1);
} else _cmemory[key, 1]--;
} else m_add(_cmemory, key, value, 1);
break;
case '=':
if ("" == value) {
m_delete(_costate, key);
break;
}
if (member(_costate, key) && _costate[key] == value) {
return 0;
}
m_add(_cmemory, key, value, -1);
_costate[key] = value;
break;
case '+':
_augment(_costate, key, value);
break;
case '-':
_diminish(_costate, key, value);
break;
default:
raise_error("Illegal variable modifier in '" + key +
"' encountered in group/master:outstate.\n");
return 0;
}
return 1;
}
mapping state(string target, int hascontext) {
mapping t2 = ([ ]), t;
unless (hascontext) return ::state(target, hascontext);
if (cunused) {
foreach (string key : cunused) {
t2[":" + key] = "";
}
cunused = 0;
}
t = ctemp;
ctemp = ([ ]);
// rock hard optimization
unless (sizeof(t))
return t2;
unless (sizeof(t2))
return t;
return t2 + t;
}
#endif // }}}

263
world/net/group/slave.c Normal file
View file

@ -0,0 +1,263 @@
// vim:foldmethod=marker:syntax=lpc:noexpandtab
// $Id: slave.c,v 1.58 2008/03/21 12:35:40 lynx Exp $
//
// generic context slave as described in a posting to psyc-dev years ago.
// it receives the single copy of a message sent out by the group master
// and fans it out to local recipients. that's why local recipients need
// to create and join this manager when they enter a room.
//
#include <net.h>
#include <presence.h>
#ifdef PERSISTENT_SLAVES
# define PARANOID_PERSISTENT_SLAVES
# include <url.h>
inherit NET_PATH "storage";
private volatile string _file;
private string *_save_members;
private string _name;
private int _revision = -1;
#endif
#ifdef CONTEXT_STATE
private volatile mapping cast_state;
private volatile mapping temp_state;
inherit NET_PATH "state";
#endif
private volatile mapping _members;
void create() {
unless(mappingp(_members)) _members = ([ ]);
#ifdef CONTEXT_STATE
unless(mappingp(cast_state)) cast_state = ([ ]);
unless(mappingp(temp_state)) temp_state = ([ ]);
#endif
}
#ifdef PERSISTENT_SLAVES
varargs void load(string context, array(mixed) u) {
string dir = DATA_PATH "slaves/";
string entity;
unless (u) u = parse_uniform(context);
dir += u[UHost];
if (u[UPort]) dir += "-"+ u[UPort];
mkdir(dir); // make sure that directory exists
_name = context;
entity = u[UUser] || u[UResource];
_file = dir +"/"+ (entity? sha1(entity): "_");
::load(_file);
foreach (string m : _save_members) {
object o = summon_person(m[1..]);
_members[o] = m;
}
P4(("loaded cslave %O with %O\n", _name, _members))
}
protected save() {
P4(("cslave:save() %O\n", _members))
unless (_file) return;
if (sizeof(_members) == 0) {
rm(_file);
_revision = -1;
// FIXME: could clean up directory if it is empty
} else {
_save_members = m_values(_members);
::save(_file);
}
}
# ifndef PARANOID_PERSISTENT_SLAVES
remove() {
if (_file) save(_file);
}
reset() {
if (_file) save(_file);
}
# endif
#endif
void insert_member(mixed member, mixed origin, mixed data) {
P4(("%O enters cslave\n", member))
// we use the values from the members mapping as counters
// for their individual psyc-state
#ifdef PERSISTENT_SLAVES
_members[member] = member->psycName();
#else
_members[member] = 0;
#endif
#ifdef CONTEXT_STATE
if (stringp(member)) {
mapping v = ([ ]);
// this is not how we do it
// foreach(string key, mixed value : cast_state)
// v["="+key] = value;
// no var ops embedded in sendmsg.
sendmsg(member, 0, 0, v + ([ "_context" : ME , "_target" : member ]));
}
#endif
#ifdef PARANOID_PERSISTENT_SLAVES
// paranoid slaves
save();
#endif
}
void remove_member(mixed member, mixed origin) {
PT(("%O leaves context slave\n", member))
m_delete(_members, member);
#ifdef PARANOID_PERSISTENT_SLAVES
save();
#endif
}
#ifdef FORK
msg(source, method, data, mapping vars) {
#else
castmsg(source, method, data, mapping vars) {
#endif
/*
* implement group manager logic
* and fan out everything else?
* fan out could happen here and group manager handled by
* higher level objects
*/
mixed o;
#ifdef XMPPERIMENTAL
PT(("%O group slave _routes is %O\n", ME, _members))
#else
P3(("%O group slave msg(%O, %O, ...)\n", ME, source, method))
#endif
#ifdef CACHE_PRESENCE
// before lynX starts complaining:
// we could cache the presenity value here
if (vars["_degree_availability"])
persistent_presence(source, vars["_degree_availability"]);
//, vars["_degree_mood"]);
# if 0 // else? why should we have presence without _degree_availability ?
else switch(method) {
case "_notice_presence_here":
persistent_presence(source, AVAILABILITY_HERE);
break;
case "_notice_presence_here_busy":
persistent_presence(source, AVAILABILITY_BUSY);
break;
case "_notice_presence_away":
case "_notice_presence_away_manual":
case "_notice_presence_away_automatic":
persistent_presence(source, AVAILABILITY_AWAY);
break;
case "_notice_presence_absent_vacation":
persistent_presence(source, AVAILABILITY_VACATION);
break;
case "_notice_presence_absent":
persistent_presence(source, AVAILABILITY_OFFLINE);
break;
}
# endif
#endif
#ifdef PERSISTENT_SLAVES
/* the lazy update strategy */
if (vars["_number_revision"] && !intp(vars["_number_revision"]))
vars["_number_revision"] = to_int(vars["_number_revision"]);
if (vars["_number_revision"] && _revision != vars["_number_revision"]) {
int delta;
/* initialize */
if (_revision == -1) {
_revision = vars["_number_revision"];
} else {
delta = vars["_number_revision"] - _revision;
/* counter increment */
if (delta == 1) {
_revision = vars["_number_revision"];
} else if (delta > 1 || delta < 1) {
P0(("warning: %O revision mismatch! have %O, master has %O, delta is %d\n",
_name, _revision, vars["_number_revision"], delta))
monitor_report("_failure_slave_revision",
object_name(ME) +" · "+ sprintf("revision mismatch with delta %d", delta));
// TODO
// for now, we just log and accept those
_revision = vars["_number_revision"];
}
}
}
if (vars["_number_revision"] > 0) {
PT(("%O (%s) counter revision is %O\n", ME, method, vars["_number_revision"]))
}
m_delete(vars, "_number_revision");
#endif
foreach(o : _members) {
if (objectp(o))
#ifdef CONTEXT_STATE
// may need copy(vars) also
o -> msg(source, method, data, cast_state + vars );
else
sendmsg(o, method, data, vars + temp_state, source);
#else
o -> msg(source, method, data, copy(vars));
else
sendmsg(o, method, data, vars, source);
#endif
/* if one or more of our local users have joined this
* place, we trust the place and allow it to use us as
* a relay for people who are topologically close to us.
* that's how remote users may end up in this structure, too.
*/
}
#ifdef CONTEXT_STATE
temp_state = ([ ]);
#endif
return 1;
}
#ifdef CONTEXT_STATE // {{{
// This won't work ... we have to have _one_ single cast-state
// which is synced with objects entering the context..
//
// TODO
void Reset(mixed source) {
cast_state = ([ ]);
foreach(mixed o : _members) {
unless (objectp(o))
sendmsg(o, 0, 0, ([ "_count" : _members[o] = 0 ]), source);
}
}
void Assign(mixed source, string key, mixed value) {
// we have to check here for source == our context
// + we expect msg to be called between state-changes from different
// packets to reset temp_state
if (stringp(value) && value == "" )
m_delete(cast_state, key);
else
cast_state[key] = value;
temp_state["="+key] = value;
}
void Augment(mixed source, string key, mixed value) {
_augment(cast_state, key, value);
_augment(temp_state, "+"+key, value);
}
void Diminish(mixed source, string key, mixed value) {
int i;
_diminish(cast_state, key, value);
if (member(temp_state, "-"+key)) {
PT(("PANIC! Received list-diminish as a list!"))
} else temp_state["-"+key] = value;
}
#endif // }}}