View Issue Details

IDProjectCategoryView StatusLast Update
0000215LDMud 3.5Runtimepublic2009-10-06 01:55
ReporterlarsAssigned To 
PrioritynormalSeverityfeatureReproducibilityN/A
Status newResolutionopen 
Summary0000215: Output hook
DescriptionDate: Fri, 21 Jan 2000 14:47:12 +0100 (MEZ)
From: Holger Kremss <jur94ewp@studserv.uni-leipzig.de>
Short: Output-Hook
Type: Feature
State: New


Hi Lars!

Ich haette mal ne Bitte und die Frage, ob es moeglich waere, sowas
einzubauen. Und zwar haette ich gerne ne Applied-Funktion, oder besser
natuerlich nen Driver-Hook, der alles bekommt, was ein Spieler an text
gesendet bekommt. Also im grund so aehnlich, wie das catch_tell und
catch_msg machen, aber eben unmittelbar vor der Ausgabe an den Socket
und fuer ALLEN Text. Und zwar waere das ne Moeglichkeit, Umlaute zu
filtern, die bestimmte Leute auch heute noch nicht auf ihren Terminals
sehen koennen. Also der Hook wuerde zum Beispiel folgendes machen:

    (string)str=player->modify_output(str);

In modify_output wuerde man dann alle 'umlaute' und 'sz' umwandeln, so
dass der Spieler eben 'ae' und 'ue' statt den Umlauten bekommt. Ich hab
mich im WL immerwieder dafuer eingesetzt, Umlaute zugaenglich zu machen,
aber da einige leider immernoch Terminals haben, die damit nichts anfangen
koennen oder englische Consolen usw, wo sie die auch nichtmal tippen
koennen, waere das eine Moeglichkeit. Ich habe mit catch_tell() und
catch_msg() rumexperimentiert und festgestellt, dass da immer einiges
nicht ankommt. Und zwar all die tell_object() die im Spieler erzeugt
werden. Und das sind in unserer Lib sehr viele. Eine andere Moeglichkeit
gibt es leider nicht. Und die gesamte Lib umschreiben, dass man mit
catch_tell und so arbeiten kann, darauf hab ich ehrlich gesagt keine Lust
*stoehn*. Waer doch bestimmt simpel so einen Hook (analog modiy_command)
zu machen, oder? Und bestimmt auch nicht zu viel Aufwand...

Leider hab ich die aktuellsten Driver-Sourcen nicht hier, aber ich denke
mir, alle Textausgaben werden an einer einheitlichen Stelle gemacht. Wenn
es doch eine Moeglichkeit jetzt schon gibt, die mir entgangen ist, dann
waer ich ueber jeden Tip dankbar. :-)

Danke und Byebye!

Holger


Hi Lars.

Ich haette noch einen Vorschlag zu meiner letzten Mail. Ich hab mir
nochmal gedanken gemacht, wie das umlaute-parsen am besten zu realisieren
ist und bin drauf gekommen, dass es besser ist, die Eingabe des Spielers
umzuwandeln in Spezialzeichen und erst bei der Ausgabe das wieder
umzuwandeln. Das ist deshalb besser, weil der Umlaut ue nur ein zeichen
lang ist, wohingegen 'ue' 2 Zeichen lang ist und dann kommen unsere ganzen
zeilenumbrueche durcheinander. Also bei der Eingabe ue -> "u und dann bei
der Ausgabe dann Spieler-individuell in Umlaute oder eben ue ae oe
umwandeln. Dann werden die Umbrueche nur vielleicht kuerzer, nicht etwa
laenger und fuer die strlen-spezifischen Sachen muss ich mir dann auch
noch was einfallen lassen. ;-)

Lange Rede kurzer Sinn: Neben dem output-Hook waer noch ein Input-Hook
schoen. Also wo alles veraendert werden kann, was reinkommt. Nicht nur
Commands (modify_command()) sondern auch all das, was an input_to() geht
usw. Eben _alles_.

Also meine Bitte nochmal zusammengefasst. 2 Hooks, einer fuers
Input-Modifizieren und einer fuer Output-Modifizieren. :-) Wenn Du keine
Lust hast, das zu machen, muss ich mich vielleicht doch mal selbst
ranwagen, aber ich glaub das koennte dann dauern...

