blob: 572eae6e2f7146640417c1f55a942d07503a035a [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2/** \file /file.c
3* merkt sich Syntaxen und deren Erfolg
4* Kommt gar nicht mit geschachtelten Befehlen klar...
5* \author Zesstra
6*/
7
8#pragma strong_types,save_types,rtt_checks
9#pragma no_clone,no_inherit,no_shadow
10#pragma pedantic, range_check
11
12#include <defines.h>
13#include <events.h>
14#include <wizlevels.h>
15#include <player/base.h>
16#include <userinfo.h>
17#include <driver_info.h>
18#include <regexp.h>
19
20#define HOME(x) (__PATH__(0)+x)
21#define ZDEBUG(x) tell_room("/players/zesstra/workroom",\
22 sprintf("syntax: %O\n",x));
23
24mapping blacklist;
25
26struct cmd_s {
27 string verb;
28 string cmd;
29 string uid;
30 int success;
31 int fp;
32 int evalno;
33 string tp_uid;
34 int finished;
35};
36
37// LIste der letzten Kommandos, Queue zum speichern in die DB.
38// Das letzte Element ist das letzte Kommando (ggf. noch nicht abgeschlossen)
39private struct cmd_s *commands = ({});
40
41protected void create()
42{
43 seteuid(getuid());
44 if (sl_open(HOME("ARCH/syntaxen.sqlite")) != 1)
45 {
46 raise_error("Datenbank konnte nicht geoeffnet werden.\n");
47 }
48 // Tabellen und Indices anlegen, falls die nicht existieren.
49 sl_exec("CREATE TABLE IF NOT EXISTS syntaxen("
50 "cmd TEXT UNIQUE, "
51 "verb TEXT NOT NULL, envuid TEXT NOT NULL, "
52 "success INTEGER, count INTEGER, fp INTEGER);");
53 sl_exec("CREATE INDEX IF NOT EXISTS idx_verb ON syntaxen(verb);");
54 // dieses Objekt darf nur per hand (mit this_player()) geladen werden, weil
55 // da ein Teil der Blacklist herkommt... Man beachte, dass dies auch das
56 // ist, was die ueblichen Kommunikationsbefehle ausfiltert.
57 string *tmp = map(this_player()->QueryProp(P_LOCALCMDS),
58 function string (mixed val)
59 {return val[0];}
60 );
61 blacklist = mkmapping(tmp
62 - ({ "toete","schnupper", "schnuppere", "suche","such",
63 "unt", "untersuche","untersuch", "riech",
64 "rieche", "lausch", "lausche", "taste",
65 "fuehl", "fuehle, beruehre", "schau", "schaue",
66 "les", "lies", "betrachte", "betr", "betracht",
67 "teile" // wird per regexp gefiltert!
68 })
69 + ({"mail","frage","frag", "xload", "xeval", "xcall",
70 "gruppe", "g", "team", "frufe", "fruf", "fwer",
71 "femote", "rknuddel", "knuddel", "nick", "nicke",
72 "rnick", "rnicke", "knuddel", "knuddele",
73 "rknuddel", "rknuddele","denke","denk",
74 "ffix","fnotiz","fuebertrage", "chaoskontrolle",
75 "kreis", // Matrixkristall
76 })
77 );
78}
79
80private void write_db()
81{
82 foreach(struct cmd_s c : commands)
83 {
84 int** row=sl_exec("SELECT rowid, success, count, fp from syntaxen "
85 "WHERE cmd=?1;",
86 c->cmd);
87 if (row)
88 {
89 sl_exec("UPDATE syntaxen SET success=?2, count=?3, fp=?4"
90 "WHERE rowid=?1;", row[0][0],
91 c->success || row[0][1],
92 ++row[0][2], c->fp || row[0][3] );
93 }
94 else
95 {
96 sl_exec("INSERT INTO syntaxen(verb, cmd, envuid, success, count, fp) "
97 "VALUES(?1,?2,?3,?4,?5,?6);",
98 c->verb, c->cmd, c->uid, c->success, 1, c->fp);
99 }
100 }
101 commands = ({});
102}
103
104// Beendet ein Kommando, wird nur intern gerufen
105private void commit(struct cmd_s c)
106{
107 c->finished = 1;
108 //printf("Kommando abgeschlossen: %O\n", c);
109 if (get_eval_cost() > 1000000
110 && sizeof(commands) > 5
111 && find_call_out(#'write_db) == -1)
112 {
113 // In DB wegschreiben
114 call_out(#'write_db, 1);
115 }
116}
117
118public varargs int remove(int silent)
119{
120 write_db();
121 destruct(this_object());
122 return 1;
123}
124
125// gerufen, wenn ein Spieler ein Kommando eingetippt hat, aus modify_command()
126// heraus. Nach Parsen und Kommandoblock, aber vor allem anderen.
127// ACHTUNG: wenn ein Kommando erfolgreich ist, wird KEIN end_cmd() von aussen
128// gerufen...
129public void start_cmd(string cmdstr)
130{
131 if (!interactive(previous_object()))
132 return;
133 cmdstr = lower_case(cmdstr);
134 // letztes Kommando notfalls abschliessen
135 struct cmd_s cmd;
136 if (sizeof(commands))
137 {
138 cmd = commands[<1];
139 if (!cmd->finished)
140 {
141 // wir betrachten es uebrigens als erfolgreich!
142 commit(cmd);
143 }
144 }
145 // teile- mit und Ebenen und alle Kommandos, die nur aus verb und einem Wort
146 // bestehen, sind auch unnuetz.
147 //printf("%O\n",cmdstr);
148 if (sizeof(explode(cmdstr, " ")) < 3
149 || sizeof(cmdstr) > 100
150 || regmatch(cmdstr,"^teile.*mit",RE_PCRE)
151 || regmatch(cmdstr,"^-[[:alpha:]-]*[' :]{1}",RE_PCRE)
152 || regmatch(cmdstr,"^[.']", RE_PCRE)
153 )
154 {
155 return;
156 }
157 cmd = (<cmd_s> verb: explode(cmdstr, " ")[0],
158 cmd: cmdstr,
159 uid: getuid(environment(this_player())),
160 evalno: driver_info(DI_EVAL_NUMBER),
161 success: 1,
162 tp_uid: getuid(previous_object()),
163 );
164 // sonstige Blacklist
165 if (member(blacklist, cmd->verb))
166 return;
167
168 commands += ({cmd});
169 //printf("Kommandostart: %O\n", cmd);
170}
171
172// gerufen von _auswerten() im Spielerobjekt - das kommt ziemlich am Ende der
173// Kommandoverarbeitung - wenn es noch gerufen wird und uns ruft, betrachten
174// wir das Kommando als NICHT erfolgreich.
175public void cmd_unsuccessful()
176{
177 if (!sizeof(commands))
178 return;
179 struct cmd_s cmd = commands[<1];
180 // Darf nur vom Spielerobjekt gerufen werden, welches das letzte Kommando
181 // angefangen hat.
182 if (cmd->tp_uid != getuid(previous_object()))
183 return;
184 // und dies gehoert nur zum letzten Kommando, wenn Verb und Eval-No
185 // uebereinstimmen.
186 if (cmd->verb == query_verb()
187 || cmd->evalno == driver_info(DI_EVAL_NUMBER))
188 {
189 cmd->success = 0;
190 //printf("Kommando nicht erfolgreich: %O\n", cmd);
191 commit(cmd);
192 }
193 // Wenn nicht, war das letzte Kommando offenbar doch erfolgreich. Aus
194 // irgendnem Grund haben wir aber den Start des neuen Kommandos verpasst.
195 // Daher verwerfen wir das jetzt. Das alte Kommando war aber offenbar
196 // erfolgreich, daher wird es jetzt abgeschlossen.
197 else
198 {
199 commit(cmd);
200 }
201}
202
203public void LogEP(int type)
204{
205 if (!sizeof(commands))
206 return;
207 struct cmd_s cmd = commands[<1];
208 // Darf nur vom Spielerobjekt gerufen werden, welches das letzte Kommando
209 // angefangen hat.
210 if (cmd->tp_uid != getuid(previous_object()))
211 return;
212 //printf("FP gefunden: %O\n", cmd);
213 // und dies gehoert nur zum letzten Kommando, wenn Verb und Eval-No
214 // uebereinstimmen.
215 if (cmd->verb == query_verb()
216 || cmd->evalno == driver_info(DI_EVAL_NUMBER))
217 {
218 cmd->fp = type+1;
219 // Und wenn es nen FP gab, ist das Kommando auch erfolgreich.
220 commit(cmd);
221 }
222}
223