Added public files
Roughly added all public files. Probably missed some, though.
diff --git a/std/player/protocols/gmcp.c b/std/player/protocols/gmcp.c
new file mode 100644
index 0000000..b951daa
--- /dev/null
+++ b/std/player/protocols/gmcp.c
@@ -0,0 +1,785 @@
+// MorgenGrauen MUDlib
+//
+// gmcp.c -- Verwaltung von GMCP im Spielerobjekt
+//
+// $Id$
+
+#pragma strong_types,save_types
+#pragma range_check
+#pragma no_clone
+#pragma no_shadow
+#pragma pedantic
+
+#include <regexp.h>
+#include <telnet.h>
+
+#define NEED_PROTOTYPES
+#include <player/base.h>
+#include <thing/properties.h>
+#include <living/attributes.h>
+#include <player/gmcp.h>
+#include <thing/description.h>
+#include <living/description.h>
+#undef NEED_PROTOTYPES
+
+#include <properties.h>
+#include <new_skills.h>
+#include <rooms.h>
+#include <tls.h>
+
+inherit "/secure/telnetneg-structs.c";
+
+struct gmcp_mod_s {
+ string id; // Name des GMCP-Moduls (z.B. "MG.Char")
+ int version; // Version des aktivierten moduls
+ closure sendcl; // Handler zum Senden (lfun-closure)
+ closure recvcl; // Handler zum Empfangen (lfunc-closure)
+ mixed data; // optional data of the module
+};
+
+nosave mapping gmcpdata;
+/* Struktur:
+ Jedes Modul hat einen Schluessel im Toplevel, worunter ggf. seine Daten
+ abgelegt sind. Die Daten sind eine struct gmcp_mod_s. Der Schluessel ist
+ der Modulname OHNE Version.
+ */
+#define NEED_PROTOTYPES
+#include "/secure/telnetneg.h"
+#undef NEED_PROTOTYPES
+
+//#define __GMCP_DEBUG__ 1
+// Low priority debug messages
+#define GMCP_DEBUG(pre,msg,prio) if (interactive(this_object()) \
+ && gmcpdata) \
+ GMCP_debug(pre,msg,prio);
+// higher priority error messages
+#define GMCPERROR(msg) if (interactive(this_object()) \
+ && gmcpdata) \
+ GMCP_debug("ERROR",msg,10);
+
+
+// **************** API nach Aussen folgt ab hier ********************
+
+// Wird vom Spielerobjekt gerufen, wenn sich Daten am Charakter veraendert
+// haben, die gesendet werden sollten.
+// Dies ist eigentlich nur ein Wrapper, der die Daten an den Handler eines
+// Moduls weitergibt, welches vom Client aktiviert wurde. Hierzu kommen zur
+// Zeit 2 in Frage: MG.Char (bevorzugt) und Char (minimaler Support).
+/*protected*/ int GMCP_Char(mapping data) {
+
+ if (!mappingp(gmcpdata)) return 0;
+
+ // Als erstes schauen, ob der Client MG.Char aktiviert hat.
+ struct gmcp_mod_s mod = gmcpdata["MG.char"];
+ if (structp(mod) && closurep(mod->sendcl))
+ {
+ funcall(mod->sendcl, data);
+ return 1;
+ }
+ // Dann noch das Modul char pruefen. Das ist aber ziemlich eingeschraenkt
+ // und es gibt hoffentlich nicht viele Clients, die es benutzen.
+ // (Aardwolf-Modul)
+ mod = gmcpdata["char"];
+ if (structp(mod) && closurep(mod->sendcl))
+ {
+ funcall(mod->sendcl, data);
+ return 1;
+ }
+ // Dann noch das Modul Char pruefen. Das ist aber ziemlich eingeschraenkt
+ // und es gibt hoffentlich nicht viele Clients, die es benutzen.
+ // (IRE-Modul)
+ mod = gmcpdata["Char"];
+ if (structp(mod) && closurep(mod->sendcl))
+ {
+ funcall(mod->sendcl, data);
+ return 1;
+ }
+ return 0;
+}
+
+/*protected*/ int GMCP_Channel(string msg, string channel, string sender) {
+ if (!mappingp(gmcpdata)) return 0;
+ // comm.channel Modul aktiv?
+ struct gmcp_mod_s mod = gmcpdata["comm.channel"];
+ if (structp(mod) && closurep(mod->sendcl))
+ {
+ funcall(mod->sendcl, (["chan":channel, "player": sender,
+ "msg": msg]) );
+ return 1;
+ }
+ return 0;
+}
+
+/*protected*/ int GMCP_Room() {
+ if (!mappingp(gmcpdata)) return 0;
+ // MG.room Modul aktiv?
+ struct gmcp_mod_s mod = gmcpdata["MG.room"];
+ if (structp(mod) && closurep(mod->sendcl))
+ {
+ funcall(mod->sendcl, 0);
+ return 1;
+ }
+ return 0;
+}
+
+// **************** Ab hier folgen eher die Lowlevel-Dinge ***********
+private void GMCP_debug(string pre, string msg, int prio) {
+ struct gmcp_mod_s mod = gmcpdata["Core"];
+ if (mod && (mod->data)["Debug"] >= prio)
+ tell_object(this_object(), sprintf("GMCP %s: %s\n",pre,msg));
+}
+
+private void GMCP_send(string cmd, mixed data)
+{
+ GMCP_DEBUG("GMCP_send",sprintf("%s %O",cmd,data), 30);
+ send_telnet_neg_str(sprintf("%c%c%s %s", SB, TELOPT_GMCP,
+ cmd, json_serialize(data)), 1);
+}
+
+private void GMCP_unregister_module(string mod)
+{
+ int version;
+ // Wenn nicht "mod version" Schema, ignorieren
+ if (sscanf(mod, "%s %d", mod, version) != 2)
+ return;
+
+ if (mod=="Core") // darf nicht abgeschaltet werden.
+ return;
+
+ m_delete(gmcpdata, mod);
+}
+
+private void GMCP_register_module(string modname)
+{
+ int version;
+ GMCP_DEBUG("register_module(): trying ... ",modname, 20);
+ // Wenn nicht "mod version" Schema, ignorieren
+ if (sscanf(modname, "%s %d", modname, version) != 2)
+ return;
+
+// GMCP_DEBUG("register_module()",modname + " v" + version);
+
+ // Modul (ggf. mit anderer Version) bereits aktiv?
+ struct gmcp_mod_s mod = gmcpdata[modname];
+ if (structp(mod)) {
+ // wenn gleicher Name und Version, wird nix gemacht, bei anderer Version
+ // wird ein neuer Handler eingetragen und die Daten geloescht.
+ // Wenn nicht-existierende Modul/Version-Kombi angefordert wird, ist das
+ // Modul hinterher aus.
+ if (mod->id == modname && mod->version == version)
+ return;
+ else
+ m_delete(gmcpdata,modname);
+ }
+
+ // Das GMCP-Modul ist nur verfuegbar, wenn es zu der Kombination aus mod und
+ // version einen Handler zum Senden gibt...
+ // Der Handler ist: GMCP_<mod>_v<version>_send, aber in <mod> werden alle
+ // "." durch "_" ersetzt.
+ string replacedname = regreplace(modname, "\\.", "_", RE_GLOBAL);
+ closure sendcl = symbol_function(sprintf("GMCPmod_%s_v%d_send",
+ replacedname, version),
+ this_object());
+ if (!sendcl)
+ return;
+ // Diese Closure darf 0 sein. Dann findet keine Behandlung von vom Client
+ // gesendeten Kommandos statt. Was fuer die meisten Module auch in Ordnung
+ // ist, da sie dem Client keine Kommandos anbieten.
+ closure recvcl = symbol_function(sprintf("GMCPmod_%s_v%d_recv",
+ replacedname, version),
+ this_object());
+
+ GMCP_DEBUG("register_module()",modname+" erfolgreich registriert.",10);
+
+ mod = (<gmcp_mod_s> id: modname, version : version,
+ sendcl : sendcl, recvcl: recvcl, data: ([]) );
+ gmcpdata[modname] = mod;
+
+ // Zum schluss noch den Senden-handler mal rufen, damit der mal alle
+ // verfuegbaren daten sendet.
+ funcall(mod->sendcl, 0);
+}
+
+// Handler fuer das Core Modul von GMCP
+// Gerufen bei Empfang von Kommandos vom Client.
+protected void GMCPmod_Core_v1_recv(string cmd, mixed args)
+{
+ struct gmcp_mod_s mod = gmcpdata["Core"];
+ mapping data = mod->data;
+
+/* if (!mod)
+ {
+ GMCPERROR("Command %s for disabled module ignored.");
+ return;
+ }
+ */
+ GMCP_DEBUG("GMCPmod_Core_v1: ", cmd, 20);
+
+ switch (cmd)
+ {
+ case "Core.Hello":
+ if (mappingp(args))
+ data["Hello"] = (["client": args["client"],
+ "version": args["version"] ]);
+ break;
+ case "Core.Supports.Set":
+ if (pointerp(args))
+ {
+ // Alte Module abschalten/loeschen
+ foreach(string m : data["Supports"])
+ GMCP_unregister_module(m);
+ data["Supports"] = args;
+ // Versuchen, die neuen Module zu registrieren
+ foreach(string m : args)
+ GMCP_register_module(m);
+ }
+ else
+ GMCP_DEBUG("GMCPmod_Core_v1: ",
+ "Data for Core.Supports.Set is no array", 5);
+ break;
+ case "Core.Supports.Add":
+ if (!pointerp(data["Supports"]))
+ data["Supports"] = ({});
+ if (pointerp(args))
+ {
+ foreach(string m: args)
+ GMCP_register_module(m);
+ data["Supports"] += args;
+ }
+ break;
+ case "Core.Supports.Remove":
+ if (!pointerp(data["Supports"]))
+ break;
+ if (pointerp(args))
+ {
+ foreach(string m: args)
+ GMCP_unregister_module(m);
+ data["Supports"] -= args;
+ }
+ break;
+ case "Core.Supports.KeepAlive":
+ break; // this is ignored by us.
+ case "Core.Ping":
+ if (intp(args))
+ data["Ping"] = args;
+ // send a ping back
+ GMCP_send("Core.Ping",0);
+ break;
+ case "Core.Debug":
+ if (intp(args) && args >= 0)
+ data["Debug"] = args;
+ break;
+ default:
+ GMCPERROR(sprintf("Unknown GMCP Core cmd %s with args %O",
+ cmd, args));
+ break;
+ }
+}
+
+// Handler fuer das Core Modul von GMCP
+// Gerufen, wenn Daten zu senden sind.
+protected void GMCPmod_Core_v1_send(mapping data)
+{
+ // Zur Zeit nix, spaeter mal Core.Goodbye.
+}
+
+
+// Handler fuer das MG.Char Modul
+// Gerufen bei Empfang von Kommandos vom Client.
+protected void GMCPmod_MG_char_v1_send(mapping data)
+{
+ mapping squeue = m_allocate(5,0);
+ struct gmcp_mod_s mod = gmcpdata["MG.char"];
+ // mod->data fungiert hier auch als Cache der Daten. Die muss man naemlich
+ // leider immer alle senden, nicht nur die geaenderten.
+ if (!mappingp(data))
+ {
+ // Alle verfuegbaren Informationen senden...
+ mod->data = m_allocate(6);
+ m_add(mod->data, "MG.char.base",
+ ([P_NAME: Name(WER),
+ P_GUILD: QueryProp(P_GUILD),
+ P_PRESAY: QueryProp(P_PRESAY), // TODO
+ P_TITLE: QueryProp(P_TITLE),
+ "wizlevel": query_wiz_level(this_object()),
+ P_RACE: QueryProp(P_RACE)]) ); // TODO
+ m_add(mod->data,"MG.char.vitals",
+ ([P_HP: QueryProp(P_HP),
+ P_SP: QueryProp(P_SP),
+ P_POISON: QueryProp(P_POISON) ]) );
+ m_add(mod->data,"MG.char.maxvitals",
+ ([P_MAX_HP: QueryProp(P_MAX_HP),
+ P_MAX_SP: QueryProp(P_MAX_SP),
+ P_MAX_POISON: QueryProp(P_MAX_POISON) ]) );
+ m_add(mod->data,"MG.char.attributes",
+ ([ A_STR: QueryAttribute(A_STR),
+ A_INT: QueryAttribute(A_INT),
+ A_DEX: QueryAttribute(A_DEX),
+ A_CON: QueryAttribute(A_CON) ]) );
+ m_add(mod->data,"MG.char.info",
+ ([P_LEVEL: QueryProp(P_LEVEL),
+ P_GUILD_LEVEL: QueryProp(P_GUILD_LEVEL),
+ P_GUILD_TITLE: QueryProp(P_GUILD_TITLE) ]) );
+ m_add(mod->data,"MG.char.wimpy",
+ ([P_WIMPY: QueryProp(P_WIMPY),
+ P_WIMPY_DIRECTION: QueryProp(P_WIMPY_DIRECTION) ]) );
+ m_add(squeue,"MG.char.base");
+ m_add(squeue,"MG.char.vitals");
+ m_add(squeue,"MG.char.maxvitals");
+ m_add(squeue,"MG.char.attributes");
+ m_add(squeue,"MG.char.info");
+ m_add(squeue,"MG.char.wimpy");
+ // dies wird direkt gesendet, weil es nicht gespeichert werden muss. (wird
+ // nur beim Start des Moduls gesendet).
+ GMCP_send("MG.char.infoVars", ([
+ P_LEVEL: "Spielerstufe", P_GUILD_LEVEL: "Gildenstufe",
+ P_GUILD_TITLE: "Gildentitel" ]) );
+ }
+ else
+ {
+ // nur die in data enthaltenen senden.
+ // jetzt erstmal alles aus data so sortieren, wie es gesendet werden
+ // muss... *seufz*
+ foreach(string key, mixed val : data)
+ {
+ switch(key)
+ {
+ case P_HP:
+ case P_SP:
+ case P_POISON:
+ (mod->data)["MG.char.vitals"] += ([key: val]);
+ m_add(squeue,"MG.char.vitals");
+ break;
+ case P_MAX_HP:
+ case P_MAX_SP:
+ case P_MAX_POISON:
+ (mod->data)["MG.char.maxvitals"] += ([key: val]);
+ m_add(squeue,"MG.char.maxvitals");
+ break;
+ case P_NAME:
+ (mod->data)["MG.char.base"] += ([key: Name(WER)]);
+ m_add(squeue,"MG.char.base");
+ break;
+ case P_RACE:
+ case P_PRESAY:
+ case P_TITLE:
+ case P_GUILD:
+ (mod->data)["MG.char.base"] += ([key: val]);
+ m_add(squeue,"MG.char.base");
+ break;
+ case A_DEX:
+ case A_STR:
+ case A_CON:
+ case A_INT:
+ (mod->data)["MG.char.attributes"] += ([key: val]);
+ m_add(squeue,"MG.char.attributes");
+ break;
+ case P_LEVEL:
+ case P_GUILD_LEVEL:
+ case P_GUILD_TITLE:
+ (mod->data)["MG.char.info"] += ([key: val]);
+ m_add(squeue,"MG.char.info");
+ break;
+ case P_WIMPY:
+ case P_WIMPY_DIRECTION:
+ (mod->data)["MG.char.wimpy"] += ([key: val]);
+ m_add(squeue,"MG.char.wimpy");
+ break;
+ }
+ }
+ }
+ GMCP_DEBUG("GMCPmod_MG_char_v1_send()",
+ sprintf("Data ready: %O, Sendqueue: %O",mod->data, squeue),50);
+
+ // Jetzt die squeue senden...
+ foreach(string key : squeue)
+ {
+ GMCP_send(key, (mod->data)[key]);
+ }
+}
+
+// Handler fuer das MG.Char Modul
+// Gerufen bei Empfang von Kommandos vom Client.
+protected void GMCPmod_MG_char_v1_recv(string cmd, mixed args)
+{
+ // dieses Modul bietet dem Client keine Kommandos an, daher ignorieren.
+ GMCP_DEBUG("GMCPmod_MG_Char_v1_recv","Client-Kommando ignoriert: "+cmd,20);
+}
+
+/*
+// Handler fuer das MG.Room Modul von GMCP
+// Gerufen, wenn Daten zu senden sind.
+protected void GMCPmod_MG_Room_v1_send(mapping data)
+{
+}
+
+// Handler fuer das Room Modul von GMCP
+// Gerufen bei Empfang von Kommandos vom Client.
+protected void GMCPmod_MG_Room_v1_recv(string cmd, mixed args)
+{
+ // dieses Modul bietet dem Client keine Kommandos an, daher ignorieren.
+ GMCP_DEBUG("GMCPmod_MG_Room_v1_recv","Client-Kommando ignoriert: "+cmd,20);
+}
+*/
+
+// Recv Handler fuer das comm.channel Modul von GMCP
+// Gerufen bei Empfang von Kommandos vom Client.
+protected void GMCPmod_comm_channel_v1_recv(string cmd, mixed args)
+{
+ GMCP_DEBUG("GMCPmod_comm_channel_v1_recv",
+ "Client-Kommando ignoriert: "+cmd,20);
+}
+
+// Send Handler fuer das comm.channel Modul von GMCP
+protected void GMCPmod_comm_channel_v1_send(mapping data)
+{
+ // Ganz simpel: einfach raussenden...
+ // Core uebergibt beim Einschalten 0 als data. Dieses modul muss aber beim
+ // Eisnchalten nix machen. Also nur ignorieren.
+ if (mappingp(data))
+ GMCP_send("comm.channel", data);
+}
+
+// Recv Handler fuer das MG.room Modul von GMCP
+// Gerufen bei Empfang von Kommandos vom Client.
+protected void GMCPmod_MG_room_v1_recv(string cmd, mixed args)
+{
+ GMCP_DEBUG("GMCPmod_MG_room_v1_recv",
+ "Client-Kommando ignoriert: "+cmd,20);
+}
+
+// Send Handler fuer das comm.channel Modul von GMCP
+protected void GMCPmod_MG_room_v1_send(mapping data)
+{
+ // Bekommt immer 0 als <data> uebergeben und sucht sich die Daten aus dem
+ // Raum zusammen.
+
+ // Baeh. Warum wird das denn ohne Env gerufen. :-(
+ if (!environment())
+ return;
+
+ // Blind gibt es auch per GMCP nix.
+ if (CannotSee(1))
+ return;
+
+ int restr = environment()->QueryProp(P_MAP_RESTRICTIONS);
+
+ if (restr & MR_NOINFO)
+ return; // gar keine info senden.
+
+ // Anmerkung: int_short() waere cool. Dummerweise uebertraegt das auch
+ // sichtbare Ausgange und Objekte. Insofern: geht nicht.
+ data = ([
+ P_SHORT: process_string(environment()->QueryProp(P_INT_SHORT)||"")+".",
+ "domain": environment()->QueryProp(P_DOMAIN) || "unbekannt",
+ ]);
+
+ // sichtbare Ausgaenge ausgeben
+ mixed hide = environment()->QueryProp(P_HIDE_EXITS);
+ if (hide && !pointerp(hide))
+ data["exits"] = ({}); // alle verstecken
+ else
+ {
+ // Query() verwenden, damit sowohl normale als auch Special Exits
+ // kommen... Die Summe von beiden wuerde auch gehen, aber dann hat man
+ // zwei unnoetige Filter in den Querymethoden. Hngl.
+ mapping exits = environment()->Query(P_EXITS, F_VALUE) || ([]);
+ if (pointerp(hide))
+ data["exits"] = m_indices(exits) - hide;
+ else
+ data["exits"] = m_indices(exits);
+ }
+
+ if (restr & MR_NOUID)
+ data["id"] = "";
+ else
+ data["id"] = hash(TLS_HASH_MD5, object_name(environment()));
+
+ GMCP_send("MG.room.info", data);
+}
+
+
+// Handler fuer das "char" Modul von GMCP (Modul von Aardwolf)
+// Gerufen, wenn Daten zu senden sind.
+protected void GMCPmod_char_v1_send(mapping data)
+{
+ mapping squeue = m_allocate(4,0);
+ struct gmcp_mod_s mod = gmcpdata["char"];
+ // mod->data fungiert hier auch als Cache der Daten. Die muss man naemlich
+ // leider immer alle senden, nicht nur die geaenderten.
+ if (!mappingp(data))
+ {
+ // Alle verfuegbaren Informationen senden...
+ mod->data = m_allocate(4);
+ m_add(mod->data, "char.base", (["name": query_real_name(),
+ "race": QueryProp(P_RACE)]) );
+ m_add(mod->data,"char.vitals", (["hp": QueryProp(P_HP),
+ "mana": QueryProp(P_SP)]) );
+ m_add(mod->data,"char.stats", ([ "str": QueryAttribute(A_STR),
+ "int": QueryAttribute(A_INT),
+ "dex": QueryAttribute(A_DEX),
+ "con": QueryAttribute(A_CON) ]) );
+ m_add(mod->data,"char.status", (["level": QueryProp(P_LEVEL) ]) );
+ m_add(squeue,"char.base");
+ m_add(squeue,"char.vitals");
+ m_add(squeue,"char.stats");
+ m_add(squeue,"char.status");
+ }
+ else
+ {
+ // nur die in data enthaltenen senden.
+ // jetzt erstmal alles aus data so sortieren, wie es gesendet werden
+ // muss... *seufz*
+ foreach(string key, mixed val : data)
+ {
+ switch(key)
+ {
+ case P_HP:
+ (mod->data)["char.vitals"] += (["hp": val]);
+ m_add(squeue,"char.vitals");
+ break;
+ case P_SP:
+ (mod->data)["char.vitals"] += (["mana": val]);
+ m_add(squeue,"char.vitals");
+ break;
+ case P_NAME:
+ case P_RACE:
+ (mod->data)["char.base"] += ([key: val]);
+ m_add(squeue,"char.base");
+ break;
+ case A_DEX:
+ case A_STR:
+ case A_CON:
+ case A_INT:
+ (mod->data)["char.stats"] += ([key: val]);
+ m_add(squeue,"char.stats");
+ break;
+ case P_LEVEL:
+ (mod->data)["char.status"] += ([key: val]);
+ m_add(squeue,"char.status");
+ break;
+ }
+ }
+ }
+ GMCP_DEBUG("GMCPmod_char_v1_send()",
+ sprintf("Data ready: %O, Sendqueue: %O",mod->data, squeue),50);
+
+ // Jetzt die squeue senden...
+ foreach(string key : squeue)
+ {
+ GMCP_send(key, (mod->data)[key]);
+ }
+}
+
+// Handler fuer das "char" Modul von GMCP (Modul von Aardwolf)
+// Gerufen bei Empfang von Kommandos vom Client.
+protected void GMCPmod_char_v1_recv(string cmd, mixed data)
+{
+ // dieses Modul bietet dem Client keine Kommandos an, daher ignorieren.
+ GMCP_DEBUG("GMCPmod_char_v1_recv","Client-Kommando ignoriert: "+cmd,20);
+}
+
+
+// Handler fuer das "Char" Modul von GMCP (Modul von IRE)
+// Gerufen, wenn Daten zu senden sind.
+protected void GMCPmod_Char_v1_send(mapping data)
+{
+ mapping squeue = m_allocate(4,0);
+ struct gmcp_mod_s mod = gmcpdata["Char"];
+ // mod->data fungiert hier auch als Cache der Daten. Die muss man naemlich
+ // leider immer alle senden, nicht nur die geaenderten.
+ if (!mappingp(data))
+ {
+ // Alle verfuegbaren Informationen senden...
+ mod->data = m_allocate(4);
+ m_add(mod->data,"Char.Vitals", (["hp": QueryProp(P_HP),
+ "mp": QueryProp(P_SP),
+ "maxhp": QueryProp(P_MAX_HP),
+ "maxmp": QueryProp(P_MAX_SP) ]) );
+ m_add(mod->data,"Char.Status", (["level": QueryProp(P_LEVEL),
+ "guild": QueryProp(P_GUILD) ]) );
+ m_add(squeue,"Char.Vitals");
+ m_add(squeue,"Char.Status");
+ // dies wird direkt gesendet, weil es nicht gespeichert werden muss. (wird
+ // nur beim Start des Moduls gesendet).
+ GMCP_send("Char.StatusVars", ([
+ "level": "Spielerstufe", "guild": "Gilde" ]) );
+ }
+ else
+ {
+ // nur die in data enthaltenen senden.
+ // jetzt erstmal alles aus data so sortieren, wie es gesendet werden
+ // muss... *seufz*
+ foreach(string key, mixed val : data)
+ {
+ switch(key)
+ {
+ case P_HP:
+ (mod->data)["Char.Vitals"] += (["hp": val]);
+ m_add(squeue,"Char.Vitals");
+ break;
+ case P_SP:
+ (mod->data)["Char.Vitals"] += (["mp": val]);
+ m_add(squeue,"Char.Vitals");
+ break;
+ case P_MAX_HP:
+ (mod->data)["Char.Vitals"] += (["maxhp": val]);
+ m_add(squeue,"Char.Vitals");
+ break;
+ case P_MAX_SP:
+ (mod->data)["Char.Vitals"] += (["maxmp": val]);
+ m_add(squeue,"Char.Vitals");
+ break;
+ case P_LEVEL:
+ case P_GUILD:
+ (mod->data)["Char.Status"] += ([key: val]);
+ m_add(squeue,"Char.Status");
+ break;
+ }
+ }
+ }
+ GMCP_DEBUG("GMCPmod_Char_v1_send()",
+ sprintf("Data ready: %O, Sendqueue: %O",mod->data, squeue),50);
+
+ // Jetzt die squeue senden...
+ foreach(string key : squeue)
+ {
+ GMCP_send(key, (mod->data)[key]);
+ }
+}
+
+// Handler fuer das "char" Modul von GMCP (Modul von Aardwolf)
+// Gerufen bei Empfang von Kommandos vom Client.
+protected void GMCPmod_Char_v1_recv(string cmd, mixed args)
+{
+ // dieses Modul bietet dem Client keine Kommandos an, daher ignorieren.
+ GMCP_DEBUG("GMCPmod_Char_v1_recv","Client-Kommando ignoriert: "+cmd,20);
+}
+
+
+// Handler, der von telnetneg.c gerufen wird.
+private void _std_re_handler_gmcp(struct telopt_s opt, int action,
+ int *optargs)
+{
+ switch(action)
+ {
+ case LOCALON:
+ // super!
+ GMCP_DEBUG("recv:", "LOCALON",10);
+ gmcpdata = ([]);
+ opt->data = gmcpdata; // daten auch dort ablegen.
+ // Coremodule in der Version 1 registrieren (es gibt nur eine).
+ GMCP_register_module("Core 1");
+#ifdef __GMCP_DEBUG__
+ GMCPmod_Core_v1_recv("Core.Debug",30);
+#endif
+ break;
+ case LOCALOFF:
+ // alles abschalten und Daten loeschen
+ GMCP_DEBUG("recv:", "LOCALOFF",10);
+ opt->data = 0;
+ gmcpdata = 0;
+ break;
+ case REMOTEON:
+ case REMOTEOFF:
+ // Huch. Auf Clientseite ist GMCP eigentlich nie an. Ignorieren...
+ GMCP_DEBUG("recv:", "Huh? REMOTE state changed?",50);
+ break;
+ case SB:
+ // Der eigentlich interessante Fall... GMCP-Kommandos
+ if (!mappingp(gmcpdata)) return; // GMCP wohl nicht eingeschaltet...
+ string cmd;
+ mixed args;
+ string payload=to_string(optargs);
+ GMCP_DEBUG("recv", payload,10);
+ if (sscanf(payload,"%s %s", cmd, args) != 2) {
+ // ist vermutlich ein Kommando ohne daten (oder Muell)
+ cmd = payload;
+ //args = 0;
+ }
+ else
+ {
+ string err=catch(args = json_parse(args);nolog);
+ if (err)
+ {
+ printf("\nFehler beim Parsen einer GMCP-Nachricht: %s. "
+ "Nachricht war: '%s'\n"
+ "Befehl: '%s', Argument: '%s'\n\n",err,payload,cmd,args||"");
+ return;
+ }
+ }
+ GMCP_DEBUG("recv", sprintf("Command: %s, Data: %O", cmd, args),20);
+
+ string *cmdparts = explode(cmd, ".");
+ struct gmcp_mod_s mod;
+ string modname;
+ // versuch, ein Modul fuer das Kommando zu finden. Anfangen mit der
+ // Annahme, dass bis zum letzten Punkt der Modulname geht und dann
+ // in jedem case einen Punkt kuerzer werdend.
+ switch(sizeof(cmdparts))
+ {
+ case 4:
+ modname = implode(cmdparts[0..2],".");
+ GMCP_DEBUG("trying modname... ", modname, 20 );
+ if (member(gmcpdata, modname)) {
+ mod = gmcpdata[modname];
+ funcall(mod->recvcl, cmd, args);
+ break;
+ }
+ // Fall-through!
+ case 3:
+ modname = implode(cmdparts[0..1],".");
+ GMCP_DEBUG("trying modname... ", modname, 20);
+ if (member(gmcpdata, modname)) {
+ mod = gmcpdata[modname];
+ funcall(mod->recvcl, cmd, args);
+ break;
+ }
+ // Fall-through!
+ case 2:
+ modname = implode(cmdparts[0..0],".");
+ GMCP_DEBUG("trying modname... ", modname, 20);
+ if (member(gmcpdata, modname)) {
+ mod = gmcpdata[modname];
+ funcall(mod->recvcl, cmd, args);
+ break;
+ }
+ // Hier ists jetzt nen Fehler.
+ GMCPERROR(sprintf("Unknown GMCP module for cmd %s",cmd));
+ break;
+ default:
+ // zuviele oder zuwenig . ;-)
+ GMCPERROR(sprintf("Illegal GMCP cmd %s with args %O",
+ cmd, args));
+ break;
+ }
+ // sbdata brauchen wir eigentlich nicht mehr.
+ opt->re_wishes->sbdata = 0;
+ break;
+ } // switch (action)
+}
+
+// wird von base.c nach Konnektierung gerufen.
+// Darf aber erst gerufen werden, wenn das Spielerobjekt fertig initialisiert
+// und eingelesen ist.
+protected void startup_telnet_negs()
+{
+ // evtl. war es ein reconnect, dann steht in gmcp noch alter kram drin. Der
+ // muss weg, koennte ja auch sein, dass der Client (jetzt) kein GMCP
+ // mehr
+ // will.
+ gmcpdata = 0;
+
+ // Hack besonderer Sorte: GMCP soll lokal eingeschaltet sein. Auf
+ // Clientseiten ist es laut Protokoll nicht vorgesehen, daher duerfen
+ // (sollten?) wir kein DO an den Client senden. Wir brauchen aber einen
+ // remote handler, um die Wuensche vom Client zu verarbeiten. Daher erstmal
+ // nur den local handler binden (und gleichzeitig negotiation anstossen) und
+ // dann direkt danach den remote handler auch binden (ohne erneute
+ // negotiation zu starten). Achja und wir nehmen die gleiche Funktion als
+ // Handler fuer remote und lokal.
+ bind_telneg_handler(TELOPT_GMCP, 0, #'_std_re_handler_gmcp, 1);
+ bind_telneg_handler(TELOPT_GMCP, #'_std_re_handler_gmcp,
+ #'_std_re_handler_gmcp, 0);
+}
+