Holger@Wunderland


Subject: H_MODIFY_OUTPUT
From: Thomas Feldmeier <tf4@informatik.uni-ulm.de>
Date: Wed, 17 Mar 1999 17:26:50 +0100 (MET)
Type: Feature
State: Acknowledged.

H_MODIFY_OUTPUT

Optionaler Hook zum Modifizieren jeder Textausgabe,
bevor der Spieler sie sieht.
Kann eine unbound closure oder ein String sein.

Ist der Hook eine Closure, wird er aufgerufen als
  string <closure>(string output).

Ist der Hook ein String, wird eine Funktion mit diesem Namen
in dem Spieler aufgerufen:
  string <name>(string output).

this_player() enthaelt den Spieler, der die Textausgabe sieht.

Bevor ein Text an einen Spieler gesendet wird, wird der Hook
mit diesem Text als Paramter aufgerufen. Ist der Rueckgabewert
des Hooks ein String, so wird dieser String anstelle des Textes
an den Spieler gesendet.

--------------------------------------
Als Vorlage zum H_MODIFY_HOOK koennte man H_MODIFY_COMMAND nehmen
(aehnliche Parameteruebergabe)

Ich habe mir gedacht, dass in comm.c, add_message() geaendert wird:
bevor die message umgewandelt wird (also die /n zu /r/n geaendert usw.),
wird ein Hook aufgerufen, der die Message aendern koennte.
(vielleicht an der gleichen stelle, an der auch gesnoopt wird?)

Damit wird wohl jedes printf(), write() usw. auch einen eigenen Aufruf des
Hooks bewirken. Nur binary_message( ,0) nicht.

Der Hook ist ja gedacht, um alle ausgaben automatisch umzubrechen, bei
beruecksichtigung der ansi-steuerzeichen. pro write usw. wuerde also
zusaetzlich ein hook ausgefuehrt werden, der dann ein paar mapping-
operationen macht und ein aufruf von terminal_colour. ich schaetze, die
cpu-last waere noch ok.

                                                     Zora
TagsNo tags attached.
Attached Files
pkmud-outputhook.diff (7,381 bytes)   
Date: Tue, 08 Feb 2000 09:01:45 -0700
From: Zwirch
Subject: H_MODIFY_OUTPUT
Type: Feature
State: New


From:    Zwirch@PK-Mud
Cc:
Subject: H_MODIFY_OUTPUT-driverhook
Date:    Tue Feb  8 14:15:20 2000

Du kannst das Diff zu LDMud-dev164 im PK-Mud-Ftp ziehen (ftp pk.mud.de 4713,
anonymous login, Software/Driver/diff-dev164-outputhook).
Folgendes macht der Hook:
---------------------------------------------------------------------------
        H_MODIFY_OUTPUT
          Optional hook to modify every output that is send to an
          interactive user (except prompts or binary_message()).

          Hook setting can be an closure or the name of the function
          to call in the object.
=== More: (38%) Page 1, 1..16 [CR,u,f,l,q,/<regexp>,<page>,?]

          If the hook is a closure, it is called as

            string <closure>(string output, object player)

          with the output as first, and the receiving player as
          second argument.

          If the hook is a string, it is used as the name of an lfun
          in the command giving player, which is called as

            string <name>(string output)

          If the result is a string it is the new output, which will be
          sent to the player, otherwise the original output is sent.
----------------------------------------------------------------------
-----
Getestet hab ichs mit dem Hook als string und als unbound-lambda oder
lfun-closure im Master.
Ich hab auch mal Laufzeitfehler im Hook erzeugt bzw. vom Hook selber
Textausgaben gemacht (ergibt too-deep-recursion). Beides hat 
zumindest mal
nicht gecrasht.
                                                        Ciao, Zwirch
PS: ich schick die Diffs auch noch an lars@bearnip
----------diffs-------------
--- ldmud-164/src/comm.c	Sat Dec 18 01:43:46 1999
+++ ldmud-164-outputhook/src/comm.c	Tue Feb  8 12:42:57 2000
@@ -823,6 +823,61 @@
 }
 
 /*-------------------------------------------------------------------------*/
