blob: e22ce1795db3f0b0481f879ab18a549cef117570 [file] [log] [blame]
//--------------------------------------------------------------------------------
// Name des Objects: Aufbewahrungtruhe fuer Autoload-Objekte
//
// Magier: Zesstra
//--------------------------------------------------------------------------------
#pragma strong_types,rtt_checks
#include "schrankladen.h"
inherit LADEN("swift_std_container");
//#include <ansi.h>
#include <class.h>
#include <wizlevels.h>
#include "/d/seher/haeuser/haus.h"
#define IDS 0
#define NAME 1
#define ALDATA 2
#define LOG(x,y) log_file(x,sprintf("%s [%s, %O, %O]: %s\n",dtime(time()),\
(uuid?uuid:" "),PL,object_name(ME),y))
#define STORELOG(x) LOG("zesstra/ALTRUHE_STORE.log",x)
#define PICKLOG(x) LOG("zesstra/ALTRUHE_PICK.log",x)
#define ITEMLOG(x) log_file("zesstra/ALTRUHE_ITEMS.log",\
sprintf("%s [%O]: %s\n",dtime(time()),\
this_interactive()||PL,x))
#define ERRLOG(x) LOG("zesstra/ALTRUHE.ERR",x)
#undef BS
#define BS(x) break_string(x,78)
#define VERSION_OBJ "5"
#ifdef MAINTAINER
#undef MAINTAINER
#endif
#define MAINTAINER ({"zesstra"})
// Savefile der Blueprint
#ifdef SAVEFILE
#undef SAVEFILE
#endif
#define SAVEFILE __FILE__[..<3]
#define DEBUG(x) if (funcall(symbol_function('find_player),MAINTAINER[0]))\
tell_object(funcall(symbol_function('find_player),MAINTAINER[0]),\
"ALTruhe: "+x+"\n")
#define ACCESS (my_secure_level() >= ARCH_LVL)
// Diese 4 sind fuer die Blueprint (Master aller Truhen)
mapping whitelist=([]); // erlaubte Autoloader, alle anderen nicht erlaubt.
mapping blacklist=([]); // bereits explizit durch EM+ abgelehnte Autoloader
mapping vorschlaege=m_allocate(1,2); // vorschlaege der Spieler
mapping data=([]); // hier speichert die BP alle Daten der Truhen
// WICHTIG: dieses Mapping wird in /secure/memory
// abgelegt und muss auch beim Neuladen ggf. von dort
// wieder abgeholt werden. Ausserdem teilen sich alle
// Truhen eines Spielers und diese Blueprint die Mappings
// darin, damit Aenderungen sofort in allen Truhen und dem
// Master bekannt sind.
/* die einzelnen Truhen speichern in autoloader. Format:
3 Werte pro Key, Keys sind die Namen der Blueprints der Objekte:
([<blueprint>: <P_IDS>; <ob->name()>; <P_AUTOLOADOBJ> ])
*/
nosave mapping autoloader=m_allocate(1,3);
nosave string uuid; // UUID des Eigentuemers
nosave object ob_in_bewegung; // uebler Hack eigentlich. :-(
void NotifyInsert(object ob, object oldenv);
static mapping QueryData();
static mapping SetData(mixed data);
protected void save_me();
protected void check_content();
nomask private int my_secure_level(); //Args.
protected void create() {
// Ja, es is Absicht, dass das create der BP erst spaeter abgebrochen wird!
swift_std_container::create();
seteuid(getuid(ME));
// falls dies die BP ist:
// 1. das Savefile zu laden.
// 2. versuchen, die truhendaten von /secure/memory zu holen, damit nach dem
// Neuladen der Master und die Client immer nach _dieselben_ Mappings
// haben.
if (!clonep(ME))
{
// Savefile restaurieren (auch wenn data im Memory ist, muss das sein,
// damit die anderen Variablen wieder eingelesen sind).
restore_object(SAVEFILE);
// jetzt Daten aus Memory holen, sofern verfuegbar
mapping tmp = "/secure/memory"->Load("truhendaten");
if (mappingp(tmp))
{
// Daten aus Savefile durch die vom Memory ersetzen
data = tmp;
}
else
{
// Keine Daten in Memory. Jetzt in jedem Fall den Pointer auf das Mapping data in
// /secure/memory ablegen
if ("/secure/memory"->Save("truhendaten",data) != 1)
{
raise_error("Could not save memory to /secure/memory.");
}
}
}
else
{
// brauchen Clones nicht.
set_next_reset(-1);
data=0; // brauchen Clones nicht.
}
SetProp(P_SHORT, "Eine kleine, magische Holztruhe");
SetProp("cnt_version_obj", VERSION_OBJ);
SetProp(P_NAME, "Holztruhe");
SetProp(P_GENDER, FEMALE);
SetProp(P_NAME_ADJ,({"klein", "magisch"}));
SetProp(P_LONG, BS(
"Die kleine Holztruhe ist aus stabilem Eichenholz gefertigt. Eigentlich "
"saehe sie recht unscheinbar aus, waeren da nicht die vielen kunstvollen "
"Runen an den Seiten und auf dem Deckel.")
+"@@cnt_status@@");
AddId(({"autoloadertruhe", "holztruhe", "truhe"}));
// den Rest vom Create braucht die BP nicht.
if (!clonep(ME)) return;
SetProp(P_LOG_FILE,"zesstra/ALTRUHE.rep");
SetProp(P_WEIGHT, 3000); // Gewicht 5 Kg
// die drei hier sind in diesme Fall eigentlich voellig ohne Bedeutung
SetProp(P_MAX_WEIGHT, 1000000); // Es passen fuer 1000 kg Sachen rein.
SetProp(P_WEIGHT_PERCENT, 100);
SetProp(P_MAX_OBJECTS, 100); // sind eh immer 0 echte Objekte drin.
SetProp(P_VALUE, 0); // Kein materieller Wert. Ist eh nicht verkaufbar.
SetProp(P_NOBUY, 1); // Wird im Laden zerstoert, falls er verkauft wird.
SetProp(P_NOGET, "Das geht nicht. "+Name(WER,1)+" haftet wie magisch am Boden.\n");
SetProp(P_MATERIAL, ([MAT_OAK:49, MAT_MISC_MAGIC:50, MAT_JOFIUM: 1]) );
SetProp(P_INFO, BS("In diese stabile Truhe kannst Du bestimmte "
"Autoload-Objekte hineinlegen und sie lagern, wenn Du sie gerade "
"nicht brauchst. (Mit 'deponiere <was>' kannst Du etwas in der "
"Truhe zur Aufbewahrung deponieren, mit 'entnehme <was> oder "
"'nimm <was> aus truhe' kannst Du einen Gegenstand wieder "
"herausnehmen.)"));
// Prop fuer rebootfeste Moebel
// Der endgueltige Wert (UUID des Spielers) wird per SetBesitzer() gesetzt,
// sobald die Truhe das erste Mal in ein Seherhaus bewegt wurde.
SetProp(H_FURNITURE, 1);
AD(({"platz","groesse"}),
BS("Die Truhe ist recht klein, aber merkwuerdigerweise "
"scheint sie viel mehr an Gegenstaenden aufnehmen zu koennen, als "
"es von ihrer Groesse her scheint."));
AD(({"gegenstand","gegenstaende"}),
BS("Die Truhe scheint zwar vielen Gegenstaenden Platz zu bieten, aber "
"dafuer nimmt sie nicht jeden Gegenstand auf."));
AD(({"holz","eichenholz"}),
BS("Das Eichenholz ist sehr stabil. Es ist ganz glatt geschliffen und "
"traegt viele kunstvolle Runen in sich."));
AD(({"seiten","seite"}),
BS("Die Truhe hat 4 Seiten, die allesamt mit Runen verziert sind."));
AD(({"boden"}),
BS("Der Boden der Truhe traegt keinerlei Runen."));
AD(({"deckel"}),
BS("Auch der Deckel der Truhe ist mit Runen verziert."));
AD(({"runen","rune"}),
BS("Die Runen bedecken alle 4 Seiten und den Deckel der Truhe. Man "
"hat sie zweifellos in muehsamer Arbeit aus dem harten Holz "
"geschnitzt. Anschliessend wurden sie offenbar mit einem "
"Metall gefuellt. Du verstehst zwar ueberhaupt nicht, was "
"die Runen bedeuten, aber sie sind bestimmt magisch, denn wann immer "
"Du den Deckel oeffnest oder schliesst oder Gegenstaende hineinlegst "
"oder herausnimmst, leuchten die Runen hell auf."));
AD(({"metall"}),
BS("Was das wohl fuer ein Metall sein mag? Zweifellos hat es auch was "
"mit Magie zu tun."));
AD(({"magie"}),
BS("In dieser Truhe scheint viel Magie zu stecken, wenn selbst ein "
"Weltuntergang ihr nichts anhaben kann."));
AD(({"arbeit","fertigung"}),
BS("Die Fertigung dieser Truhe muss sehr aufwendig gewesen sein. "
"Kein Wunder, dass die Truhe so teuer ist."));
AD(({"wunder"}),
BS("Ein Wunder scheint es zu sein."));
AD(({"weltuntergang","armageddon"}),
BS("Dir schaudert beim Gedanken an den Weltuntergang."));
AD(({"gedanken"}),
BS("Denk doch lieber an was anderes..."));
AddCmd("deponier|deponiere&@PRESENT","cmd_deponiere",
"Was moechtest Du in der Eichenholztruhe deponieren?");
AddCmd("entnimm|entnehme|entnehm","cmd_entnehmen");
// bei dieser Truhe waere das Erlauben voellig sinnlos. ;-)
RemoveCmd(({"serlaube"}));
}
// keine Truhen zerstoeren, die irgendeinen INhalt haben.
int zertruemmern(string str) {
// aus swift_std_container
string nf_str;
nf_str="Syntax: zertruemmer [Objekt-Id]\n"
+"Bsp.: zertruemmer "+QueryProp(P_IDS)[1]+"\n";
notify_fail("Fehler: Ohne Parameter klappt das nicht.\n"+nf_str);
if(!str) return 0;
notify_fail("Fehler: Du musst eine gueltige Objekt-Id angeben!\n"+nf_str);
if(present(str)!=TO) // Ueberpruefe, ob auch dieses Objekt gemeint ist!
return 0;
if( QueryHausbesitzer() != QueryTP() && !QueryProp("test") )
{
write( BS("Nur "+QueryHausbesitzer()+" darf "+name(WEN,1)+" zertruemmern!"));
return 1;
}
// Objekte enthalten? Wenn ja, abbruch.
if (sizeof(autoloader)) {
tell_object(PL,BS("Du willst gerade zum Schlag ausholen, um "
+name(WEN,1)+ " zu zertruemmern, als Dir einfaellt, dass "
+QueryPronoun(WER)+ " ja gar nicht leer ist! Nene, wer weiss, ob "
"Du das nicht noch brauchen koenntest."));
return 1;
}
// sonst geerbten Kram ausfuehren.
return ::zertruemmern(str);
}
// Zesstra, 1.7.07, fuers Hoerrohr
string GetOwner() {return "zesstra";}
// Prueft das Objekt auf Eignung fuer die Truhe, speichert seine Daten und
// zerstoert es anschliessend. NODROP-Objekte koennen nur so in die Truhe
// gelegt werden, alle anderen koennen auch mit "stecke ... in truhe"
// deponiert werden, woraufhin ebenfalls PreventInsert() und NotifyInsert()
// durchlaufen werden.
protected int cmd_deponiere(string cmd, mixed args) {
if (!objectp(PL)) return 0;
notify_fail(Name(WER,1)+" ist doch geschlossen!\n");
if(QueryProp(P_CNT_STATUS)!=CNT_STATUS_OPEN) return 0;
notify_fail("Was moechtest Du in der Eichenholztruhe deponieren?\n");
if (!stringp(cmd) || !sizeof(cmd)
|| !pointerp(args) || !sizeof(args)) return 0;
object ob=args[0];
if (!objectp(ob)) return 0;
// wuerde die Truhe das Objekt ueberhaupt aufnehmen? Fehlerausgabe durch
// PrevenInsert()
if (PreventInsert(ob)) return 1;
// Ausziehen... Schade, dass DoUnwear nix vernuenftiges an Rueckgabewert
// hat. :-(
if (objectp(ob->QueryProp(P_WORN))) {
ob->DoUnwear();
if (objectp(ob->QueryProp(P_WORN))) {
tell_object(PL, BS("Du musst "+ ob->name(WEN,1)+ "zunaechst einmal "
"ausziehen!"));
return 1;
}
}
// wegstecken
if (objectp(ob->QueryProp(P_WIELDED))) {
ob->DoUnwield();
if (objectp(ob->QueryProp(P_WIELDED))) {
tell_object(PL, BS("Du musst "+ ob->name(WEN,1)+ "zunaechst einmal "
"wegstecken!"));
return 1;
}
}
// NO_CHECK und Silent-Bewegung, Meldungen an den Spieler selber machen.
tell_object(PL, BS("Du steckst "+ ob->name(WEN,1) + " in " + name(WEN,1) +
"."));
tell_room(environment(),BS(PL->Name(WER) + " legt " + ob->name(WEN,0) +
" in " + name(WEN,1) + " hinein."),({PL}));
ob->move(ME, M_NOCHECK|M_SILENT);
return 1;
}
// alternative zum "nimm bla aus truhe". Spieler wollten was kurzes dafuer
// haben.
protected int cmd_entnehmen(string cmd) {
int res;
mixed noget;
if (!objectp(PL)) return 0;
notify_fail(Name(WER,1)+" ist doch geschlossen!\n");
if(QueryProp(P_CNT_STATUS)!=CNT_STATUS_OPEN) return 0;
notify_fail(BS("Was moechtest Du aus "+name(WEM,1)+ " entnehmen?\n"));
if (!stringp(cmd) || !sizeof(cmd)) return 0;
object *obs=present_objects(cmd);
if (!sizeof(obs) || !objectp(obs[0]))
return 0;
// NOGET ist hier bloed. So ist es zwar auch nicht richtig doll... *seufz*
// Die hier ist/waere aber nen uebler Hack, erstmal auskommentiert lassen.
// also, P_NOGET sichern.
/*if (!closurep(noget=ob->Query(P_NOGET,F_QUERY_METHOD))) {
noget=ob->Query(P_NOGET,F_VALUE);
ob->Set(P_NOGET,0,F_VALUE);
}
else {
ob->Set(P_NOGET,0,F_QUERY_METHOD);
}*/
// nehmen.
res=PL->pick_obj(obs[0]);
// P_NOGET zurueckschreiben. (Ja, wenn eine Closure als F_VALUE drinsteht,
// landet die jetzt als F_QUERY_METHOD im Objekt.
/*if (closurep(noget)) {
ob->Set(P_NOGET,noget,F_QUERY_METHOD);
}
else
ob->Set(P_NOGET,noget,F_VALUE);
*/
return(res);
}
// Hier wird auch das PreventInsert() von der Blueprint gerufen. Das
// erleichtert es, die Liste an erlaubten Objekten zu aendern, ohne dass man
// alle Clones ersetzen muss.
varargs int PreventInsert(object ob) {
string oname;
// Das Objekt, was die Truhe gerade selber bewegt, wird ignoriert.
if (!objectp(ob) || ob_in_bewegung==ob) return 0;
oname=BLUE_NAME(ob);
// Pruefung in Clonen:
if (clonep(ME))
{
// nur Eigentuemer
if (!stringp(uuid) || !sizeof(uuid)) {
if (objectp(PL))
tell_object(PL,BS(sprintf("%s gehoert Dir nicht, daher kannst Du "
"auch keine Gegenstaende in %s legen.",
Name(WER,1),QueryPronoun(WEN))));
return 1;
}
if (!objectp(PL) || getuuid(PL)!=uuid) {
if (objectp(PL))
tell_object(PL,BS(sprintf("Nur %s darf Gegenstaende in %s "
"hineinlegen.",capitalize(explode(uuid,"_")[0]),name(WEN,1))));
return 1;
}
// jedes Objekt nur einmal.
if (member(autoloader,oname)) {
if (objectp(PL))
tell_object(PL,BS(Name(WER,1)+ " kann von einem Gegenstand jeweils "
"nur ein Exemplar aufnehmen, es ist aber bereits "
+ob->name(WER,0) + " in " + QueryPronoun(WEM) + "."));
return 1;
}
// jetzt Erlaubnisliste der BP fragen.
if (objectp(blueprint(ME)))
return blueprint(ME)->PreventInsert(ob);
else
return load_object(load_name(ME))->PreventInsert(ob);
}
// Ende fuer Pruefung fuer Clone.
// ab hier jetzt die Pruefung durch die BP.
// Keine (freigegebener) Autoloader? Hat in diesem Container nix verloren!
if( !ob->QueryProp(P_AUTOLOADOBJ) ) {
if (objectp(PL))
tell_object(PL,BS("In "+name(WEN,1)
+" kannst Du nur Autoload-Objekte hineinlegen. "));
return 1;
}
else if (member(blacklist,oname)) {
if (objectp(PL))
tell_object(PL,BS("In "+name(WEN,1)
+" kannst Du nur dafuer erlaubte Autoload-Objekte hineinlegen. "
+ob->Name(WER,1) + " wurde explizit als nicht erwuenscht "
"befunden."));
return 1;
}
else if (!member(whitelist,oname)) {
if (!member(vorschlaege,oname)) {
vorschlaege[oname,0]=ob->name(WER,0) || "";
vorschlaege[oname,1]=ob->short() || "";
}
if (objectp(PL))
tell_object(PL,BS("In "+name(WEN,1)
+" kannst Du nur dafuer erlaubte Autoload-Objekte hineinlegen. "
+ob->Name(WER,1) + " ist momentan nicht auf der Liste. Der "
"Gegenstand wurde jetzt als Vorschlag gespeichert. Bitte hab "
"etwas Geduld, bis sich jemand den Gegenstand angeschaut hat."));
// ggf. reset reaktivieren und Maintainer informieren.
if (!query_next_reset())
set_next_reset(1);
return 1;
}
// getragenes?
if (ob->QueryProp(P_WORN)) {
ob->DoUnwear(0);
if (ob->QueryProp(P_WORN)) { //ARGL. GRUMMEL.
if (objectp(PL))
tell_object(PL,BS("Willst Du "+ob->name(WEN,1) + " nicht erstmal "
"ausziehen?"));
return 1;
}
}
// enthaelt es irgendwelche anderen Objekte?
if (sizeof(all_inventory(ob))) {
if (objectp(PL))
tell_object(PL,BS(ob->Name(WER,1) + " ist nicht leer."));
return 1;
}
// andere Einschraenkungen, als hier geprueft werden, gibt es nicht.
return 0;
}
// hier ist das Objekt jetzt in der Truhe, d.h. Daten speichern und Objekt
// destructen. ;)
void NotifyInsert(object ob, object oldenv) {
// Das Objekt, was die Truhe gerade selber bewegt, wird ignoriert.
if (!objectp(ob) || ob==ob_in_bewegung)
return;
STORELOG(sprintf("%s deponiert %s [%O], Daten: %O",getuid(PL),ob->name(WEN,0),
ob,ob->QueryProp(P_AUTOLOADOBJ)));
// noetig sind die IDs, den Namen und die AUTOLOADOBJ-Daten des Objekts.
// ;-)
autoloader[BLUE_NAME(ob),ALDATA]=ob->QueryProp(P_AUTOLOADOBJ);
autoloader[BLUE_NAME(ob),IDS]=ob->QueryProp(P_IDS);
// Objekte, die 0 als short liefern, kriegen eine Standard-Short und werden
// sichtbar gemacht, da sie sonst nicht wiederzufinden sind. Objekte, die
// unsichtbar sein sollen, duerfen nicht erlaubt werden.
// TODO eine ID als Short nehmen?
autoloader[BLUE_NAME(ob),NAME]=capitalize((ob->short()||"<Unbekannt>.\n")[..<3]);
// nach dem Move die realen Objekte destructen.
call_out(#'check_content, 0);
save_me();
}
// Objekt wurde entnommen, also aus der Liste der enthaltenen Autoloader
// loeschen. Ausserdem Objekt konfigurieren. Dies wird erst hier gemacht und
// nicht in create_object(), damit es so aehnlich wie moeglich zum clonen der
// Autoloader beim erstellen des Spielerobjektes wird (dort wird erst bewegt,
// dann konfiguriert).
void NotifyLeave(object ob, object dest) {
string error, oname;
if (!objectp(ob))
return;
oname=BLUE_NAME(ob);
if (!member(autoloader,oname)) {
// Das sollte definitiv nicht passieren.
ERRLOG(sprintf("Gegenstand (%O) wurde entnommen, der nicht "
"gespeichert war!",ob));
return;
}
// wenn kein Fehler: Objekt aus Liste loeschen. Sonst wird das Objekt
// zerstoert. Damit bleiben die Autoloader-Daten hier erhalten und der
// Spieler hat kein disfunktionales Objekt im Inventar.
if (error) {
ERRLOG(sprintf("Fehler beim Konfigurieren von %O. Fehlermeldung: %O."
"Daten: %O",ob,error,autoloader[oname,ALDATA]));
ob->remove(1);
if (objectp(ob))
destruct(ob);
}
else {
PICKLOG(sprintf("Objekt (%O) wurde entnommen.",ob));
m_delete(autoloader,oname);
// speichern
save_me();
}
}
protected void check_content() {
// wenn Objekte noch in der Truhe sind, also nicht erfolgreich in
// einen Spieler bewegt wurden, werden zerstoert. Datenverlust gibt es
// hierbei nicht, weil die Daten der Autoloader nur durch NotifyLeave()
// geloescht werden.
foreach(object ob: all_inventory()) {
ob->remove(1);
if (objectp(ob)) destruct(ob);
}
}
// hier nochmal schauen, ob das Objekt auch in den richtigen Spieler bewegt
// wird... Falls das move dann hier abgebrochen wird, wird das Objekt im
// naechsten HB von der Truhe zerstoert. (chk_contents())
varargs int PreventLeave(object ob, mixed dest) {
object ob2;
//DEBUG(sprintf("PreventLeave(): Ob: %O, Dest: %O (%O)",
// ob,dest,(objectp(dest)?"Objekt":"Non-Object")));
if (!objectp(ob)) return 0;
// falls string uebergeben, erstmal zug. Spieler finden.
if (stringp(dest) && sizeof(dest))
dest=find_player(dest);
// richtiges INteractive? Dann darf das Objekt raus. Sonst nicht. ;-)
if (!objectp(dest) || !interactive(dest) || getuuid(dest)!=uuid)
return 1;
// pruefen, ob der Spieler schon ein Objekt dieser Blueprint dabei hat. Wenn
// ja, Abbruch, das koennte sonst zuviele Probleme geben.
if (objectp(ob2=present_clone(ob,dest))) {
tell_object(dest,BS("Du hast bereits "+ob2->name(WEN,0) +
" dabei. Zwei Gegenstaende dieser Art kannst du nicht gleichzeitig "
"mit Dir herumtragen."));
return 1; //nicht rausnehmen.
}
return 0;
}
// Objekte ausgeben, die hier angeblich drin sind. ;-) Gibt aber nur die
// Autoloader aus, keine echten Objekt, die Container sind. Allerdings sollte
// sowas eh nicht vorkommen. ;-)
// flags: 1 - return array, 2 - don't collect equal objects '
// flags: 4 - don't append infos for wizards
varargs mixed make_invlist(object viewer, mixed inv, int flags) {
int iswiz;
mixed objs;
iswiz = IS_LEARNER( viewer ) && viewer->QueryProp(P_WANTS_TO_LEARN);
// Mapping benutzen, um multiplen Overhead fuer allokation im foreach() zu
// vermeiden.
objs=m_allocate(sizeof(autoloader),1);
foreach(string oname, string *ids, string sh: autoloader) {
if (iswiz && !(flags & 4))
objs[oname]=sh + ". ["+oname+"]";
else
objs[oname]=sh + ".";
}
if(flags & 1) return(m_values(objs)-({""}));
if(!sizeof(autoloader)) return "";
return sprintf("%"+(sizeof(objs) > 6 ? "#" : "=")+"-78s",
implode(m_values(objs)-({""}), "\n")) + "\n";
}
// erzeugt das benannte Objekt und liefert es zurueck. Liefert 0 im Fehlerfall.
// ausserdem bewegt es das Objekt in die Truhe, damit es ein Env hat und die
// Truhe via NotifyLeave() mitkriegt, dass es tatsaechlich entnommen wurde.
private object create_object(string oname) {
string error;
object ob;
mixed noget;
if (!member(autoloader,oname)) return 0;
//Blueprint finden (ja, das ist nicht unbedingt noetig, man koennte auch
//direkt clonen)
if (error=catch(ob=load_object(oname);publish) ||
!objectp(ob)) {
ERRLOG(sprintf("Konnte %s nicht laden/finden. Fehler: %O",
oname,error));
return 0;
}
// clonen
if (error=catch(ob=clone_object(oname);publish) ||
!objectp(ob)) {
ERRLOG(sprintf("Konnte %s nicht clonen. Fehler: %O",
oname,error));
return 0;
}
// konfigurieren
error=catch(ob->SetProp(P_AUTOLOADOBJ,autoloader[oname,ALDATA]);publish);
//Objekt bewegen, dabei Objekt in glob. Var. merken, damit PreventInsert()
//und NotifyInsert() es ignorieren. *seufz*
ob_in_bewegung=ob;
ob->move(ME,M_NOCHECK);
ob_in_bewegung=0;
// jetzt noch nen Callout starten, damit das Objekt zerstoert wird, wenn es
// nicht wirklich in einen Spieler bewegt wird.
call_out(#'check_content,1);
return(ob);
}
// Schauen, ob die truhe ein Objekt mit passender ID enthaelt. Wenn
// ja, das Objekt erzeugen und zurueckliefern.
// Diese Funktion hat eine Reihe von Nachteilen bzw. Defiziten ggue. der
// normalerweise in Containern definierten Funktion:
// - Nur das erste Objekt, auf das id passt. Ich hab erstmal keine Lust,
// hier mehrere Objekte zu erzeugen. Mal schauen, ob es so geht. Auch waere
// es ein Problem, wenn viele Objekt die Evalgrenze bzw. nicht alle in den
// Spieler bewegt werden, nachdem sie erzeugt wurden.
// - keine komplexen Ausdruecke, nur reine IDs.
// Ich halte den Aufwand fuer nicht gerechtfertigt.
object *present_objects( string complex_desc ) {
object ob;
if (!stringp(complex_desc) || !sizeof(complex_desc))
return ({});
// diese Funktion liefert nur Objete zurueck, wenn der richtige Interactive
// versucht, sie zu entnehmen. ;-)
if (!objectp(this_interactive()) ||
getuuid(this_interactive())!=uuid)
return ({});
// "alles" liefert das erstbeste Objekt.
if (complex_desc=="alles" && sizeof(autoloader))
{
string oname=m_indices(autoloader)[0];
ob=create_object(oname);
if (objectp(ob)) return ({ob});
}
// ueber alle Eintraege gehen, bis eine ID stimmt, erstes passendes Objekt
// erzeugen und in einem Array zurueckliefern.
foreach(string oname, string *ids: autoloader) {
if (member(ids,complex_desc)==-1) continue;
ob=create_object(oname);
break; //objekt gefunden, fertig hier
}
if (objectp(ob)) return ({ob});
return ({}); // nix gefunden
}
// ******************* Verwaltung *********************************
// registriert die Truhe auf den jeweiligen Eigentuemer.
protected void SetBesitzer(string unused, string newuuid) {
if (!stringp(newuuid) || !sizeof(newuuid)) return;
// wenn schon registriert, abbrechen
if (stringp(uuid) && sizeof(uuid)) return;
uuid=newuuid;
Set(H_FURNITURE,uuid,F_VALUE); //Setmethode umgehen, sonst Rekursion
// ab jetzt nur noch von der Truhe selber.
Set(H_FURNITURE,SECURED,F_MODE_AS);
// Daten fuer den Benutzer aus der Blueprint holen (BP liefert KEINE Kopie
// und es darf KEINE gemacht werden!):
autoloader=(mapping)load_name()->GetData(uuid);
// keine Daten gekriegt? -> Fehler loggen
if (!mappingp(autoloader))
{
ERRLOG(sprintf("Keine gueltigen Daten vom Truhenmaster (BP) erhalten. "
"initialisiere mit leerem Mapping. :-("));
raise_error(sprintf(
"Keine gueltigen Daten vom Truhenmaster (BP) fuer UUID %s "
"erhalten.\n",uuid));
}
}
// Set-Funktion
string _set_h_furniture(mixed arg) {
if (stringp(arg))
{
SetBesitzer(0,arg); // bricht ab, wenn bereits registriert.
}
return(uuid);
}
// Falls das Speichern in der BP nicht klappte, rufen die Clones diese
// Funktion. Schreiben der Daten in in Logfile zur Restaurieren per Hand.
private void EmergencyStore(int res) {
ERRLOG(sprintf("EmergencyStore() called. Rueckgabewert des "
"Truhenmaster (BP) war: %O. Speichere Daten in Logfile. ",res));
write_file(__DIR__+"ALTRUHE.NOTFALLDATEN",
sprintf("Daten fuer %O:\n%O\n",uuid,autoloader));
}
protected void save_me() {
int res;
// nur BP speichern
if (!clonep(ME))
save_object(SAVEFILE);
else
{
if (objectp(blueprint(ME)))
res=(int)blueprint(ME)->StoreData(uuid,autoloader);
else
res=(int)load_object(load_name(ME))->StoreData(uuid,autoloader);
if (res!=1)
EmergencyStore(res); // Daten in einem Notfall-Logfile ablegen.
}
}
// diese Funktion wird vom Seherhausraum gerufen, sobald ein rebootfestes
// Moebelstueck erzeugt und konfiguriert wurde.
void post_create() {
if (!clonep()) return;
// wenn jetzt kein Env: destructen
if (!objectp(environment())) {
remove(1);
return;
}
//beim Schrankmaster registrieren
SCHRANKMASTER->RegisterCnt(ME, QueryProp("cnt_version_std")
+":"+QueryProp("cnt_version_obj"), environment()->QueryOwner(),
environment());
}
// diese Funktion wird vom Schrankmaster gerufen, wenn dieser meint, dass
// dieses Objekt neu erstellt werden sollte.
int UpdateMe()
{
if (!clonep())
return 0;
if (!objectp(environment()))
return 1;
object ob=clone_object(load_name(ME));
if (objectp(ob)) {
object oldenv=environment();
// UUID uebertragen
ob->SetProp(H_FURNITURE, uuid);
// hierhier bewegen, dabei werden UUID und Daten von der neuen Truhe meist
// automatisch geholt.
ob->move(oldenv,M_NOCHECK);
// jetzt erst post_create() rufen!
ob->post_create();
// dieses Objekt rausbewegen (damit das Seherhaus es austraegt).
move("/room/void",M_NOCHECK);
// Seherhausraum speichern (koennte teuer sein!)
oldenv->Save(1);
// selbstzerstoeren
// __INT_MAX__ bedeutet: nicht speichern, die neue Truhe hat die daten
// schon aus der BP abgefragt.
remove(__INT_MAX__);
}
return(1);
}
// bei Selbstzerstoerung speichern bzw. Daten an die Blueprint uebermitteln
// und beim Schrankmaster abmelden.
varargs int remove(int silent) {
string uid="";
// Blueprint speichern im Savefile, Clones uebertragen die Daten hier an die
// Blueprint. Clones nur, wenn nicht __INT_MAX__ uebergeben wurde.
if (silent!=__INT_MAX__ || !clonep())
save_me();
if (clonep()) {
// Clone melden sich beim Schrankmaster ab.
if (objectp(environment())) {
uid=environment()->QueryOwner();
}
//beim Schrankmaster deregistrieren
SCHRANKMASTER->RemoveCnt(ME,uid);
}
return(::remove(silent));
}
// ***************** NUR BLUEPRINTS *********************************
// neuen Autoloader zulassen (nur EM+!)
varargs int AddAutoloader(string path,string nam) {
object ob;
if (clonep(ME)) return 0;
if (!stringp(path) || !sizeof(path)) return -1;
if (!ACCESS) return -2;
//if (!ARCH_SECURITY) return -2;
if (member(whitelist,path)) return -3;
if (catch(ob=load_object(path);publish)
|| !objectp(ob))
return -4;
// wenn Name nicht angegeben und auch nicht aus BP ermittelbar: Abbruch.
if (!stringp(nam) || !sizeof(nam)) {
nam=ob->name(WER,0);
if (!stringp(nam) || !sizeof(nam)) return -5;
}
ITEMLOG(sprintf("%s erlaubt: %s (%s)\n",getuid(this_interactive()),
path,nam));
whitelist+=([path:capitalize(nam)]);
if (member(vorschlaege,path))
m_delete(vorschlaege,path);
save_me();
return(1);
}
// Autoloader aus Erlaubnisliste entfernen (nur EM+!)
int RemoveAutoloader(string path) {
if (clonep(ME)) return 0;
if (!stringp(path) || !sizeof(path)) return -1;
if (!ACCESS) return -2;
//if (!ARCH_SECURITY) return -2;
if (!member(whitelist,path)) return -3;
ITEMLOG(sprintf("%s widerruft Erlaubnis: %s (%s)\n",getuid(this_interactive()),
path,whitelist[path]));
whitelist-=([path]);
save_me();
return(1);
}
// erlaubte Autoloader abfragen
mapping QueryAutoloader() {
return(copy(whitelist));
}
// neuen Autoloader in Blacklist eintragen (nur EM+!)
varargs int AddBlacklist(string path, string nam) {
object ob;
if (clonep(ME)) return 0;
if (!stringp(path) || !sizeof(path)) return -1;
if (!ACCESS) return -2;
//if (!ARCH_SECURITY) return -2;
if (member(blacklist,path)) return -3;
if (catch(ob=load_object(path);publish)
|| !objectp(ob))
return -4;
// wenn Name nicht angegeben und auch nicht aus BP ermittelbar: Abbruch.
if (!stringp(nam) || !sizeof(nam)) {
nam=ob->name(WER,0);
if (!stringp(nam) || !sizeof(nam)) return -5;
}
// ggf. erlaubten entfernen.
if (member(whitelist,path))
RemoveAutoloader(path);
ITEMLOG(sprintf("%s verbietet: %s (%s)\n",getuid(this_interactive()),
path,nam));
blacklist+=([path:capitalize(nam)]);
if (member(vorschlaege,path))
m_delete(vorschlaege,path);
save_me();
return(1);
}
// Autoloader aus Blacklist entfernen (nur EM+!)
int RemoveBlacklist(string path) {
if (clonep(ME)) return 0;
if (!stringp(path) || !sizeof(path)) return -1;
if (!ACCESS) return -2;
//if (!ARCH_SECURITY) return -2;
if (member(blacklist,path)==-1) return -3;
ITEMLOG(sprintf("%s loescht: %s (%s) von Blacklist\n",getuid(this_interactive()),
path,blacklist[path]));
blacklist-=([path]);
save_me();
return(1);
}
// gesperrte Autoloader abfragen
mapping QueryBlacklist() {
return(copy(blacklist));
}
// vorschlaege abfragen
varargs mixed QueryVorschlaege(int format) {
string res="\n";
if (!format) return(copy(vorschlaege));
foreach(string oname, string nam, string sh: vorschlaege) {
res+=sprintf("%.78s:\n %.37s,%.37s\n",oname,nam,sh);
}
if (format==2 && objectp(PL))
tell_object(PL,res);
else
return res;
return 0;
}
// Wird diese funktion in der Blueprint gerufen, liefert sie die Daten fuer
// eine Truhe mit dieser UUID zurueck. Aber nur dann, wenn die auch von einer
// truhe abgerufen wird und keinem beliebigen anderen Objekt oder wenn ein EM
// abfragt.
mapping GetData(string uid) {
if (clonep(ME)) return 0;
if (extern_call())
{
if (!objectp(previous_object())) return 0;
if (blueprint(previous_object()) != ME
&& load_name(previous_object()) != __FILE__[..<3]
&& !ACCESS)
return(0);
}
if (!stringp(uid)) return ([]);
if (!member(data, uid))
data[uid] = m_allocate(1,3);
// Absichtlich keine Kopie, damit die Truhen (falls jemand mehrere kauft)
// sich alle eine Kopie des Mappings teilen.
return data[uid];
}
// Loest die Speicherung der Daten im Savefile aus, denn irgendwelche Daten
// haben sich in einem Clone geaendert, welche noch auf die Platte muessen.
// Diese Funktion speicherte frueher Daten aus den Clones wieder im Mapping
// des Masters (der Blueprint). Dies ist heute nicht mehr so, weil alle Truhen
// (eines Benutzers) sich data[uid] teilen. Daher wird hier ein Fehler
// ausgeloest, wenn es von einer Truhe ein Mapping kriegt, welches nicht
// identisch (!) mit dem data[uid] ist.
// liefert 0 im Fehlerfall!
int StoreData(string uid, mapping tmp) {
if (clonep(ME)) return 0;
// Aber nur dann, wenn die auch von einer truhe abgerufen wird und keinem
// beliebigen anderen Objekt oder wenn ein EM setzt.
if (extern_call())
{
if (!objectp(previous_object()))
return 0;
if (blueprint(previous_object()) != ME
&& load_name(previous_object()) != __FILE__[..<3]
&& !ACCESS)
return 0;
}
if (!stringp(uid) || !mappingp(tmp))
return(0);
// tmp muss auf _dasselbe_ Mapping zeigen wie data[uid]. Wenn das nicht der
// Fall ist, ist was schiefgelaufen. Jedenfalls wird dann hier nen Fehler
// ausgeloest.
if (tmp != data[uid])
{
// if(program_time(previous_object()) < 1400525694)
// data[uid]=tmp;
// else
raise_error("StoreData() gerufen und Daten sind nicht identisch "
"mit den bereits bekannten.\n");
}
// Absichtlich keine Kopie, damit sich alle Truhen das Mapping teilen.
//data[uid]=tmp;
// Savefile muss natuerlich geschrieben werden, fuer den naechsten Reboot.
if (find_call_out(#'save_me)==-1)
call_out(#'save_me,10); // Speichervorgaenge ggf. sammeln (upd -ar ...)
return(1);
}
// Maintainer ueber Truhenvorschlaege informieren
void reset() {
if (clonep() || !sizeof(vorschlaege)) {
// ohne Vorschlaege ist auch kein reset noetig.
set_next_reset(-1);
return;
}
set_next_reset(12000); // alle 3-4h reicht.
foreach(string uid: MAINTAINER) {
object pl=find_player(uid);
if (objectp(pl) && query_idle(pl) < 1800)
tell_object(pl,BS("Es gibt neue Objektvorschlaege fuer die "
"Autoloadertruhe. Bitt schau da doch bei Gelegenheit mal drueber."));
}
}
// *************************************************************************
// **************** SONSTIGES **********************************************
// *seufz*
// secure_level() aus der simul_efun ist hier momentan nicht brauchbar, weil
// dort auch p/seher/moebel/autoloadertruhe in der Callerkette steht und das
// ein levle von 0 hat. *seufz*
nomask private int my_secure_level() {
int *level;
//kette der Caller durchlaufen, den niedrigsten Level in der Kette
//zurueckgeben. Zerstoerte Objekte (Selbstzerstoerer) fuehren zur Rueckgabe
//von 0.
//caller_stack(1) fuegt dem Rueckgabearray this_interactive() hinzu bzw. 0,
//wenn es keinen Interactive gibt. Die 0 fuehrt dann wie bei zerstoerten
//Objekten zur Rueckgabe von 0, was gewuenscht ist, da es hier einen
//INteractive geben muss.
level=map(caller_stack(1),function int (object caller)
{if (objectp(caller))
return(query_wiz_level(geteuid(caller)));
return(0); // kein Objekt da, 0.
} );
return(min(level)); //den kleinsten Wert im Array zurueckgeben (ggf. 0)
}
// debugkram
mixed _query_content() {
//deep_copy, damit nicht jemand einfach so an den Daten rumbasteln kann.
return(deep_copy(autoloader));
}
mixed _query_owner() {
return(uuid);
}
int DeleteData(string uid) {
if (clonep(ME)) return 0;
if (!stringp(uid) || !sizeof(uid)) return -1;
if (!ACCESS) return -2;
//if (!ARCH_SECURITY) return -2;
m_delete(data,uid);
save_me();
return(1);
}
mixed testfun() {return "bin da\n";}