| // 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); |
| } |
| |