+
+char *
+call_modify_output(char *buff)
+/* Calls the H_MODIFY_OUTPUT-driverhook (if there is one).
+ * Returns an xalloced copy of the modified string or
+ * NULL if there was no hook.
+ * (The inputstring itself cannot be modified)
+ */
+{
+    svalue_t *svp;
+    char *output;
+    
+    svp = NULL;
+    output=NULL;
+
+    if (closure_hook[H_MODIFY_OUTPUT].type == T_CLOSURE)
+    {
+        lambda_t *l;
+
+        l = closure_hook[H_MODIFY_OUTPUT].u.lambda;
+        if (closure_hook[H_MODIFY_OUTPUT].x.closure_type == CLOSURE_LAMBDA)
+            l->ob = command_giver;
+        push_volatile_string(buff);
+        push_object(command_giver);
+        call_lambda(&closure_hook[H_MODIFY_OUTPUT], 2);
+        transfer_svalue(svp = &apply_return_value, inter_sp--);
+        if (!command_giver)
+            return NULL;
+    }    
+    else if (closure_hook[H_MODIFY_OUTPUT].type == T_STRING
+        && !(O_DESTRUCTED & command_giver->flags))
+    {
+        push_volatile_string(buff);
+        svp =
+          sapply(closure_hook[H_MODIFY_OUTPUT].u.string, command_giver, 1);
+        if (!command_giver)
+            return NULL;
+    }
+
+    /* If svp is not NULL, it contains the new, modified output.
+     */
+    if (svp && svp->type == T_STRING) 
+    {
+        output=xalloc(svalue_strlen(svp)+1);
+        if(!output)
+        {
+            error("Out of memory.\n");
+            /* NOTREACHED */
+            return NULL;
+        }
+        strcpy(output, svp->u.string);
+    } 
+    return output;
+} 
+
 void
 add_message (char *fmt, ...)
 
@@ -870,6 +925,7 @@
        * TODO: Actually, it is used just as a flag for flush/non-flush.
        */
     int   old_message_length;  /* accumulated message length so far */
+    char *tmp;
     char *source;              /* Pointer to the final message to add */
     char *end;                 /* One char past the end of .message_buf[] */
     char *dest;                /* First free char in .message_buf[] */
@@ -878,6 +934,7 @@
     object_t      *snooper;  /* Snooper of <ip> */
     int   n;
 
+    tmp=NULL;
     va_start(va, fmt);
 
     /* Test if the command_giver is a real, living, undestructed user,
@@ -950,7 +1007,6 @@
 
         if (!sending_telnet_command)
         {
-
             /* If there's a shadow successfully handling the
              * message, return.
              * This may cause a recursive call to add_message()!.
@@ -959,6 +1015,13 @@
             if (shadow_catch_message(command_giver, source))
                 return;
 
+            /* call the H_MODIFY_OUTPUT-hook
+             * If the hook modified the output we continue with
+             * the modified string
+             */
+            if(tmp=call_modify_output(source))
+                source=tmp;
+
             /* If there's a snooper, send it the new message prepended
              * with a '%'.
              * For interactive snoopers this means a recursion with
@@ -1118,6 +1181,7 @@
                   "%s comm: write EINTR. Message discarded.\n", time_stamp());
                 if (old_message_length)
                     remove_flush_entry(ip);
+                if(tmp) xfree(tmp);
                 return;
 
               case EWOULDBLOCK:
@@ -1125,10 +1189,12 @@
                   "%s comm: write EWOULDBLOCK. Message discarded.\n", time_stamp());
                 if (old_message_length)
                     remove_flush_entry(ip);
+                if(tmp) xfree(tmp);
                 return;
 
               case EMSGSIZE:
                 fprintf(stderr, "%s comm: write EMSGSIZE.\n", time_stamp());
+                if(tmp) xfree(tmp);
                 return;
 
               case EINVAL:
@@ -1158,6 +1224,7 @@
             if (old_message_length)
                 remove_flush_entry(ip);
             ip->do_close = FLAG_DO_CLOSE;
+            if(tmp) xfree(tmp);
             return;
 
         } /* for (retries) */
@@ -1177,7 +1244,8 @@
     } while (*source);
 
     /* --- Final touches --- */
