blob: 2d95d2cc206c263e3ed9153522d737e155ab1442 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// channeld.c
2//
3// $Id: channeld.c 9138 2015-02-03 21:46:56Z Zesstra $
4//
5
6#pragma strong_types
7#pragma no_shadow // keine Shadowing...
8#pragma no_clone
9#pragma no_inherit
10#pragma save_types
11
12#include <sys_debug.h>
13#include <lpctypes.h>
14#include <wizlevels.h>
Arathorn78c08372019-12-11 20:14:23 +010015#include <regexp.h>
MG Mud User88f12472016-06-24 23:31:02 +020016
17#include <properties.h>
18#include <config.h>
19#include <language.h>
20
21#define NEED_PROTOTYPES
22#include "channel.h"
23
Arathorn78c08372019-12-11 20:14:23 +010024#define CHANNEL_SAVE "/p/daemon/save/channeld"
25#define MEMORY "/secure/memory"
26#define MAX_HIST_SIZE 200
27#define MAX_CHANNELS 90
Zesstra6ddbacb2020-08-13 21:43:28 +020028// Default-Zeit (in Sekunden), die eine Ebene und ihr History inaktiv sein darf,
29// bevor sie expired wird. Das reduziert sich jedoch, falls es zuviele
30// inaktive Ebenen gibt. Entspricht 30 Tagen.
31#define INACTIVE_EXPIRE 2592000
32// Aber eine Ebene darf min. solange inaktiv sein, bevor sie geloescht wird
33#define MIN_INACTIVE_LIFETIME 3600
34// max. Anzahl der inaktiven Ebenen. Wenn die Haelfte davon ueberschritten
35// wird, wird mit zunehmend kleinerem CHANNEL_EXPIRE gearbeitet.
36#define MAX_INACTIVE_CHANNELS 500
Arathorn78c08372019-12-11 20:14:23 +010037#define CMDS ({C_FIND, C_LIST, C_JOIN, C_LEAVE, C_SEND, C_NEW})
MG Mud User88f12472016-06-24 23:31:02 +020038
Zesstra0caa8e42020-08-11 22:51:59 +020039// Standard-Ebenen-Supervisor erben, Variablen nosave, die sollen hier nicht
40// gespeichert werden.
41nosave variables inherit "/std/channel_supervisor";
Arathorn78c08372019-12-11 20:14:23 +010042
Zesstrab7720dc2020-08-11 22:14:18 +020043// Datenstrukturen fuer die Ebenen.
44// Basisdaten, welche auch inaktive Ebenen in channelC haben
45struct channel_base_s {
46 string name; // readable channelname, case-sensitive
47 string|closure desc; // stat. oder dyn. Beschreibung
Zesstrad9ec04b2020-08-11 23:47:03 +020048 string creator; // Ersteller der Ebene (Objektname), Original-SV
49 int flags; // Flags, die bestimmtes Verhalten steuern.
Zesstrab7720dc2020-08-11 22:14:18 +020050};
51
52// Basisdaten + die von aktiven Ebenen
53struct channel_s (channel_base_s) {
Zesstra9359fab2020-08-13 12:03:01 +020054 object supervisor; // aktueller Supervisor der Ebene
55 closure access_cl; // Closure fuer Zugriffsrechtepruefung
56 object *members; // Zuhoerer der Ebene
Zesstrab7720dc2020-08-11 22:14:18 +020057};
58
59/* Ebenenliste und die zugehoerigen Daten in struct (<channel>).
60 channels = ([string channelname : (<channel_s>) ])
Zesstra78310012020-08-09 12:21:48 +020061 */
Arathorn78c08372019-12-11 20:14:23 +010062private nosave mapping channels = ([]);
Arathorn19459eb2019-11-30 00:45:51 +010063
Arathorn78c08372019-12-11 20:14:23 +010064/* Ebenenhistory
65 mapping channelH = ([ string channelname : ({ ({string channelname,
66 string sender,
67 string msg,
68 int msg_type}) }) ]) */
69// channelH wird in create() geeignet initialisiert
70// HINWEIS: Bitte beachten, dass channelH immer nur so manipuliert werden
71// darf, dass keine Kopie erstellt wird, weder direkt noch implizit. Die
72// History wird via Referenz in /secure/memory hinterlegt, damit sie einen
73// Reload des Channeld ueberlebt. Das funktioniert aber nur, wenn die Mapping-
74// Referenz in Memory und Channeld dieselbe ist.
MG Mud User88f12472016-06-24 23:31:02 +020075private nosave mapping channelH;
Arathorn19459eb2019-11-30 00:45:51 +010076
Arathorn78c08372019-12-11 20:14:23 +010077/* Globale channeld-Stats (Startzeit, geladen von, Anzahl erstellte und
78 zerstoerte Ebenen.
79 mapping stats = ([ "time" : int object_time(),
80 "boot" : string getuid(previous_object()),
Arathorn19459eb2019-11-30 00:45:51 +010081 "new" : int total_channels_created,
82 "disposed" : int total_channels_removed ]) */
Arathorn78c08372019-12-11 20:14:23 +010083// stats wird in create() geeignet initialisiert
MG Mud User88f12472016-06-24 23:31:02 +020084private nosave mapping stats;
85
Arathorn78c08372019-12-11 20:14:23 +010086/* Ebenen-Cache, enthaelt Daten zu inaktiven Ebenen.
Zesstrab7720dc2020-08-11 22:14:18 +020087 mapping channelC = ([ string channelname : (<channel_base_s>);
88 int time() ])
89 Der Zeitstempel ist die letzte Aenderung, d.h. in der Regel des Ablegens in
90 channelC.
91 */
92private mapping channelC = ([:2]);
Arathorn19459eb2019-11-30 00:45:51 +010093
Arathorn78c08372019-12-11 20:14:23 +010094/* Liste von Spielern, fuer die ein Bann besteht, mit den verbotenen Kommandos
95 mapping channelB = ([ string playername : string* banned_command ]) */
96private mapping channelB = ([]);
MG Mud User88f12472016-06-24 23:31:02 +020097
Arathorn78c08372019-12-11 20:14:23 +010098/* Timeout-Liste der Datenabfrage-Kommandos; die Timestamps werden verwendet,
99 um sicherzustellen, dass jedes Kommando max. 1x pro Minute benutzt werden
100 kann.
101
Arathorn19459eb2019-11-30 00:45:51 +0100102 mapping Tcmd = ([ "lag": int timestamp,
103 "uptime": int timestamp,
104 "statistik": int timestamp]) */
105private mapping Tcmd = ([]);
106
Arathorn78c08372019-12-11 20:14:23 +0100107/* Flag, das anzeigt, dass Daten veraendert wurden und beim naechsten
108 Speicherevent das Savefile geschrieben werden soll.
109 Wird auf 0 oder 1 gesetzt. */
Zesstraa2db5522020-08-11 22:14:55 +0200110private nosave int save_me_soon;
MG Mud User88f12472016-06-24 23:31:02 +0200111
112// BEGIN OF THE CHANNEL MASTER ADMINISTRATIVE PART
113
Arathorn78c08372019-12-11 20:14:23 +0100114/* CountUsers() zaehlt die Anzahl Abonnenten aller Ebenen. */
115// TODO: Mapping- und Arrayvarianten bzgl. der Effizienz vergleichen
116private int CountUsers()
MG Mud User88f12472016-06-24 23:31:02 +0200117{
Arathorn78c08372019-12-11 20:14:23 +0100118 object* userlist = ({});
Zesstrab7720dc2020-08-11 22:14:18 +0200119 foreach(string ch_name, struct channel_s ch : channels)
Arathorn78c08372019-12-11 20:14:23 +0100120 {
Zesstrab7720dc2020-08-11 22:14:18 +0200121 userlist += ch.members;
Arathorn78c08372019-12-11 20:14:23 +0100122 }
123 // Das Mapping dient dazu, dass jeder Eintrag nur einmal vorkommt.
124 return sizeof(mkmapping(userlist));
MG Mud User88f12472016-06-24 23:31:02 +0200125}
126
Arathorn78c08372019-12-11 20:14:23 +0100127// Ist das Objekt <sender> Abonnent der Ebene <ch>?
Zesstrab7720dc2020-08-11 22:14:18 +0200128private int IsChannelMember(struct channel_s ch, object sender)
MG Mud User88f12472016-06-24 23:31:02 +0200129{
Zesstrab7720dc2020-08-11 22:14:18 +0200130 return (member(ch.members, sender) != -1);
MG Mud User88f12472016-06-24 23:31:02 +0200131}
132
Arathorn78c08372019-12-11 20:14:23 +0100133// Besteht fuer das Objekt <ob> ein Bann fuer die Ebenenfunktion <command>?
134private int IsBanned(string|object ob, string command)
MG Mud User88f12472016-06-24 23:31:02 +0200135{
Arathorn78c08372019-12-11 20:14:23 +0100136 if (objectp(ob))
137 ob = getuid(ob);
138 return(pointerp(channelB[ob]) &&
139 member(channelB[ob], command) != -1);
140}
MG Mud User88f12472016-06-24 23:31:02 +0200141
Arathorn78c08372019-12-11 20:14:23 +0100142private void banned(string plname, string* cmds, string res)
143{
144 res += sprintf("%s [%s], ", capitalize(plname), implode(cmds, ","));
145}
146
147#define TIMEOUT (time() - 60)
148
149// IsNotBlocked(): prueft fuer die Liste der uebergebenen Kommandos, ob
150// die Zeitsperre fuer alle abgelaufen ist und sie ausgefuehrt werden duerfen.
151// Dabei gilt jedes Kommando, dessen letzte Nutzung laenger als 60 s
152// zurueckliegt, als "nicht gesperrt".
153private int IsNotBlocked(string* cmd)
154{
155 string* res = filter(cmd, function int (string str) {
156 return (Tcmd[str] < TIMEOUT);
157 });
158 // Wenn das Ergebnis-Array genauso gross ist wie das Eingabe-Array, dann
159 // sind alle Kommandos frei. Sie werden direkt gesperrt; return 1
160 // signalisiert dem Aufrufer, dass das Kommando ausgefuehrt werden darf.
161 if (sizeof(res) == sizeof(cmd)) {
162 foreach(string str : cmd) {
163 Tcmd[str] = time();
164 }
165 return 1;
166 }
167 return 0;
168}
169
170// Prueft, ob der gesendete Befehl <cmd> als gueltiges Kommando <check>
171// zugelassen wird. Anforderungen:
172// 1) <cmd> muss Teilstring von <check> sein
173// 2) <cmd> muss am Anfang von <check> stehen
174// 3) <cmd> darf nicht laenger sein als <check>
175// 4) die Nutzung von <cmd> darf nur einmal pro Minute erfolgen
176// Beispiel: check = "statistik", cmd = "stat" ist gueltig, nicht aber
177// cmd = "statistiker" oder cmd = "tist"
178// Wenn die Syntax zugelassen wird, wird anschliessend geprueft
179private int IsValidChannelCommand(string cmd, string check) {
180 // Syntaxcheck (prueft Bedingungen 1 bis 3).
181 if ( strstr(check, cmd)==0 && sizeof(cmd) <= sizeof(check) ) {
182 string* cmd_to_check;
183 // Beim Kombi-Kommando "lust" muessen alle 3 Befehle gecheckt werden.
184 // Der Einfachheit halber werden auch Einzelkommandos als Array ueber-
185 // geben.
186 if ( cmd == "lust" )
187 cmd_to_check = ({"lag", "statistik", "uptime"});
188 else
189 cmd_to_check = ({cmd});
190 // Prueft die Zeitsperre (Bedingung 4).
191 return (IsNotBlocked(cmd_to_check));
192 }
193 return 0;
194}
195
196#define CH_NAME 0
197#define CH_SENDER 1
198#define CH_MSG 2
199#define CH_MSG_TYPE 3
200// Gibt die Channelmeldungen fuer die Kommandos up, stat, lag und bann des
201// <MasteR>-Channels aus. Auszugebende Informationen werden in <ret> gesammelt
202// und dieses per Callout an send() uebergeben.
Zesstrab7720dc2020-08-11 22:14:18 +0200203// Argument: ({channel.name, object pl, string msg, int type})
Arathorn78c08372019-12-11 20:14:23 +0100204// Funktion muss public sein, auch wenn der erste Check im Code das Gegenteil
205// nahezulegen scheint, weil sie von send() per call_other() gerufen wird,
206// was aber bei einer private oder protected Funktion nicht moeglich waere.
207public void ChannelMessage(<string|object|int>* msg)
208{
209 // Wir reagieren nur auf Meldungen, die wir uns selbst geschickt haben,
210 // aber nur dann, wenn sie auf der Ebene <MasteR> eingegangen sind.
211 if (msg[CH_SENDER] == this_object() || !stringp(msg[CH_MSG]) ||
212 msg[CH_NAME] != CMNAME || previous_object() != this_object())
Arathorn19459eb2019-11-30 00:45:51 +0100213 return;
MG Mud User88f12472016-06-24 23:31:02 +0200214
Arathorn78c08372019-12-11 20:14:23 +0100215 float* lag;
216 int max, rekord;
217 string ret;
Arathorn739a4fa2020-08-06 21:52:58 +0200218 string mesg = msg[CH_MSG];
MG Mud User88f12472016-06-24 23:31:02 +0200219
Arathorn78c08372019-12-11 20:14:23 +0100220 if (IsValidChannelCommand(mesg, "hilfe"))
MG Mud User88f12472016-06-24 23:31:02 +0200221 {
Arathorn78c08372019-12-11 20:14:23 +0100222 ret = "Folgende Kommandos gibt es: hilfe, lag, uptime, statistik, lust, "
223 "bann. Die Kommandos koennen abgekuerzt werden.";
Arathorn19459eb2019-11-30 00:45:51 +0100224 }
Arathorn78c08372019-12-11 20:14:23 +0100225 else if (IsValidChannelCommand(mesg, "lag"))
Arathorn19459eb2019-11-30 00:45:51 +0100226 {
MG Mud User88f12472016-06-24 23:31:02 +0200227 lag = "/p/daemon/lag-o-daemon"->read_ext_lag_data();
228 ret = sprintf("Lag: %.1f%%/60, %.1f%%/15, %.1f%%/5, %.1f%%/1, "
Arathorn19459eb2019-11-30 00:45:51 +0100229 "%.1f%%/20s, %.1f%%/2s",
230 lag[5], lag[4], lag[3], lag[2], lag[1], lag[0]);
Arathorn78c08372019-12-11 20:14:23 +0100231 // Erster Callout wird hier schon abgesetzt, um sicherzustellen, dass
232 // die Meldung in zwei Zeilen auf der Ebene erscheint.
Arathorn19459eb2019-11-30 00:45:51 +0100233 call_out(#'send, 2, CMNAME, this_object(), ret);
MG Mud User88f12472016-06-24 23:31:02 +0200234 ret = query_load_average();
Arathorn19459eb2019-11-30 00:45:51 +0100235 }
Arathorn78c08372019-12-11 20:14:23 +0100236 else if (IsValidChannelCommand(mesg, "uptime"))
MG Mud User88f12472016-06-24 23:31:02 +0200237 {
Arathorn78c08372019-12-11 20:14:23 +0100238 if (file_size("/etc/maxusers") > 0 && file_size("/etc/maxusers.ever"))
Arathorn19459eb2019-11-30 00:45:51 +0100239 {
Arathorn78c08372019-12-11 20:14:23 +0100240 string unused;
241 sscanf(read_file("/etc/maxusers"), "%d %s", max, unused);
242 sscanf(read_file("/etc/maxusers.ever"), "%d %s", rekord, unused);
243 ret = sprintf("Das MUD laeuft jetzt %s. Es sind momentan %d Spieler "
244 "eingeloggt; das Maximum lag heute bei %d und der Rekord "
245 "bisher ist %d.", uptime(), sizeof(users()), max, rekord);
Arathorn19459eb2019-11-30 00:45:51 +0100246 }
247 else
248 {
Arathorn78c08372019-12-11 20:14:23 +0100249 ret = "Diese Information liegt nicht vor.";
MG Mud User88f12472016-06-24 23:31:02 +0200250 }
Arathorn19459eb2019-11-30 00:45:51 +0100251 }
Arathorn78c08372019-12-11 20:14:23 +0100252 else if (IsValidChannelCommand(mesg, "statistik"))
MG Mud User88f12472016-06-24 23:31:02 +0200253 {
MG Mud User88f12472016-06-24 23:31:02 +0200254 ret = sprintf(
Arathorn78c08372019-12-11 20:14:23 +0100255 "Im Moment sind insgesamt %d Ebenen mit %d Teilnehmern aktiv. "
256 "Der %s wurde das letzte mal am %s von %s neu gestartet. "
257 "Seitdem wurden %d Ebenen neu erzeugt und %d zerstoert.",
258 sizeof(channels), CountUsers(), CMNAME,
Arathorn19459eb2019-11-30 00:45:51 +0100259 dtime(stats["time"]), stats["boot"], stats["new"], stats["dispose"]);
260 }
Arathorn78c08372019-12-11 20:14:23 +0100261 // Ebenenaktion beginnt mit "bann"?
262 else if (strstr(mesg, "bann")==0)
MG Mud User88f12472016-06-24 23:31:02 +0200263 {
264 string pl, cmd;
Arathorn19459eb2019-11-30 00:45:51 +0100265
266 if (mesg == "bann")
267 {
268 if (sizeof(channelB))
MG Mud User88f12472016-06-24 23:31:02 +0200269 {
Arathorn78c08372019-12-11 20:14:23 +0100270 ret = "Fuer folgende Spieler besteht ein Bann: ";
271 // Zwischenspeicher fuer die Einzeleintraege, um diese spaeter mit
272 // CountUp() in eine saubere Aufzaehlung umwandeln zu koennen.
273 string* banlist = ({});
274 foreach(string plname, string* banned_cmds : channelB) {
275 banlist += ({ sprintf("%s [%s]",
276 capitalize(plname), implode(banned_cmds, ", "))});
277 }
278 ret = CountUp(banlist);
MG Mud User88f12472016-06-24 23:31:02 +0200279 }
280 else
281 {
Arathorn19459eb2019-11-30 00:45:51 +0100282 ret = "Zur Zeit ist kein Bann aktiv.";
283 }
284 }
285 else
286 {
Arathorn78c08372019-12-11 20:14:23 +0100287 if (sscanf(mesg, "bann %s %s", pl, cmd) == 2 &&
288 IS_DEPUTY(msg[CH_SENDER]))
Arathorn19459eb2019-11-30 00:45:51 +0100289 {
290 pl = lower_case(pl);
291 cmd = lower_case(cmd);
292
293 if (member(CMDS, cmd) != -1)
294 {
Arathorn78c08372019-12-11 20:14:23 +0100295 // Kein Eintrag fuer <pl> in der Bannliste vorhanden, dann anlegen;
296 // ist der Eintrag kein Array, ist ohnehin was faul, dann wird
297 // ueberschrieben.
Arathorn19459eb2019-11-30 00:45:51 +0100298 if (!pointerp(channelB[pl]))
Arathorn78c08372019-12-11 20:14:23 +0100299 m_add(channelB, pl, ({}));
Arathorn19459eb2019-11-30 00:45:51 +0100300
Arathorn78c08372019-12-11 20:14:23 +0100301 if (IsBanned(pl, cmd))
Arathorn19459eb2019-11-30 00:45:51 +0100302 channelB[pl] -= ({ cmd });
303 else
304 channelB[pl] += ({ cmd });
Arathorn19459eb2019-11-30 00:45:51 +0100305
Arathorn78c08372019-12-11 20:14:23 +0100306 ret = "Fuer '" + capitalize(pl) + "' besteht " +
307 (sizeof(channelB[pl])
Zesstraf5f10122020-08-13 21:47:33 +0200308 ? "folgender Bann: " + CountUp(channelB[pl]) + "."
Arathorn78c08372019-12-11 20:14:23 +0100309 : "kein Bann mehr.");
310
311 // Liste der gebannten Kommandos leer? Dann <pl> komplett austragen.
Arathorn19459eb2019-11-30 00:45:51 +0100312 if (!sizeof(channelB[pl]))
Arathorn78c08372019-12-11 20:14:23 +0100313 m_delete(channelB, pl);
Arathorn19459eb2019-11-30 00:45:51 +0100314
Zesstra18f2ad62020-08-13 21:45:57 +0200315 save_me_soon = 1;
Arathorn19459eb2019-11-30 00:45:51 +0100316 }
317 else
318 {
319 ret = "Das Kommando '" + cmd + "' ist unbekannt. "
Arathorn78c08372019-12-11 20:14:23 +0100320 "Erlaubte Kommandos: "+ CountUp(CMDS);
Arathorn19459eb2019-11-30 00:45:51 +0100321 }
322 }
323 else
324 {
Arathorn78c08372019-12-11 20:14:23 +0100325 if (IS_ARCH(msg[CH_SENDER]))
Arathorn19459eb2019-11-30 00:45:51 +0100326 ret = "Syntax: bann <name> <kommando>";
MG Mud User88f12472016-06-24 23:31:02 +0200327 }
328 }
329 }
Arathorn78c08372019-12-11 20:14:23 +0100330 else if (IsValidChannelCommand(mesg, "lust"))
MG Mud User88f12472016-06-24 23:31:02 +0200331 {
MG Mud User88f12472016-06-24 23:31:02 +0200332 lag = "/p/daemon/lag-o-daemon"->read_lag_data();
Arathorn78c08372019-12-11 20:14:23 +0100333 if (file_size("/etc/maxusers") > 0 && file_size("/etc/maxusers.ever"))
334 {
335 string unused;
336 sscanf(read_file("/etc/maxusers"), "%d %s", max, unused);
337 sscanf(read_file("/etc/maxusers.ever"), "%d %s", rekord, unused);
338 }
MG Mud User88f12472016-06-24 23:31:02 +0200339
Arathorn78c08372019-12-11 20:14:23 +0100340 int t = time() - last_reboot_time();
341
342 // TODO: fuer solche Anwendungen ein separates Inheritfile bauen, da
343 // die Funktionalitaet oefter benoetigt wird als nur hier.
344 string up = "";
Arathorn19459eb2019-11-30 00:45:51 +0100345 if (t >= 86400)
346 up += sprintf("%dT", t / 86400);
MG Mud User88f12472016-06-24 23:31:02 +0200347
Arathorn78c08372019-12-11 20:14:23 +0100348 t %= 86400;
Arathorn19459eb2019-11-30 00:45:51 +0100349 if (t >= 3600)
Arathorn78c08372019-12-11 20:14:23 +0100350 up += sprintf("%dh", t / 3600);
Arathorn19459eb2019-11-30 00:45:51 +0100351
Arathorn78c08372019-12-11 20:14:23 +0100352 t %= 3600;
Arathorn19459eb2019-11-30 00:45:51 +0100353 if (t > 60)
Arathorn78c08372019-12-11 20:14:23 +0100354 up += sprintf("%dm", t / 60);
Arathorn19459eb2019-11-30 00:45:51 +0100355
356 up += sprintf("%ds", t % 60);
Arathorn78c08372019-12-11 20:14:23 +0100357
MG Mud User88f12472016-06-24 23:31:02 +0200358 ret = sprintf("%.1f%%/15 %.1f%%/1 %s %d:%d:%d E:%d T:%d",
Arathorn19459eb2019-11-30 00:45:51 +0100359 lag[1], lag[2], up, sizeof(users()), max, rekord,
Arathorn78c08372019-12-11 20:14:23 +0100360 sizeof(channels), CountUsers());
Arathorn19459eb2019-11-30 00:45:51 +0100361 }
362 else
363 {
364 return;
365 }
MG Mud User88f12472016-06-24 23:31:02 +0200366
Arathorn78c08372019-12-11 20:14:23 +0100367 // Nur die Ausgabe starten, wenn ein Ausgabestring vorliegt. Es kann
368 // vorkommen, dass weiter oben keiner zugewiesen wird, weil die Bedingungen
369 // nicht erfuellt sind.
370 if (stringp(ret) && sizeof(ret))
371 call_out(#'send, 2, CMNAME, this_object(), ret);
MG Mud User88f12472016-06-24 23:31:02 +0200372}
373
374// setup() -- set up a channel and register it
375// arguments are stored in the following order:
Arathorn78c08372019-12-11 20:14:23 +0100376// string* chinfo = ({ channel_name, receive_level, send_level,
Zesstrad9ec04b2020-08-11 23:47:03 +0200377// adminflags, channelflags, description,supervisor })
Arathorn78c08372019-12-11 20:14:23 +0100378private void setup(string* chinfo)
MG Mud User88f12472016-06-24 23:31:02 +0200379{
Arathorn78c08372019-12-11 20:14:23 +0100380 string desc = "- Keine Beschreibung -";
Zesstrae19391f2020-08-09 13:40:12 +0200381 object supervisor = this_object();
Zesstra0caa8e42020-08-11 22:51:59 +0200382 int sv_recv, sv_send, sv_flags; // an den Supervisor weiterreichen
Zesstrad9ec04b2020-08-11 23:47:03 +0200383 int chflags;
Arathorn19459eb2019-11-30 00:45:51 +0100384
Arathorn78c08372019-12-11 20:14:23 +0100385 if (sizeof(chinfo) && sizeof(chinfo[0]) > 1 && chinfo[0][0] == '\\')
386 chinfo[0] = chinfo[0][1..];
MG Mud User88f12472016-06-24 23:31:02 +0200387
Arathorn78c08372019-12-11 20:14:23 +0100388 switch (sizeof(chinfo))
MG Mud User88f12472016-06-24 23:31:02 +0200389 {
Arathorn78c08372019-12-11 20:14:23 +0100390 // Alle Fallthroughs in dem switch() sind Absicht.
Zesstra0caa8e42020-08-11 22:51:59 +0200391 default:
Zesstrad9ec04b2020-08-11 23:47:03 +0200392 if (stringp(chinfo[6]) && sizeof(chinfo[6]))
393 catch(supervisor = load_object(chinfo[6]); publish);
Zesstrae19391f2020-08-09 13:40:12 +0200394 if (!objectp(supervisor))
395 supervisor = this_object();
Zesstrad9ec04b2020-08-11 23:47:03 +0200396 case 6:
397 if (stringp(chinfo[5]))
398 desc = chinfo[5];
Arathorn19459eb2019-11-30 00:45:51 +0100399 case 5:
Zesstrad9ec04b2020-08-11 23:47:03 +0200400 chflags = to_int(chinfo[4]);
Arathorn19459eb2019-11-30 00:45:51 +0100401 case 4:
Zesstra0caa8e42020-08-11 22:51:59 +0200402 sv_flags = to_int(chinfo[3]);
Arathorn19459eb2019-11-30 00:45:51 +0100403 case 3:
Zesstra0caa8e42020-08-11 22:51:59 +0200404 sv_send = to_int(chinfo[2]);
Arathorn19459eb2019-11-30 00:45:51 +0100405 case 2:
Zesstra0caa8e42020-08-11 22:51:59 +0200406 sv_recv = to_int(chinfo[1]);
Arathorn19459eb2019-11-30 00:45:51 +0100407 break;
408
409 case 0:
Zesstra0caa8e42020-08-11 22:51:59 +0200410 case 1:
Arathorn19459eb2019-11-30 00:45:51 +0100411 return;
MG Mud User88f12472016-06-24 23:31:02 +0200412 }
Zesstra0caa8e42020-08-11 22:51:59 +0200413 // Zugriffsrechte im channel_supervisor konfigurieren. (Kann auch dieses
414 // Objekt selber sein...)
415 supervisor->ch_supervisor_setup(lower_case(chinfo[0]), sv_recv,
416 sv_send, sv_flags);
Arathorn19459eb2019-11-30 00:45:51 +0100417
Zesstrad9ec04b2020-08-11 23:47:03 +0200418 if (new(chinfo[0], supervisor, desc, chflags) == E_ACCESS_DENIED)
MG Mud User88f12472016-06-24 23:31:02 +0200419 {
Arathorn78c08372019-12-11 20:14:23 +0100420 log_file("CHANNEL", sprintf("[%s] %s: %O: error, access denied\n",
Zesstrae19391f2020-08-09 13:40:12 +0200421 dtime(time()), chinfo[0], supervisor));
MG Mud User88f12472016-06-24 23:31:02 +0200422 }
423 return;
424}
425
Arathorn78c08372019-12-11 20:14:23 +0100426private void initialize()
MG Mud User88f12472016-06-24 23:31:02 +0200427{
Arathorn78c08372019-12-11 20:14:23 +0100428 string ch_list;
Zesstra@Morgengrauen3b569c82016-07-18 20:22:08 +0200429#if !defined(__TESTMUD__) && MUDNAME=="MorgenGrauen"
Arathorn78c08372019-12-11 20:14:23 +0100430 ch_list = read_file(object_name(this_object()) + ".init");
Zesstra@Morgengrauen3b569c82016-07-18 20:22:08 +0200431#else
Arathorn78c08372019-12-11 20:14:23 +0100432 ch_list = read_file(object_name(this_object()) + ".init.testmud");
Zesstra@Morgengrauen3b569c82016-07-18 20:22:08 +0200433#endif
Arathorn19459eb2019-11-30 00:45:51 +0100434
Arathorn78c08372019-12-11 20:14:23 +0100435 if (!stringp(ch_list))
Zesstra@Morgengrauen2b229372016-07-20 23:59:54 +0200436 return;
Arathorn19459eb2019-11-30 00:45:51 +0100437
Arathorn78c08372019-12-11 20:14:23 +0100438 // Channeldatensaetze erzeugen, dazu zuerst Datenfile in Zeilen zerlegen
439 // "Allgemein: 0: 0: 0:Allgemeine Unterhaltungsebene"
440 // Danach drueberlaufen und in Einzelfelder splitten, dabei gleich die
441 // Trennzeichen (Doppelpunkt, Tab und Space) rausfiltern.
442 foreach(string ch : old_explode(ch_list, "\n"))
443 {
444 if (ch[0]=='#')
445 continue;
446 setup( regexplode(ch, ":[ \t]*", RE_OMIT_DELIM) );
447 }
MG Mud User88f12472016-06-24 23:31:02 +0200448}
449
Arathorn78c08372019-12-11 20:14:23 +0100450// BEGIN OF THE CHANNEL MASTER IMPLEMENTATION
Zesstra@Morgengrauen2b229372016-07-20 23:59:54 +0200451protected void create()
MG Mud User88f12472016-06-24 23:31:02 +0200452{
453 seteuid(getuid());
454 restore_object(CHANNEL_SAVE);
Arathorn19459eb2019-11-30 00:45:51 +0100455
Zesstrab7720dc2020-08-11 22:14:18 +0200456 // Altes channelC aus Savefiles konvertieren...
457 if (widthof(channelC) == 1)
458 {
459 mapping new = m_allocate(sizeof(channelC), 2);
460 foreach(string chname, mixed arr: channelC)
461 {
462 struct channel_base_s ch = (<channel_base_s> name: arr[0],
463 desc: arr[1]);
464 // die anderen beiden Werte bleiben 0
465 m_add(new, chname, ch, arr[2]);
466 }
467 channelC = new;
468 }
Zesstra26aaf1a2020-08-07 19:10:39 +0200469 //TODO: weitere Mappings im MEMORY speichern, Savefile ersetzen.
470
Arathorn19459eb2019-11-30 00:45:51 +0100471 /* Die Channel-History wird nicht nur lokal sondern auch noch im Memory
472 gespeichert, dadurch bleibt sie auch ueber ein Reload erhalten.
MG Mud User88f12472016-06-24 23:31:02 +0200473 Der folgende Code versucht, den Zeiger aus dem Memory zu holen. Falls
474 das nicht moeglich ist, wird ein neuer erzeugt und gegebenenfalls im
475 Memory abgelegt. */
476
477 // Hab ich die noetigen Rechte im Memory?
Arathorn19459eb2019-11-30 00:45:51 +0100478 if (call_other(MEMORY, "HaveRights"))
479 {
MG Mud User88f12472016-06-24 23:31:02 +0200480 // Objektpointer laden
Dominik Schaeferfa564d52020-08-05 20:50:27 +0200481 channelH = ({mapping}) call_other(MEMORY, "Load", "History");
MG Mud User88f12472016-06-24 23:31:02 +0200482
483 // Wenns nich geklappt hat, hat der Memory noch keinen Zeiger, dann
Arathorn78c08372019-12-11 20:14:23 +0100484 if (!mappingp(channelH))
485 {
MG Mud User88f12472016-06-24 23:31:02 +0200486 // Zeiger erzeugen
487 channelH = ([]);
488 // und in den Memory schreiben
Arathorn19459eb2019-11-30 00:45:51 +0100489 call_other(MEMORY, "Save", "History", channelH);
MG Mud User88f12472016-06-24 23:31:02 +0200490 }
Arathorn19459eb2019-11-30 00:45:51 +0100491 }
492 else
493 {
MG Mud User88f12472016-06-24 23:31:02 +0200494 // Keine Rechte im Memory, dann wird mit einem lokalen Zeiger gearbeitet.
495 channelH = ([]);
496 }
497
498 stats = (["time": time(),
Arathorn78c08372019-12-11 20:14:23 +0100499 "boot": capitalize(getuid(previous_object()) || "<Unbekannt>")]);
500
501 // <MasteR>-Ebene erstellen. Channeld wird Ebenenbesitzer und somit auch
502 // Zuhoerer, damit er auf Kommandos auf dieser Ebene reagieren kann.
503 new(CMNAME, this_object(), "Zentrale Informationen zu den Ebenen");
504
MG Mud User88f12472016-06-24 23:31:02 +0200505 initialize();
Arathorn78c08372019-12-11 20:14:23 +0100506 users()->RegisterChannels();
507
508 // Die Zugriffskontrolle auf die Ebenen wird von der Funktion access()
509 // erledigt. Weil sowohl externe Aufrufe aus dem Spielerobjekt, als auch
510 // interne Aufrufe aus diesem Objekt vorkommen koennen, wird hier ein
511 // explizites call_other() auf this_object() gemacht, damit der
512 // Caller-Stack bei dem internen Aufruf denselben Aufbau hat wie bei
513 // einem externen.
MG Mud User88f12472016-06-24 23:31:02 +0200514 this_object()->send(CMNAME, this_object(),
Arathorn19459eb2019-11-30 00:45:51 +0100515 sprintf("%d Ebenen mit %d Teilnehmern initialisiert.",
516 sizeof(channels),
Arathorn78c08372019-12-11 20:14:23 +0100517 CountUsers()));
MG Mud User88f12472016-06-24 23:31:02 +0200518}
519
Arathorn78c08372019-12-11 20:14:23 +0100520varargs void reset()
MG Mud User88f12472016-06-24 23:31:02 +0200521{
Zesstra6ddbacb2020-08-13 21:43:28 +0200522 // Im Durchschnitt 1 Tag: 21.6h + random(4.8h), d.h. naechster reset ist
523 // zwischen 21.6h und 26.4h von jetzt.
524 set_next_reset(77760 + random(17280));
Zesstra26aaf1a2020-08-07 19:10:39 +0200525
Zesstra6ddbacb2020-08-13 21:43:28 +0200526 // inaktive Ebenen bereinigen
527 int timeout = INACTIVE_EXPIRE;
528 // Wir behalten immer ungefaehr die Haelfte von MAX_INACTIVE_CHANNELS
529 // inaktive Ebenen. In jeder Iteration wird das erlaubte Timeout reduziert,
530 // bis genug inaktive Ebenen weg sind, aber MIN_INACTIVE_LIFETIME bleiben
531 // Ebenen min. inaktiv bestehen.
532 while (sizeof(channelC) > MAX_INACTIVE_CHANNELS/2
533 && timeout > MIN_INACTIVE_LIFETIME)
534 {
535 channelC = filter(channelC,
536 function int (string ch_name, mixed values)
Zesstra8f5102c2020-08-08 12:51:52 +0200537 {
Zesstra6ddbacb2020-08-13 21:43:28 +0200538 struct channel_base_s data = values[0];
539 int ts = values[1];
540 if (ts + timeout > time())
Zesstra8f5102c2020-08-08 12:51:52 +0200541 return 1;
542 // Ebenendaten koennen weg, inkl. History, die also auch loeschen
543 m_delete(channelH, ch_name);
544 return 0;
545 });
Zesstra6ddbacb2020-08-13 21:43:28 +0200546 // timeout halbieren und neu versuchen wenn noetig.
547 timeout /= 2;
548 }
549 // achja, speichern sollten wir uns ggf. auch noch.
MG Mud User88f12472016-06-24 23:31:02 +0200550 if (save_me_soon)
551 {
Arathorn19459eb2019-11-30 00:45:51 +0100552 save_me_soon = 0;
MG Mud User88f12472016-06-24 23:31:02 +0200553 save_object(CHANNEL_SAVE);
554 }
555}
556
Zesstra5856ada2020-08-13 21:52:47 +0200557varargs int remove(int silent)
558{
559 if (save_me_soon)
560 {
561 save_me_soon = 0;
562 save_object(CHANNEL_SAVE);
563 }
564 if (!silent)
565 {
566 this_object()->send(CMNAME, this_object(),
567 sprintf("remove() durch %O gerufen. Speichern und Ende.\n",
568 previous_object()));
569 }
570 destruct(this_object());
571 return 1;
572}
573
MG Mud User88f12472016-06-24 23:31:02 +0200574// name() - define the name of this object.
Arathorn19459eb2019-11-30 00:45:51 +0100575string name()
576{
577 return CMNAME;
578}
579
580string Name()
581{
582 return CMNAME;
583}
MG Mud User88f12472016-06-24 23:31:02 +0200584
Zesstra28986e12020-08-09 12:44:26 +0200585// Low-level function for adding members without access checks
Zesstrafb350dc2020-08-12 00:49:31 +0200586// return values < 0 are errors, success is 1.
Zesstrab7720dc2020-08-11 22:14:18 +0200587private int add_member(struct channel_s ch, object m)
Zesstra28986e12020-08-09 12:44:26 +0200588{
589 if (IsChannelMember(ch, m))
590 return E_ALREADY_JOINED;
591
Zesstrab7720dc2020-08-11 22:14:18 +0200592 ch.members += ({ m });
Zesstrafb350dc2020-08-12 00:49:31 +0200593 return 1;
Zesstra28986e12020-08-09 12:44:26 +0200594}
595
Zesstra52d5f8a2020-08-12 00:39:15 +0200596private void remove_all_members(struct channel_s ch)
597{
598 // Einer geloeschten/inaktiven Ebene kann man nicht zuhoeren: Ebenenname
599 // aus der Ebenenliste aller Mitglieder austragen. Dabei werden sowohl ein-,
600 // als auch temporaer ausgeschaltete Ebenen beruecksichtigt.
601 string chname = lower_case(ch.name);
602 foreach(object listener : ch.members)
603 {
604 string* pl_chans = listener->QueryProp(P_CHANNELS);
605 if (pointerp(pl_chans))
606 {
607 listener->SetProp(P_CHANNELS, pl_chans-({chname}));
608 }
609 pl_chans = listener->QueryProp(P_SWAP_CHANNELS);
610 if (pointerp(pl_chans))
611 {
612 listener->SetProp(P_SWAP_CHANNELS, pl_chans-({chname}));
613 }
614 }
615}
616
Zesstra6ddbacb2020-08-13 21:43:28 +0200617private void delete_channel(string chname, int force);
618
Zesstraf87cb772020-08-10 11:14:45 +0200619// Deaktiviert eine Ebene, behaelt aber einige Stammdaten in channelC und die
620// History, so dass sie spaeter reaktiviert werden kann.
Zesstra52d5f8a2020-08-12 00:39:15 +0200621// Wenn <force>, dann wird wie Ebene sogar deaktiviert, wenn noch Zuhoerer
622// anwesend sind.
623private void deactivate_channel(string chname, int force)
Zesstraf87cb772020-08-10 11:14:45 +0200624{
Zesstra6ddbacb2020-08-13 21:43:28 +0200625 // Wenn zuviele inaktive Ebenen, wird sie geloescht statt deaktiviert.
626 if (sizeof(channelC) > MAX_INACTIVE_CHANNELS)
627 {
628 log_file("CHANNEL",
629 sprintf("[%s] Zuviele inaktive Ebenen. Channel %s geloescht statt "
630 "deaktiviert.\n", dtime(time()), chname));
631 this_object()->send(CMNAME, this_object(),
632 sprintf("Zuviele inaktive Ebenen. Ebene %s geloescht statt "
633 "deaktiviert.\n", chname));
634 delete_channel(chname, force);
635 return;
636 }
Zesstrab7720dc2020-08-11 22:14:18 +0200637 chname = lower_case(chname);
638 struct channel_s ch = channels[chname];
Zesstra52d5f8a2020-08-12 00:39:15 +0200639 // Deaktivieren kann man nur aktive Ebenen.
Zesstrab7720dc2020-08-11 22:14:18 +0200640 if (!structp(ch))
Zesstraf87cb772020-08-10 11:14:45 +0200641 return;
Zesstra52d5f8a2020-08-12 00:39:15 +0200642 // Falls sie noch Zuhoerer hat, muss man sich erstmal um die kuemmern.
Zesstrab7720dc2020-08-11 22:14:18 +0200643 if (sizeof(ch.members))
644 {
Zesstra52d5f8a2020-08-12 00:39:15 +0200645 // ohne <force> nur Ebenen ohne Zuhoerer deaktivieren.
646 if (!force)
647 {
648 raise_error(
649 sprintf("Attempt to deactivate channel %s with listeners.\n",
650 ch.name));
651 }
652 else
653 {
654 remove_all_members(ch);
655 }
Zesstrab7720dc2020-08-11 22:14:18 +0200656 }
Zesstraf87cb772020-08-10 11:14:45 +0200657 // Einige Daten merken, damit sie reaktiviert werden kann, wenn jemand
658 // einloggt, der die Ebene abonniert hat.
Zesstrab7720dc2020-08-11 22:14:18 +0200659 m_add(channelC, chname, to_struct(channels[chname], (<channel_base_s>)),
660 time());
Zesstraf87cb772020-08-10 11:14:45 +0200661
Zesstrab7720dc2020-08-11 22:14:18 +0200662 // aktive Ebene loeschen bzw. deaktivieren.
663 m_delete(channels, chname);
Zesstraf87cb772020-08-10 11:14:45 +0200664 // History wird nicht geloescht, damit sie noch verfuegbar ist, wenn die
665 // Ebene spaeter nochmal neu erstellt wird. Sie wird dann bereinigt, wenn
666 // channelC bereinigt wird.
667
668 stats["dispose"]++;
669 save_me_soon = 1;
670}
671
672// Loescht eine Ebene vollstaendig inkl. Stammdaten und History.
Zesstra52d5f8a2020-08-12 00:39:15 +0200673// Wenn <force>, dann wird wie Ebene sogar deaktiviert, wenn noch Zuhoerer
674// anwesend sind.
675private void delete_channel(string chname, int force)
Zesstraf87cb772020-08-10 11:14:45 +0200676{
Zesstrab7720dc2020-08-11 22:14:18 +0200677 chname = lower_case(chname);
678 struct channel_s ch = channels[chname];
Zesstra52d5f8a2020-08-12 00:39:15 +0200679 // Ist die Ebene noch aktiv?
Zesstrab7720dc2020-08-11 22:14:18 +0200680 if (ch)
Zesstraf87cb772020-08-10 11:14:45 +0200681 {
Zesstra52d5f8a2020-08-12 00:39:15 +0200682 // Und hat sie Zuhoerer?
Zesstrab7720dc2020-08-11 22:14:18 +0200683 if (sizeof(ch.members))
Zesstra52d5f8a2020-08-12 00:39:15 +0200684 {
685 // ohne <force> nur Ebenen ohne Zuhoerer loeschen.
686 if (!force)
687 {
688 raise_error(
689 sprintf("Attempt to delete channel %s with listeners.\n",
690 ch.name));
691 }
692 else
693 {
694 remove_all_members(ch);
695 }
696 }
Zesstraf87cb772020-08-10 11:14:45 +0200697 stats["dispose"]++;
Zesstrab7720dc2020-08-11 22:14:18 +0200698 m_delete(channels, chname);
Zesstraf87cb772020-08-10 11:14:45 +0200699 }
700 // Ab hier das gleiche fuer aktive und inaktive Ebenen.
Zesstra6ddbacb2020-08-13 21:43:28 +0200701 m_delete(channelC, chname);
702 m_delete(channelH, chname);
Zesstraf87cb772020-08-10 11:14:45 +0200703 save_me_soon = 1;
704}
705
Zesstra5b7f2fc2020-08-10 02:09:13 +0200706// Aendert das Supervisor-Objekt einer Ebene, ggf. mit Meldung.
707// Wenn kein neuer SV angegeben, wird der aelteste Zuhoerer gewaehlt.
Zesstrabf4f86d2020-08-12 00:56:17 +0200708private int change_sv_object(struct channel_s ch, object new_sv)
Zesstra5b7f2fc2020-08-10 02:09:13 +0200709{
710 if (!new_sv)
711 {
Zesstrab7720dc2020-08-11 22:14:18 +0200712 ch.members -= ({0});
713 if (sizeof(ch.members))
714 new_sv = ch.members[0];
Zesstra5b7f2fc2020-08-10 02:09:13 +0200715 else
716 return 0; // kein neuer SV moeglich.
717 }
Zesstra9359fab2020-08-13 12:03:01 +0200718 // evtl. darf der supervisor aber nicht zu was anderes als dem creator
719 // wechseln. Ausserdem darf niemand supervisor werden, der nicht auf der
720 // Ebene ist.
721 if ( ((ch.flags & CHF_FIXED_SUPERVISOR)
722 && new_sv != find_object(ch.creator))
723 || !IsChannelMember(ch, new_sv)
724 )
725 return 0;
726
Zesstrabf4f86d2020-08-12 00:56:17 +0200727 object old_sv = ch.supervisor;
728
Zesstrab7720dc2020-08-11 22:14:18 +0200729 ch.supervisor = new_sv;
Zesstrab7720dc2020-08-11 22:14:18 +0200730 ch.access_cl = symbol_function("check_ch_access", new_sv);
Zesstra0c69c2d2020-08-10 02:27:20 +0200731
Zesstra5b7f2fc2020-08-10 02:09:13 +0200732 if (old_sv && new_sv
733 && !old_sv->QueryProp(P_INVIS)
734 && !new_sv->QueryProp(P_INVIS))
735 {
736 // Die Zugriffskontrolle auf die Ebenen wird von der Funktion access()
737 // erledigt. Weil sowohl externe Aufrufe aus dem Spielerobjekt, als auch
738 // interne Aufrufe aus diesem Objekt vorkommen koennen, wird hier ein
739 // explizites call_other() auf this_object() gemacht, damit der
740 // Caller-Stack bei dem internen Aufruf denselben Aufbau hat wie bei
741 // einem externen.
Zesstrab7720dc2020-08-11 22:14:18 +0200742 this_object()->send(ch.name, old_sv,
Zesstra5b7f2fc2020-08-10 02:09:13 +0200743 sprintf("uebergibt die Ebene an %s.",new_sv->name(WEN)),
744 MSG_EMOTE);
745 }
Zesstrabf4f86d2020-08-12 00:56:17 +0200746 else if (old_sv && !old_sv->QueryProp(P_INVIS))
Zesstra5b7f2fc2020-08-10 02:09:13 +0200747 {
Zesstrab7720dc2020-08-11 22:14:18 +0200748 this_object()->send(ch.name, old_sv,
Zesstra5b7f2fc2020-08-10 02:09:13 +0200749 "uebergibt die Ebene an jemand anderen.", MSG_EMOTE);
750 }
751 else if (new_sv && !new_sv->QueryProp(P_INVIS))
752 {
Zesstrab7720dc2020-08-11 22:14:18 +0200753 this_object()->send(ch.name, new_sv,
Zesstra5b7f2fc2020-08-10 02:09:13 +0200754 "uebernimmt die Ebene von jemand anderem.", MSG_EMOTE);
755 }
756 return 1;
757}
758
Zesstra56692c72020-08-09 13:03:10 +0200759// Stellt sicher, dass einen Ebenen-Supervisor gibt. Wenn dies nicht moeglich
760// ist (z.b. leere Ebene), dann wird die Ebene geloescht und 0
Zesstrab7720dc2020-08-11 22:14:18 +0200761// zurueckgegeben. Allerdings kann nach dieser Funktion sehr wohl die
762// access_cl 0 sein, wenn der SV keine oeffentliche definiert! In diesem Fall
Zesstra56692c72020-08-09 13:03:10 +0200763// wird access() den Zugriff immer erlauben.
Zesstrab7720dc2020-08-11 22:14:18 +0200764private int assert_supervisor(struct channel_s ch)
MG Mud User88f12472016-06-24 23:31:02 +0200765{
Zesstra9359fab2020-08-13 12:03:01 +0200766 // Wenn der supervisor nicht mehr existiert, muss ein neuer gesucht werden.
767 if (!ch.supervisor)
MG Mud User88f12472016-06-24 23:31:02 +0200768 {
Zesstra9359fab2020-08-13 12:03:01 +0200769 // Wenn der Wechsel des SV verboten ist, wird versucht, den
770 // urspruenglichen Ersteller neuzuladen und zum neuen, alten Supervisor zu
771 // machen.
772 if (ch.flags & CHF_FIXED_SUPERVISOR)
MG Mud User88f12472016-06-24 23:31:02 +0200773 {
Zesstra9359fab2020-08-13 12:03:01 +0200774 object sv;
775 string err=catch(sv=load_object(ch.creator);publish);
Arathorn78c08372019-12-11 20:14:23 +0100776 if (!err)
777 {
Zesstra9359fab2020-08-13 12:03:01 +0200778 // Juchu, die richtige SV ist wieder da. Sie muss noch auf die Ebene
779 // und kann dann wieder SV werden.
780 add_member(ch, sv);
781 if (!change_sv_object(ch, sv))
782 {
783 // ich wuesste nicht, was in change_sv_object in diesem Fall
784 // schiefgehen kann, daher einfach ein raise_error.
785 raise_error(sprintf("Supervisor von Channel %s konnte nicht "
786 "reaktiviert werden: %O\n",ch.name,sv));
787 }
Arathorn78c08372019-12-11 20:14:23 +0100788 }
Zesstra9359fab2020-08-13 12:03:01 +0200789 // wenns nicht geklappt hat, wird die Ebene deaktiviert.
Arathorn78c08372019-12-11 20:14:23 +0100790 else
791 {
Zesstra9359fab2020-08-13 12:03:01 +0200792 // Die inaktive Ebene kann wegen CHF_FIXED_SUPERVISOR nur vom
793 // urspruenglichen Ersteller reaktiviert/neu erstellt werden. Und
794 // solange der das nicht tut, ist weder die History zugaenglich, noch
795 // kann jemand sonst was damit machen. Wenn die inaktive Ebene
796 // irgendwann inkl. History expired wird, kann jemand anderes dann
797 // den Namen wieder verwenden und ein komplett neue Ebene erstellen.
798 deactivate_channel(lower_case(ch.name), 1);
Zesstra5770ba62020-08-10 10:19:23 +0200799 log_file("CHANNEL",
Zesstra9359fab2020-08-13 12:03:01 +0200800 sprintf("[%s] Channel %s deaktiviert. SV-Fehler: %O -> %O\n",
Zesstrab7720dc2020-08-11 22:14:18 +0200801 dtime(time()), ch.name, ch.supervisor, err));
Arathorn78c08372019-12-11 20:14:23 +0100802 return 0;
803 }
MG Mud User88f12472016-06-24 23:31:02 +0200804 }
Zesstra9359fab2020-08-13 12:03:01 +0200805 // Der normalfall ist aber, dass wir einfach einen supervisor aus dem
806 // Kreise der Zuhoerer bestimmen und zwar den aeltesten. Das macht
807 // change_sv_object().
808 else
Zesstra56692c72020-08-09 13:03:10 +0200809 {
Zesstrabf4f86d2020-08-12 00:56:17 +0200810 if (!change_sv_object(ch, 0))
Zesstra5770ba62020-08-10 10:19:23 +0200811 {
Zesstra9359fab2020-08-13 12:03:01 +0200812 // wenn das nicht klappt, Ebene deaktivieren, vermutlich hat sie keine
813 // Zuhoerer.
814 deactivate_channel(lower_case(ch.name), 1);
Zesstra5770ba62020-08-10 10:19:23 +0200815 log_file("CHANNEL",
Zesstra9359fab2020-08-13 12:03:01 +0200816 sprintf("[%s] Kein SV, deaktiviere channel %s.\n",
Zesstrab7720dc2020-08-11 22:14:18 +0200817 dtime(time()), ch.name));
Zesstra5770ba62020-08-10 10:19:23 +0200818 return 0;
819 }
Zesstra56692c72020-08-09 13:03:10 +0200820 }
MG Mud User88f12472016-06-24 23:31:02 +0200821 }
Zesstra78310012020-08-09 12:21:48 +0200822 return 1;
823}
824
825// access() - check access by looking for the right argument types and
826// calling access closures respectively
827// SEE: new, join, leave, send, list, users
828// Note: <pl> is usually an object, only the master supplies a string during
829// runtime error handling.
830// Wertebereich: 0 fuer Zugriff verweigert, 1 fuer Zugriff erlaubt, 2 fuer
831// Zugriff erlaubt fuer privilegierte Objekte, die senden duerfen ohne
832// Zuhoerer zu sein. (Die Aufrufer akzeptieren aber auch alle negativen Werte
833// als Erfolg und alles ueber >2 als privilegiert.)
Zesstrab7720dc2020-08-11 22:14:18 +0200834varargs private int access(struct channel_s ch, object|string pl, string cmd,
Zesstra78310012020-08-09 12:21:48 +0200835 string txt)
836{
Zesstrab7720dc2020-08-11 22:14:18 +0200837 if (!ch)
Zesstra78310012020-08-09 12:21:48 +0200838 return 0;
839
Zesstrafbfe6362020-08-09 13:30:21 +0200840 // Dieses Objekt und Root-Objekte duerfen auf der Ebene senden, ohne
841 // Mitglied zu sein. Das ist die Folge der zurueckgegebenen 2.
Zesstra78310012020-08-09 12:21:48 +0200842 if ( !previous_object(1) || !extern_call() ||
843 previous_object(1) == this_object() ||
Zesstrafbfe6362020-08-09 13:30:21 +0200844 getuid(previous_object(1)) == ROOTID)
Zesstra78310012020-08-09 12:21:48 +0200845 return 2;
Arathorn739a4fa2020-08-06 21:52:58 +0200846
Zesstra56692c72020-08-09 13:03:10 +0200847 // Nur dieses Objekt darf Meldungen im Namen anderer Objekte faken,
848 // ansonsten muss <pl> der Aufrufer sein.
Arathorn739a4fa2020-08-06 21:52:58 +0200849 if (!objectp(pl) ||
850 ((previous_object(1) != pl) && (previous_object(1) != this_object())))
851 return 0;
852
853 if (IsBanned(pl, cmd))
854 return 0;
855
Zesstra56692c72020-08-09 13:03:10 +0200856 // Wenn kein SV-Objekt mehr existiert und kein neues bestimmt werden konnte,
857 // wurde die Ebene ausfgeloest. In diesem Fall auch den Zugriff verweigern.
Zesstra78310012020-08-09 12:21:48 +0200858 if (!assert_supervisor(ch))
Zesstra56692c72020-08-09 13:03:10 +0200859 return 0;
860 // Wenn closure jetzt dennoch 0, wird der Zugriff erlaubt.
Zesstrab7720dc2020-08-11 22:14:18 +0200861 if (!ch.access_cl)
Arathorn739a4fa2020-08-06 21:52:58 +0200862 return 1;
863
Zesstra6fe46cd2020-08-09 13:12:15 +0200864 // Das SV-Objekt wird gefragt, ob der Zugriff erlaubt ist. Dieses erfolgt
865 // fuer EM+ aber nur, wenn der CHANNELD selber das SV-Objekt ist, damit
866 // nicht beliebige SV-Objekt EMs den Zugriff verweigern koennen. Ebenen mit
867 // CHANNELD als SV koennen aber natuerlich auch EM+ Zugriff verweigern.
Zesstra9359fab2020-08-13 12:03:01 +0200868 if (IS_ARCH(previous_object(1)) && ch.supervisor != this_object())
Zesstra6fe46cd2020-08-09 13:12:15 +0200869 return 1;
870
Zesstrab7720dc2020-08-11 22:14:18 +0200871 return funcall(ch.access_cl, lower_case(ch.name), pl, cmd, &txt);
MG Mud User88f12472016-06-24 23:31:02 +0200872}
873
Arathorn78c08372019-12-11 20:14:23 +0100874// Neue Ebene <ch> erstellen mit <owner> als Ebenenbesitzer.
Zesstrab7720dc2020-08-11 22:14:18 +0200875// <desc> kann die statische Beschreibung der Ebene sein oder eine Closure,
Arathorn78c08372019-12-11 20:14:23 +0100876// die dynamisch aktualisierte Infos ausgibt.
877// Das Objekt <owner> kann eine Funktion check_ch_access() definieren, die
878// gerufen wird, wenn eine Ebenenaktion vom Typ join/leave/send/list/users
879// eingeht.
MG Mud User88f12472016-06-24 23:31:02 +0200880#define IGNORE "^/xx"
Zesstrad9ec04b2020-08-11 23:47:03 +0200881public varargs int new(string ch_name, object owner, string|closure desc,
882 int channel_flags)
MG Mud User88f12472016-06-24 23:31:02 +0200883{
Arathorn78c08372019-12-11 20:14:23 +0100884 // Kein Channelmaster angegeben, oder wir sind es selbst, aber der Aufruf
885 // kam von ausserhalb. (Nur der channeld selbst darf sich als Channelmaster
886 // fuer eine neue Ebene eintragen.)
887 if (!objectp(owner) || (owner == this_object() && extern_call()) )
MG Mud User88f12472016-06-24 23:31:02 +0200888 return E_ACCESS_DENIED;
889
Arathorn78c08372019-12-11 20:14:23 +0100890 // Kein gescheiter Channelname angegeben.
891 if (!stringp(ch_name) || !sizeof(ch_name))
892 return E_ACCESS_DENIED;
893
894 // Channel schon vorhanden oder schon alle Channel-Slots belegt.
895 if (channels[lower_case(ch_name)] || sizeof(channels) >= MAX_CHANNELS)
896 return E_ACCESS_DENIED;
897
898 // Der angegebene Ebenenbesitzer darf keine Ebenen erstellen, wenn fuer ihn
899 // ein Bann auf die Aktion C_NEW besteht, oder das Ignore-Pattern auf
900 // seinen Objektnamen matcht.
901 if (IsBanned(owner,C_NEW) || regmatch(object_name(owner), IGNORE))
902 return E_ACCESS_DENIED;
903
Zesstraf20e8ba2020-08-12 01:56:30 +0200904 // Zunaechst pruefen, ob eine alte, inaktive Ebene mit dem Namen noch
905 // existiert.
906 struct channel_base_s cbase = channelC[lower_case(ch_name)];
Zesstrab7720dc2020-08-11 22:14:18 +0200907 struct channel_s ch;
Zesstraf20e8ba2020-08-12 01:56:30 +0200908 if (cbase)
Arathorn19459eb2019-11-30 00:45:51 +0100909 {
Zesstraf20e8ba2020-08-12 01:56:30 +0200910 // Wenn bei Reaktivierung von Ebenen (auch mit neuer Beschreibung *g*) der
911 // neue owner != dem urspruenglichen Ersteller der Ebene ist und das Flag
912 // CHF_FIXED_SUPERVISOR gesetzt ist, wird die Reaktivierung abgebrochen,
913 // damit niemand inaktive Ebenen und deren History auf diesem Wege
914 // uebernehmen kann, d.h. den Supervisor ersetzen kann.
915 if ((cbase.flags & CHF_FIXED_SUPERVISOR)
916 && object_name(owner) != cbase.creator)
Arathorn19459eb2019-11-30 00:45:51 +0100917 return E_ACCESS_DENIED;
Zesstraf20e8ba2020-08-12 01:56:30 +0200918 // Alte Daten der Ebene uebernehmen
919 ch = to_struct(cbase, (<channel_s>));
920 // Wenn eine Beschreibung uebergeben, dann ersetzt sie jetzt die alte
921 if (desc)
922 ch.desc = desc;
923 // creator bleibt natuerlich bestehen. Die Flags auch. Wir behalten auch
924 // die Schreibweise (Gross-/Kleinschreibung) des Namens aus
925 // Konsistenzgruenden bei.
MG Mud User88f12472016-06-24 23:31:02 +0200926 }
Arathorn19459eb2019-11-30 00:45:51 +0100927 else
928 {
Zesstraf20e8ba2020-08-12 01:56:30 +0200929 // Wenn keine Beschreibung und keine inaktive Ebene da ist, wirds nen
930 // Fehler...
931 if (!desc)
932 return E_ACCESS_DENIED;
933 // prima, alles da. Dann wird ein ganz frische neue Ebenenstruktur
934 // erzeugt.
Zesstrad9ec04b2020-08-11 23:47:03 +0200935 ch = (<channel_s> name: ch_name, desc: desc, creator: object_name(owner),
936 flags: channel_flags);
Arathorn19459eb2019-11-30 00:45:51 +0100937 }
MG Mud User88f12472016-06-24 23:31:02 +0200938
Zesstrab7720dc2020-08-11 22:14:18 +0200939 ch_name = lower_case(ch_name);
940
941 ch.members = ({ owner });
Zesstra9359fab2020-08-13 12:03:01 +0200942 ch.supervisor = owner;
Zesstrad9ec04b2020-08-11 23:47:03 +0200943 // check_ch_access() dient der Zugriffskontrolle und entscheidet, ob die
944 // Nachricht gesendet werden darf oder nicht.
Zesstra798312a2020-08-12 23:34:19 +0200945 ch.access_cl = symbol_function("check_ch_access", owner);
Zesstrab7720dc2020-08-11 22:14:18 +0200946
947 m_add(channels, ch_name, ch);
MG Mud User88f12472016-06-24 23:31:02 +0200948
Arathorn78c08372019-12-11 20:14:23 +0100949 // History fuer eine Ebene nur dann initialisieren, wenn es sie noch
950 // nicht gibt.
Zesstrab7720dc2020-08-11 22:14:18 +0200951 if (!pointerp(channelH[ch_name]))
952 channelH[ch_name] = ({});
MG Mud User88f12472016-06-24 23:31:02 +0200953
Zesstraf20e8ba2020-08-12 01:56:30 +0200954 // Datenstruktur einer ggf. inaktiven Ebene mit dem Namen in channelC kann
955 // jetzt auch weg.
956 if (cbase)
957 m_delete(channelC, ch_name);
958
Arathorn78c08372019-12-11 20:14:23 +0100959 // Erstellen neuer Ebenen loggen, wenn wir nicht selbst der Ersteller sind.
960 if (owner != this_object())
Zesstra5770ba62020-08-10 10:19:23 +0200961 log_file("CHANNEL.new", sprintf("[%s] Neue Ebene %s: %O %O\n",
Zesstrab7720dc2020-08-11 22:14:18 +0200962 dtime(time()), ch.name, owner, desc));
Arathorn19459eb2019-11-30 00:45:51 +0100963
Arathorn78c08372019-12-11 20:14:23 +0100964 // Erfolgsmeldung ausgeben, ausser bei unsichtbarem Ebenenbesitzer.
965 if (!owner->QueryProp(P_INVIS))
966 {
967 // Die Zugriffskontrolle auf die Ebenen wird von der Funktion access()
968 // erledigt. Weil sowohl externe Aufrufe aus dem Spielerobjekt, als auch
969 // interne Aufrufe aus diesem Objekt vorkommen koennen, wird hier ein
970 // explizites call_other() auf this_object() gemacht, damit der
971 // Caller-Stack bei dem internen Aufruf denselben Aufbau hat wie bei
972 // einem externen.
973 this_object()->send(CMNAME, owner,
Zesstrab7720dc2020-08-11 22:14:18 +0200974 "laesst die Ebene '" + ch.name + "' entstehen.", MSG_EMOTE);
Arathorn78c08372019-12-11 20:14:23 +0100975 }
Arathorn19459eb2019-11-30 00:45:51 +0100976
MG Mud User88f12472016-06-24 23:31:02 +0200977 stats["new"]++;
Arathorn19459eb2019-11-30 00:45:51 +0100978 return (0);
MG Mud User88f12472016-06-24 23:31:02 +0200979}
980
Arathorn78c08372019-12-11 20:14:23 +0100981// Objekt <pl> betritt Ebene <ch>. Dies wird zugelassen, wenn <pl> die
982// Berechtigung hat und noch nicht Mitglied ist. (Man kann einer Ebene nicht
983// zweimal beitreten.)
Zesstrab7720dc2020-08-11 22:14:18 +0200984public int join(string chname, object pl)
MG Mud User88f12472016-06-24 23:31:02 +0200985{
Zesstrab7720dc2020-08-11 22:14:18 +0200986 struct channel_s ch = channels[lower_case(chname)];
Arathorn739a4fa2020-08-06 21:52:58 +0200987 /* funcall() auf Closure-Operator, um einen neuen Eintrag im Caller Stack
988 zu erzeugen, weil access() mit extern_call() und previous_object()
989 arbeitet und sichergestellt sein muss, dass das in jedem Fall das
990 richtige ist. */
991 if (!funcall(#'access, ch, pl, C_JOIN))
Arathorn19459eb2019-11-30 00:45:51 +0100992 return E_ACCESS_DENIED;
993
Zesstrafb350dc2020-08-12 00:49:31 +0200994 int res = add_member(ch, pl);
995 if (res != 1)
996 return res;
997
Zesstra2b7ed1a2020-08-12 00:58:33 +0200998 // Wenn der <pl> der urspruengliche Ersteller der Ebene und kein Spieler
999 // ist, wird er automatisch wieder zum Supervisor.
1000 if (!query_once_interactive(pl) && object_name(pl) == ch.creator)
1001 change_sv_object(ch, pl);
1002
Zesstrafb350dc2020-08-12 00:49:31 +02001003 return 0;
MG Mud User88f12472016-06-24 23:31:02 +02001004}
1005
Arathorn78c08372019-12-11 20:14:23 +01001006// Objekt <pl> verlaesst Ebene <ch>.
1007// Zugriffsrechte werden nur der Vollstaendigkeit halber geprueft; es duerfte
1008// normalerweise keinen Grund geben, das Verlassen einer Ebene zu verbieten.
1009// Dies ist in check_ch_access() so geregelt, allerdings koennte dem Objekt
1010// <pl> das Verlassen auf Grund eines Banns verboten sein.
1011// Wenn kein Spieler mehr auf der Ebene ist, loest sie sich auf, sofern nicht
1012// noch ein Ebenenbesitzer eingetragen ist.
Zesstrab7720dc2020-08-11 22:14:18 +02001013public int leave(string chname, object pl)
MG Mud User88f12472016-06-24 23:31:02 +02001014{
Zesstrab7720dc2020-08-11 22:14:18 +02001015 struct channel_s ch = channels[lower_case(chname)];
Zesstra877cb0a2020-08-10 02:10:21 +02001016
Zesstrab7720dc2020-08-11 22:14:18 +02001017 ch.members -= ({0}); // kaputte Objekte erstmal raus
Zesstra877cb0a2020-08-10 02:10:21 +02001018
1019 if (!IsChannelMember(ch, pl))
1020 return E_NOT_MEMBER;
1021
Arathorn739a4fa2020-08-06 21:52:58 +02001022 /* funcall() auf Closure-Operator, um einen neuen Eintrag im Caller Stack
1023 zu erzeugen, weil access() mit extern_call() und previous_object()
1024 arbeitet und sichergestellt sein muss, dass das in jedem Fall das
1025 richtige ist. */
1026 if (!funcall(#'access, ch, pl, C_LEAVE))
Arathorn19459eb2019-11-30 00:45:51 +01001027 return E_ACCESS_DENIED;
1028
Zesstrab7720dc2020-08-11 22:14:18 +02001029 // Dann mal den Zuhoerer raus.
1030 ch.members -= ({pl});
Zesstrae6d33852020-08-09 14:37:53 +02001031
Zesstra5b7f2fc2020-08-10 02:09:13 +02001032 // Wenn auf der Ebene jetzt noch Objekte zuhoeren, muss ggf. der SV
1033 // wechseln.
Zesstrab7720dc2020-08-11 22:14:18 +02001034 if (sizeof(ch.members))
MG Mud User88f12472016-06-24 23:31:02 +02001035 {
Zesstra5b7f2fc2020-08-10 02:09:13 +02001036 // Kontrolle an jemand anderen uebergeben, wenn der Ebenensupervisor
1037 // diese verlassen hat. change_sv_object() waehlt per Default den
1038 // aeltesten Zuhoerer.
Zesstra9359fab2020-08-13 12:03:01 +02001039 if (pl == ch.supervisor)
Arathorn78c08372019-12-11 20:14:23 +01001040 {
Zesstrabf4f86d2020-08-12 00:56:17 +02001041 change_sv_object(ch, 0);
Arathorn78c08372019-12-11 20:14:23 +01001042 }
MG Mud User88f12472016-06-24 23:31:02 +02001043 }
Zesstra137ea1c2020-08-10 02:15:20 +02001044 // ansonsten Ebene loeschen, wenn keiner zuhoert.
1045 // Kommentar: Supervisoren sind auch Zuhoerer auf der Ebene. Wenn keine
1046 // Zuhoerer mehr, folglich auch kein Supervisor mehr da.
1047 else
MG Mud User88f12472016-06-24 23:31:02 +02001048 {
Arathorn78c08372019-12-11 20:14:23 +01001049 // Der Letzte macht das Licht aus, aber nur, wenn er nicht unsichtbar ist.
Zesstra137ea1c2020-08-10 02:15:20 +02001050 // Wenn Spieler, NPC, Clone oder Channeld als letztes die Ebene verlassen,
1051 // wird diese zerstoert, mit Meldung.
Arathorn19459eb2019-11-30 00:45:51 +01001052 if (!pl->QueryProp(P_INVIS))
Arathorn78c08372019-12-11 20:14:23 +01001053 {
1054 // Die Zugriffskontrolle auf die Ebenen wird von der Funktion access()
1055 // erledigt. Weil sowohl externe Aufrufe aus dem Spielerobjekt, als auch
1056 // interne Aufrufe aus diesem Objekt vorkommen koennen, wird hier ein
1057 // explizites call_other() auf this_object() gemacht, damit der
1058 // Caller-Stack bei dem internen Aufruf denselben Aufbau hat wie bei
1059 // einem externen.
1060 this_object()->send(CMNAME, pl,
1061 "verlaesst als "+
1062 (pl->QueryProp(P_GENDER) == 1 ? "Letzter" : "Letzte")+
Zesstrab7720dc2020-08-11 22:14:18 +02001063 " die Ebene '" + ch.name + "', worauf diese sich in "
Arathorn78c08372019-12-11 20:14:23 +01001064 "einem Blitz oktarinen Lichts aufloest.", MSG_EMOTE);
1065 }
Zesstra52d5f8a2020-08-12 00:39:15 +02001066 deactivate_channel(lower_case(ch.name),0);
MG Mud User88f12472016-06-24 23:31:02 +02001067 }
Arathorn19459eb2019-11-30 00:45:51 +01001068 return (0);
MG Mud User88f12472016-06-24 23:31:02 +02001069}
1070
Arathorn78c08372019-12-11 20:14:23 +01001071// Nachricht <msg> vom Typ <type> mit Absender <pl> auf der Ebene <ch> posten,
1072// sofern <pl> dort senden darf.
Zesstrab7720dc2020-08-11 22:14:18 +02001073public varargs int send(string chname, object pl, string msg, int type)
MG Mud User88f12472016-06-24 23:31:02 +02001074{
Zesstrab7720dc2020-08-11 22:14:18 +02001075 chname = lower_case(chname);
1076 struct channel_s ch = channels[chname];
Arathorn739a4fa2020-08-06 21:52:58 +02001077 /* funcall() auf Closure-Operator, um einen neuen Eintrag im Caller Stack
1078 zu erzeugen, weil access() mit extern_call() und previous_object()
1079 arbeitet und sichergestellt sein muss, dass das in jedem Fall das
1080 richtige ist. */
1081 int a = funcall(#'access, ch, pl, C_SEND, msg);
Arathorn78c08372019-12-11 20:14:23 +01001082 if (!a)
Arathorn19459eb2019-11-30 00:45:51 +01001083 return E_ACCESS_DENIED;
1084
Zesstra26aaf1a2020-08-07 19:10:39 +02001085 // a<2 bedeutet effektiv a==1 (weil a==0 oben rausfaellt), was dem
1086 // Rueckgabewert von check_ch_access() entspricht, wenn die Aktion zugelassen
1087 // wird. access() allerdings 2 fuer "privilegierte" Objekte (z.B.
1088 // ROOT-Objekte oder den channeld selber). Der Effekt ist, dass diese
1089 // Objekte auf Ebenen senden duerfen, auf denen sie nicht zuhoeren.
Arathorn78c08372019-12-11 20:14:23 +01001090 if (a < 2 && !IsChannelMember(ch, pl))
Arathorn19459eb2019-11-30 00:45:51 +01001091 return E_NOT_MEMBER;
1092
1093 if (!msg || !stringp(msg) || !sizeof(msg))
1094 return E_EMPTY_MESSAGE;
1095
Arathorn78c08372019-12-11 20:14:23 +01001096 // Jedem Mitglied der Ebene wird die Nachricht ueber die Funktion
1097 // ChannelMessage() zugestellt. Der Channeld selbst hat ebenfalls eine
1098 // Funktion dieses Namens, so dass er, falls er Mitglied der Ebene ist, die
1099 // Nachricht ebenfalls erhaelt.
1100 // Um die Kommandos der Ebene <MasteR> verarbeiten zu koennen, muss er
1101 // demzufolge Mitglied dieser Ebene sein. Da Ebenenbesitzer automatisch
1102 // auch Mitglied sind, wird die Ebene <MasteR> im create() mittels new()
1103 // erzeugt und der Channeld als Besitzer angegeben.
1104 // Die Aufrufkette ist dann wie folgt:
1105 // Eingabe "-< xyz" => pl::ChannelParser() => send() => ChannelMessage()
Zesstrab7720dc2020-08-11 22:14:18 +02001106 (ch.members)->ChannelMessage(
1107 ({ ch.name, pl, msg, type}));
Arathorn19459eb2019-11-30 00:45:51 +01001108
Zesstrab7720dc2020-08-11 22:14:18 +02001109 if (sizeof(channelH[chname]) > MAX_HIST_SIZE)
1110 channelH[chname] = channelH[chname][1..];
Arathorn19459eb2019-11-30 00:45:51 +01001111
Zesstrab7720dc2020-08-11 22:14:18 +02001112 channelH[chname] +=
1113 ({ ({ ch.name,
Arathorn19459eb2019-11-30 00:45:51 +01001114 (stringp(pl)
1115 ? pl
1116 : (pl->QueryProp(P_INVIS)
1117 ? "/(" + capitalize(getuid(pl)) + ")$"
1118 : "")
1119 + (pl->Name(WER, 2) || "<Unbekannt>")),
1120 msg + " <" + strftime("%a, %H:%M:%S") + ">\n",
1121 type }) });
Arathorn78c08372019-12-11 20:14:23 +01001122 return (0);
MG Mud User88f12472016-06-24 23:31:02 +02001123}
1124
Arathorn78c08372019-12-11 20:14:23 +01001125// Gibt ein Mapping mit allen Ebenen aus, die das Objekt <pl> lesen kann,
1126// oder einen Integer-Fehlercode
1127public int|mapping list(object pl)
MG Mud User88f12472016-06-24 23:31:02 +02001128{
Arathorn78c08372019-12-11 20:14:23 +01001129 mapping chs = ([]);
Zesstrab7720dc2020-08-11 22:14:18 +02001130 foreach(string chname, struct channel_s ch : channels)
Arathorn78c08372019-12-11 20:14:23 +01001131 {
Arathorn739a4fa2020-08-06 21:52:58 +02001132 /* funcall() auf Closure-Operator, um einen neuen Eintrag im Caller Stack
1133 zu erzeugen, weil access() mit extern_call() und previous_object()
1134 arbeitet und sichergestellt sein muss, dass das in jedem Fall das
1135 richtige ist. */
Zesstrab7720dc2020-08-11 22:14:18 +02001136 if(funcall(#'access, ch, pl, C_LIST))
Arathorn78c08372019-12-11 20:14:23 +01001137 {
Zesstrab7720dc2020-08-11 22:14:18 +02001138 ch.members = filter(ch.members, #'objectp);
1139 m_add(chs, chname, ({ch.members, ch.access_cl, ch.desc,
1140 ch.supervisor, ch.name }) );
Arathorn78c08372019-12-11 20:14:23 +01001141 }
1142 }
Arathorn19459eb2019-11-30 00:45:51 +01001143
1144 if (!sizeof(chs))
1145 return E_ACCESS_DENIED;
Arathorn78c08372019-12-11 20:14:23 +01001146 return (chs);
MG Mud User88f12472016-06-24 23:31:02 +02001147}
1148
Arathorn78c08372019-12-11 20:14:23 +01001149// Ebene suchen, deren Name <ch> enthaelt, und auf der Objekt <pl> senden darf
1150// Rueckgabewerte:
1151// - den gefundenen Namen als String
1152// - String-Array, wenn es mehrere Treffer gibt
1153// - 0, wenn es keinen Treffer gibt
Zesstrab7720dc2020-08-11 22:14:18 +02001154public string|string* find(string chname, object pl)
MG Mud User88f12472016-06-24 23:31:02 +02001155{
Zesstrab7720dc2020-08-11 22:14:18 +02001156 chname = lower_case(chname);
Arathorn19459eb2019-11-30 00:45:51 +01001157
Arathorn78c08372019-12-11 20:14:23 +01001158 // Suchstring <ch> muss Formatanforderung erfuellen;
1159 // TODO: soll das ein Check auf gueltigen Ebenennamen als Input sein?
1160 // Wenn ja, muesste laut Manpage mehr geprueft werden:
1161 // "Gueltige Namen setzen sich zusammen aus den Buchstaben a-z, A-Z sowie
1162 // #$%&@<>-." Es wuerden also $%&@ fehlen.
Zesstrab7720dc2020-08-11 22:14:18 +02001163 if (!regmatch(chname, "^[<>a-z0-9#-]+$"))
Arathorn78c08372019-12-11 20:14:23 +01001164 return 0;
Arathorn19459eb2019-11-30 00:45:51 +01001165
Arathorn78c08372019-12-11 20:14:23 +01001166 // Der Anfang des Ebenennamens muss dem Suchstring entsprechen und das
1167 // Objekt <pl> muss auf dieser Ebene senden duerfen, damit der Ebenenname
1168 // in das Suchergebnis aufgenommen wird.
Zesstrab7720dc2020-08-11 22:14:18 +02001169 string* chs = filter(m_indices(channels), function int (string ch_n) {
Arathorn739a4fa2020-08-06 21:52:58 +02001170 /* funcall() auf Closure-Operator, um einen neuen Eintrag
1171 im Caller Stack zu erzeugen, weil access() mit
1172 extern_call() und previous_object() arbeitet und
1173 sichergestellt sein muss, dass das in jedem Fall das
1174 richtige ist. */
Zesstrab7720dc2020-08-11 22:14:18 +02001175 return ( stringp(regmatch(ch_n, "^"+chname)) &&
1176 funcall(#'access, channels[ch_n], pl, C_SEND) );
Arathorn78c08372019-12-11 20:14:23 +01001177 });
Arathorn19459eb2019-11-30 00:45:51 +01001178
Arathorn78c08372019-12-11 20:14:23 +01001179 int num_channels = sizeof(chs);
1180 if (num_channels > 1)
1181 return chs;
1182 else if (num_channels == 1)
Zesstrab7720dc2020-08-11 22:14:18 +02001183 return chs[0];
Arathorn78c08372019-12-11 20:14:23 +01001184 else
1185 return 0;
MG Mud User88f12472016-06-24 23:31:02 +02001186}
1187
Arathorn78c08372019-12-11 20:14:23 +01001188// Ebenen-History abfragen.
Zesstrab7720dc2020-08-11 22:14:18 +02001189public int|<int|string>** history(string chname, object pl)
MG Mud User88f12472016-06-24 23:31:02 +02001190{
Zesstrab7720dc2020-08-11 22:14:18 +02001191 struct channel_s ch = channels[lower_case(chname)];
Arathorn739a4fa2020-08-06 21:52:58 +02001192 /* funcall() auf Closure-Operator, um einen neuen Eintrag im Caller Stack
1193 zu erzeugen, weil access() mit extern_call() und previous_object()
1194 arbeitet und sichergestellt sein muss, dass das in jedem Fall das
1195 richtige ist. */
1196 if (!funcall(#'access, ch, pl, C_JOIN))
MG Mud User88f12472016-06-24 23:31:02 +02001197 return E_ACCESS_DENIED;
Arathorn78c08372019-12-11 20:14:23 +01001198 else
Zesstrab7720dc2020-08-11 22:14:18 +02001199 return channelH[chname];
MG Mud User88f12472016-06-24 23:31:02 +02001200}
1201
Arathorn78c08372019-12-11 20:14:23 +01001202// Wird aus der Shell gerufen, fuer das Erzmagier-Kommando "kill".
Zesstrab7720dc2020-08-11 22:14:18 +02001203public int remove_channel(string chname, object pl)
MG Mud User88f12472016-06-24 23:31:02 +02001204{
Zesstra26aaf1a2020-08-07 19:10:39 +02001205 //TODO: integrieren in access()?
Arathorn19459eb2019-11-30 00:45:51 +01001206 if (previous_object() != this_object())
1207 {
Zesstrab7720dc2020-08-11 22:14:18 +02001208 if (!stringp(chname) ||
Arathorn19459eb2019-11-30 00:45:51 +01001209 pl != this_player() || this_player() != this_interactive() ||
1210 this_interactive() != previous_object() ||
1211 !IS_ARCH(this_interactive()))
MG Mud User88f12472016-06-24 23:31:02 +02001212 return E_ACCESS_DENIED;
Arathorn19459eb2019-11-30 00:45:51 +01001213 }
Zesstra52d5f8a2020-08-12 00:39:15 +02001214
1215 delete_channel(lower_case(chname), 1);
Arathorn19459eb2019-11-30 00:45:51 +01001216
Arathorn19459eb2019-11-30 00:45:51 +01001217 return (0);
MG Mud User88f12472016-06-24 23:31:02 +02001218}
1219
Arathorn78c08372019-12-11 20:14:23 +01001220// Wird aus der Shell aufgerufen, fuer das Erzmagier-Kommando "clear".
Zesstrab7720dc2020-08-11 22:14:18 +02001221public int clear_history(string chname)
MG Mud User88f12472016-06-24 23:31:02 +02001222{
Zesstra26aaf1a2020-08-07 19:10:39 +02001223 //TODO: mit access() vereinigen?
MG Mud User88f12472016-06-24 23:31:02 +02001224 // Sicherheitsabfragen
Arathorn19459eb2019-11-30 00:45:51 +01001225 if (previous_object() != this_object())
1226 {
Zesstrab7720dc2020-08-11 22:14:18 +02001227 if (!stringp(chname) ||
Arathorn19459eb2019-11-30 00:45:51 +01001228 this_player() != this_interactive() ||
1229 this_interactive() != previous_object() ||
1230 !IS_ARCH(this_interactive()))
MG Mud User88f12472016-06-24 23:31:02 +02001231 return E_ACCESS_DENIED;
Arathorn19459eb2019-11-30 00:45:51 +01001232 }
Zesstrab7720dc2020-08-11 22:14:18 +02001233 chname=lower_case(chname);
Zesstra26aaf1a2020-08-07 19:10:39 +02001234 // History des Channels loeschen (ohne die ebene als ganzes, daher Key nicht
1235 // aus dem mapping loeschen.)
Zesstrab7720dc2020-08-11 22:14:18 +02001236 if (pointerp(channelH[chname]))
1237 channelH[chname] = ({});
MG Mud User88f12472016-06-24 23:31:02 +02001238
1239 return 0;
1240}