-
+    if(tmp) xfree(tmp);
+  
     ip->message_length = length = dest - ip->message_buf;
 
     /* Update the ring of interactives with pending data */
--- ldmud-164/src/make_func.y	Mon Dec  6 18:41:06 1999
+++ ldmud-164-outputhook/src/make_func.y	Tue Feb  1 15:10:34 2000
@@ -1284,6 +1284,8 @@
         return H_MODIFY_COMMAND_FNAME;
     if ( !strcmp(name, "COMMAND") )
         return H_COMMAND;
+    if ( !strcmp(name, "MODIFY_OUTPUT") )
+        return H_MODIFY_OUTPUT;
     return -1;
 }
 
--- ldmud-164/src/prolang.y	Fri Dec 17 02:05:54 1999
+++ ldmud-164-outputhook/src/prolang.y	Tue Feb  1 14:40:42 2000
@@ -180,7 +180,7 @@
     H_ERQ_STOP:       SH(T_CLOSURE), \
     H_MODIFY_COMMAND_FNAME: SH(T_STRING), \
     H_COMMAND:        SH(T_CLOSURE) SH(T_STRING), \
-
+    H_MODIFY_OUTPUT:  SH(T_CLOSURE) SH(T_STRING), \
 #undef SH
 
 /*-------------------------------------------------------------------------*/
--- ldmud-164/mudlib/sys/driver_hook.h	Tue Sep 28 23:11:40 1999
+++ ldmud-164-outputhook/mudlib/sys/driver_hook.h	Tue Feb  1 14:39:25 2000
@@ -22,8 +22,9 @@
 #define H_ERQ_STOP              15
 #define H_MODIFY_COMMAND_FNAME  16
 #define H_COMMAND               17
+#define H_MODIFY_OUTPUT         18
 
-#define NUM_CLOSURE_HOOKS       18  /* Number of hooks */
+#define NUM_CLOSURE_HOOKS       19  /* Number of hooks */
 
 #endif /* _DRIVER_HOOK_ */
 

---------------------------
pkmud-outputhook.diff (7,381 bytes)   

Relationships

has duplicate 0000426 closed Generic text-reformatting functions for all input and output 
related to 0000289 new Input hook 

Activities

lars

2004-11-26 22:13

reporter   ~0000215

Zwirch implemented the hook for PKMud.

zesstra

2009-09-26 17:45

administrator   ~0001304

I thought, that catch_tell() receives every message sent to an interactive and only the interactive object itself can sent messages directly to its socket. So, isn't the output hook superfluous now?

Sorcerer

2009-09-27 12:48

updater   ~0001313

When this bug was created, tell_object() did not yet call catch_msg(). This was introduced with issue 354 one year later.
So probably this issue is resolved by now.

Gnomi

2009-09-29 11:00

manager   ~0001337

But on the other hand I think an interface via input and output hooks might be much cleaner than the call of catch_tell (and having a simul_efun for input_to to catch umlauts). Without an output hook the driver could still call catch_tell as a default.

Coogan

2009-09-29 15:24

reporter   ~0001353

In Tubmud, all input from resp. output to a player is sent through encoding functions to ensure all players are able to communicate with each other. This way it's also possible for player A to use UTF8, player B to use LATIN1 and C to use plain ascii, and umlauts and other characters are converted correctly.
This is of course combined with proper linebreaks even for coloured messages.

Shortly, it is possible to handle that real reason behind this issue completely by the mudlib.

Issue History

Date Modified Username Field Change
2004-11-26 22:09 lars New Issue
2004-11-26 22:13 lars File Added: pkmud-outputhook.diff
2004-11-26 22:13 lars Note Added: 0000215
2009-09-26 17:45 zesstra Note Added: 0001304
2009-09-27 12:48 Sorcerer Note Added: 0001313
2009-09-29 11:00 Gnomi Note Added: 0001337
2009-09-29 15:24 Coogan Note Added: 0001353
2009-10-05 16:48 zesstra Project LDMud => LDMud 3.5
2009-10-06 01:55 zesstra Relationship added has duplicate 0000426
2011-02-20 00:12 zesstra Relationship added related to 0000289