blob: a0e0768f7ea37f6bd905d0f84c490f4b12be70e7 [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>
15
16#include <properties.h>
17#include <config.h>
18#include <language.h>
19
20#define NEED_PROTOTYPES
21#include "channel.h"
22
Arathorn19459eb2019-11-30 00:45:51 +010023#define CMNAME "<MasteR>"
24#define CHANNEL_SAVE "/p/daemon/save/channeld"
25#define MEMORY "/secure/memory"
26#define TIMEOUT (time() - 60)
27#define CMDS ({C_FIND, C_LIST, C_JOIN, C_LEAVE, C_SEND, C_NEW})
MG Mud User88f12472016-06-24 23:31:02 +020028
Arathorn19459eb2019-11-30 00:45:51 +010029/* list of channels and their corresponding data (members, etc.)
30 channels = ([string channelname : ({ ({object* members}),
31 closure access_rights,
32 string channel_info,
33 string|object master_object,
34 string channelname }) ]) */
MG Mud User88f12472016-06-24 23:31:02 +020035private nosave mapping channels;
Arathorn19459eb2019-11-30 00:45:51 +010036//private nosave mapping lowerch; // unused
37
38/* channel history
39 mapping channelH = ([ string channelname : ({ string channelname,
40 string sender,
41 string msg,
42 int msg_type }) ]) */
MG Mud User88f12472016-06-24 23:31:02 +020043private nosave mapping channelH;
Arathorn19459eb2019-11-30 00:45:51 +010044
45/* list of global channelmaster stats
46 mapping stats: ([ "time" : int object_time(),
47 "boot" : string load_name(previous_object()),
48 "new" : int total_channels_created,
49 "disposed" : int total_channels_removed ]) */
MG Mud User88f12472016-06-24 23:31:02 +020050private nosave mapping stats;
51
Arathorn19459eb2019-11-30 00:45:51 +010052/* channel cache
53 mapping channelC = ([ string channelname : ({ string I_NAME,
54 string I_INFO,
55 int time() }) ]) */
MG Mud User88f12472016-06-24 23:31:02 +020056private mapping channelC;
Arathorn19459eb2019-11-30 00:45:51 +010057
58/* list of players' banned commands, if any
59 mapping channelB = ([ string playername : ({ string* banned_commands })])*/
MG Mud User88f12472016-06-24 23:31:02 +020060private mapping channelB;
61
Arathorn19459eb2019-11-30 00:45:51 +010062/* timeout cache for player commands (timeout = 60 s, see above)
63 ensures that the same command is only executed once per minute, max
64 mapping Tcmd = ([ "lag": int timestamp,
65 "uptime": int timestamp,
66 "statistik": int timestamp]) */
67private mapping Tcmd = ([]);
68
69/* Flag to indicate that data changes have occurred and that we need saving
70 in the next save_object() run.
71 set to 0 or 1 */
MG Mud User88f12472016-06-24 23:31:02 +020072private int save_me_soon;
73
Arathorn19459eb2019-11-30 00:45:51 +010074
MG Mud User88f12472016-06-24 23:31:02 +020075// BEGIN OF THE CHANNEL MASTER ADMINISTRATIVE PART
76
77#define RECV 0
78#define SEND 1
79#define FLAG 2
80
81// Channel flags
82// Levelbeschraenkungen gegen Magierlevel (query_wiz_level) pruefen, nicht
83// P_LEVEL.
84#define F_WIZARD 1
85// Keine Gaeste. ;-)
86#define F_NOGUEST 2
87
88private nosave mapping admin = m_allocate(0, 3);
89
90int check(string ch, object pl, string cmd)
91{
92 int level;
MG Mud User88f12472016-06-24 23:31:02 +020093
Arathorn19459eb2019-11-30 00:45:51 +010094 if ((admin[ch, FLAG] & F_NOGUEST) && pl->QueryGuest())
95 return 0;
96
97 if ((admin[ch, FLAG] & F_WIZARD) && query_wiz_level(pl) < SEER_LVL)
98 return 0;
99
MG Mud User88f12472016-06-24 23:31:02 +0200100 level = (admin[ch, FLAG] & F_WIZARD
Arathorn19459eb2019-11-30 00:45:51 +0100101 ? query_wiz_level(pl)
102 : pl->QueryProp(P_LEVEL));
MG Mud User88f12472016-06-24 23:31:02 +0200103
Arathorn19459eb2019-11-30 00:45:51 +0100104 switch (cmd)
MG Mud User88f12472016-06-24 23:31:02 +0200105 {
Arathorn19459eb2019-11-30 00:45:51 +0100106 case C_FIND:
107 case C_LIST:
108 case C_JOIN:
109 if (admin[ch, RECV] == -1)
110 return 0;
111 if (admin[ch, RECV] <= level)
112 return 1;
113 break;
114
115 case C_SEND:
116 if (admin[ch, SEND] == -1)
117 return 0;
118 if (admin[ch, SEND] <= level)
119 return 1;
120 break;
121
122 case C_LEAVE:
123 return 1;
124
125 default:
126 break;
MG Mud User88f12472016-06-24 23:31:02 +0200127 }
Arathorn19459eb2019-11-30 00:45:51 +0100128 return (0);
MG Mud User88f12472016-06-24 23:31:02 +0200129}
130
131private int CountUser(mapping l)
132{
Arathorn5ea15362018-11-15 22:22:05 +0100133 mapping n = ([]);
Arathorn19459eb2019-11-30 00:45:51 +0100134 walk_mapping(l, function void (string chan_name, mixed * chan_data)
135 {
136 n += mkmapping(chan_data[I_MEMBER]);
137 });
MG Mud User88f12472016-06-24 23:31:02 +0200138 return sizeof(n);
139}
140
Arathorn19459eb2019-11-30 00:45:51 +0100141private void banned(string n, string* cmds, string res)
MG Mud User88f12472016-06-24 23:31:02 +0200142{
143 res += sprintf("%s [%s], ", capitalize(n), implode(cmds, ","));
144}
145
MG Mud User88f12472016-06-24 23:31:02 +0200146void ChannelMessage(mixed msg)
147{
148 string ret, mesg;
149 mixed lag;
150 int max, rekord;
151 string tmp;
MG Mud User88f12472016-06-24 23:31:02 +0200152
Arathorn19459eb2019-11-30 00:45:51 +0100153 if (msg[1] == this_object() || !stringp(msg[2]) ||
154 msg[0] != CMNAME || previous_object() != this_object())
155 return;
MG Mud User88f12472016-06-24 23:31:02 +0200156
157 mesg = lower_case(msg[2]);
158
Arathorn19459eb2019-11-30 00:45:51 +0100159 if (!strstr("hilfe", mesg) && sizeof(mesg) <= 5)
MG Mud User88f12472016-06-24 23:31:02 +0200160 {
Arathorn19459eb2019-11-30 00:45:51 +0100161 ret = "Folgende Kommandos gibt es: hilfe, lag, up[time], statistik, bann";
162 }
163 else if (!strstr("lag", mesg) && sizeof(mesg) <= 3)
164 {
165 // TODO: invert logic to make this better understandable, e.g.
166 // if ( CMD_AVAILABLE("lag") ) and rearrange code block
167 if (Tcmd["lag"] > TIMEOUT)
168 return;
169
MG Mud User88f12472016-06-24 23:31:02 +0200170 Tcmd["lag"] = time();
171 lag = "/p/daemon/lag-o-daemon"->read_ext_lag_data();
172 ret = sprintf("Lag: %.1f%%/60, %.1f%%/15, %.1f%%/5, %.1f%%/1, "
Arathorn19459eb2019-11-30 00:45:51 +0100173 "%.1f%%/20s, %.1f%%/2s",
174 lag[5], lag[4], lag[3], lag[2], lag[1], lag[0]);
175 call_out(#'send, 2, CMNAME, this_object(), ret);
MG Mud User88f12472016-06-24 23:31:02 +0200176 ret = query_load_average();
Arathorn19459eb2019-11-30 00:45:51 +0100177 }
178 // TODO: move this logic to a function with a self-explanatory name
179 else if (!strstr("uptime", mesg) && sizeof(mesg) <= 6)
MG Mud User88f12472016-06-24 23:31:02 +0200180 {
Arathorn19459eb2019-11-30 00:45:51 +0100181 if (Tcmd["uptime"] > TIMEOUT)
182 return;
183
MG Mud User88f12472016-06-24 23:31:02 +0200184 Tcmd["uptime"] = time();
Arathorn19459eb2019-11-30 00:45:51 +0100185
186 if (file_size("/etc/maxusers") <= 0)
187 {
MG Mud User88f12472016-06-24 23:31:02 +0200188 ret = "Diese Information liegt nicht vor.";
Arathorn19459eb2019-11-30 00:45:51 +0100189 }
190 else
191 {
MG Mud User88f12472016-06-24 23:31:02 +0200192 sscanf(read_file("/etc/maxusers"), "%d %s", max, tmp);
193 sscanf(read_file("/etc/maxusers.ever"), "%d %s", rekord, tmp);
194 ret = sprintf("Das MUD laeuft jetzt %s. Es sind momentan %d Spieler "
Arathorn19459eb2019-11-30 00:45:51 +0100195 "eingeloggt; das Maximum lag heute bei %d und der Rekord "
196 "bisher ist %d.", uptime(), sizeof(users()), max, rekord);
MG Mud User88f12472016-06-24 23:31:02 +0200197 }
Arathorn19459eb2019-11-30 00:45:51 +0100198 }
199 else if (!strstr("statistik", mesg) && sizeof(mesg) <= 9)
MG Mud User88f12472016-06-24 23:31:02 +0200200 {
Arathorn19459eb2019-11-30 00:45:51 +0100201 if (Tcmd["statistik"] > TIMEOUT)
202 return;
203
MG Mud User88f12472016-06-24 23:31:02 +0200204 Tcmd["statistik"] = time();
205 ret = sprintf(
Arathorn19459eb2019-11-30 00:45:51 +0100206 "Im Moment sind insgesamt %d Ebenen mit %d Teilnehmern aktiv.\n"
207 "Der %s wurde das letzte mal am %s von %s neu gestartet.\n"
208 "Seitdem wurden %d Ebenen neu erzeugt und %d zerstoert.\n",
209 sizeof(channels), CountUser(channels), CMNAME,
210 dtime(stats["time"]), stats["boot"], stats["new"], stats["dispose"]);
211 }
212 else if (!strstr(mesg, "bann"))
MG Mud User88f12472016-06-24 23:31:02 +0200213 {
214 string pl, cmd;
Arathorn19459eb2019-11-30 00:45:51 +0100215
216 if (mesg == "bann")
217 {
218 if (sizeof(channelB))
MG Mud User88f12472016-06-24 23:31:02 +0200219 {
220 ret = "";
Arathorn19459eb2019-11-30 00:45:51 +0100221 walk_mapping(channelB, #'banned, &ret);
MG Mud User88f12472016-06-24 23:31:02 +0200222 ret = "Fuer folgende Spieler besteht ein Bann: " + ret;
MG Mud User88f12472016-06-24 23:31:02 +0200223 }
224 else
225 {
Arathorn19459eb2019-11-30 00:45:51 +0100226 ret = "Zur Zeit ist kein Bann aktiv.";
227 }
228 }
229 else
230 {
231 if (sscanf(mesg, "bann %s %s", pl, cmd) == 2 && IS_DEPUTY(msg[1]))
232 {
233 pl = lower_case(pl);
234 cmd = lower_case(cmd);
235
236 if (member(CMDS, cmd) != -1)
237 {
238 if (!pointerp(channelB[pl]))
239 channelB[pl] = ({});
240
241 if (member(channelB[pl], cmd) != -1)
242 channelB[pl] -= ({ cmd });
243 else
244 channelB[pl] += ({ cmd });
245 ret = "Fuer '" + capitalize(pl) + "' besteht "
246 + (sizeof(channelB[pl]) ?
247 "folgender Bann: " + implode(channelB[pl], ", ") :
248 "kein Bann mehr.");
249
250 if (!sizeof(channelB[pl]))
251 channelB = m_copy_delete(channelB, pl);
252
253 save_object(CHANNEL_SAVE);
254 }
255 else
256 {
257 ret = "Das Kommando '" + cmd + "' ist unbekannt. "
258 "Erlaubte Kommandos: "+ implode(CMDS, ", ");
259 }
260 }
261 else
262 {
263 if (!IS_ARCH(msg[1]))
264 return;
265 else
266 ret = "Syntax: bann <name> <kommando>";
MG Mud User88f12472016-06-24 23:31:02 +0200267 }
268 }
269 }
Arathorn19459eb2019-11-30 00:45:51 +0100270 else if (mesg == "lust")
MG Mud User88f12472016-06-24 23:31:02 +0200271 {
272 mixed t, up;
Arathorn19459eb2019-11-30 00:45:51 +0100273
274 if (Tcmd["lag"] > TIMEOUT ||
275 Tcmd["statistik"] > TIMEOUT ||
276 Tcmd["uptime"] > TIMEOUT)
277 return;
278
MG Mud User88f12472016-06-24 23:31:02 +0200279 Tcmd["lag"] = time();
280 Tcmd["statistik"] = time();
281 Tcmd["uptime"] = time();
282 lag = "/p/daemon/lag-o-daemon"->read_lag_data();
MG Mud User88f12472016-06-24 23:31:02 +0200283 sscanf(read_file("/etc/maxusers"), "%d %s", max, tmp);
284 sscanf(read_file("/etc/maxusers.ever"), "%d %s", rekord, tmp);
Arathorn19459eb2019-11-30 00:45:51 +0100285 t = time() - last_reboot_time();
286 up = "";
MG Mud User88f12472016-06-24 23:31:02 +0200287
Arathorn19459eb2019-11-30 00:45:51 +0100288 if (t >= 86400)
289 up += sprintf("%dT", t / 86400);
MG Mud User88f12472016-06-24 23:31:02 +0200290
Arathorn19459eb2019-11-30 00:45:51 +0100291 if (t >= 3600)
292 up += sprintf("%dh", (t = t % 86400) / 3600);
293
294 if (t > 60)
295 up += sprintf("%dm", (t = t % 3600) / 60);
296
297 up += sprintf("%ds", t % 60);
MG Mud User88f12472016-06-24 23:31:02 +0200298 ret = sprintf("%.1f%%/15 %.1f%%/1 %s %d:%d:%d E:%d T:%d",
Arathorn19459eb2019-11-30 00:45:51 +0100299 lag[1], lag[2], up, sizeof(users()), max, rekord,
300 sizeof(channels), CountUser(channels));
301 }
302 else
303 {
304 return;
305 }
MG Mud User88f12472016-06-24 23:31:02 +0200306
Arathorn19459eb2019-11-30 00:45:51 +0100307 call_out(#'send, 2, CMNAME, this_object(), ret);
MG Mud User88f12472016-06-24 23:31:02 +0200308}
309
310// setup() -- set up a channel and register it
311// arguments are stored in the following order:
312// ({ channel name,
313// receive level, send level,
314// flags,
315// description,
316// master obj
317// })
318private void setup(mixed c)
319{
320 closure cl;
321 object m;
322 string d;
323 d = "- Keine Beschreibung -";
324 m = this_object();
Arathorn19459eb2019-11-30 00:45:51 +0100325
326 if (sizeof(c) && sizeof(c[0]) > 1 && c[0][0] == '\\')
MG Mud User88f12472016-06-24 23:31:02 +0200327 c[0] = c[0][1..];
328
Arathorn19459eb2019-11-30 00:45:51 +0100329 switch (sizeof(c))
MG Mud User88f12472016-06-24 23:31:02 +0200330 {
Arathorn19459eb2019-11-30 00:45:51 +0100331 case 6:
332 if (!stringp(c[5]) || !sizeof(c[5]) ||
333 (catch(m = load_object(c[5]); publish) ||
334 !objectp(m)))
335 m = this_object();
336
337 case 5:
338 d = stringp(c[4]) || closurep(c[4]) ? c[4] : d;
339
340 case 4:
341 admin[c[0], FLAG] = to_int(c[3]);
342
343 case 3:
344 admin[c[0], SEND] = to_int(c[2]);
345
346 case 2:
347 admin[c[0], RECV] = to_int(c[1]);
348 break;
349
350 case 0:
351 default:
352 return;
MG Mud User88f12472016-06-24 23:31:02 +0200353 }
Arathorn19459eb2019-11-30 00:45:51 +0100354
355 switch (new(c[0], m, d))
MG Mud User88f12472016-06-24 23:31:02 +0200356 {
Arathorn19459eb2019-11-30 00:45:51 +0100357 case E_ACCESS_DENIED:
358 log_file("CHANNEL", sprintf("[%s] %s: %O: error, access denied\n",
359 dtime(time()), c[0], m));
360 break;
361
362 default:
363 break;
MG Mud User88f12472016-06-24 23:31:02 +0200364 }
Arathorn19459eb2019-11-30 00:45:51 +0100365
MG Mud User88f12472016-06-24 23:31:02 +0200366 return;
367}
368
369void initialize()
370{
371 mixed tmp;
Zesstra@Morgengrauen3b569c82016-07-18 20:22:08 +0200372#if !defined(__TESTMUD__) && MUDNAME=="MorgenGrauen"
Arathorn19459eb2019-11-30 00:45:51 +0100373 tmp = read_file(object_name(this_object()) + ".init");
Zesstra@Morgengrauen3b569c82016-07-18 20:22:08 +0200374#else
Arathorn19459eb2019-11-30 00:45:51 +0100375 tmp = read_file(object_name(this_object()) + ".init.testmud");
Zesstra@Morgengrauen3b569c82016-07-18 20:22:08 +0200376#endif
Arathorn19459eb2019-11-30 00:45:51 +0100377
Zesstra@Morgengrauen2b229372016-07-20 23:59:54 +0200378 if (!stringp(tmp))
379 return;
Arathorn19459eb2019-11-30 00:45:51 +0100380
MG Mud User88f12472016-06-24 23:31:02 +0200381 tmp = regexp(old_explode(tmp, "\n"), "^[^#]");
Arathorn19459eb2019-11-30 00:45:51 +0100382 tmp = map(tmp, #'regexplode, "[^:][^:]*$|[ \\t]*:[ \\t]*");
383 tmp = map(tmp, #'regexp, "^[^: \\t]");
384 map(tmp, #'setup);
MG Mud User88f12472016-06-24 23:31:02 +0200385}
386
Arathorn19459eb2019-11-30 00:45:51 +0100387 // BEGIN OF THE CHANNEL MASTER IMPLEMENTATION
MG Mud User88f12472016-06-24 23:31:02 +0200388
Zesstra@Morgengrauen2b229372016-07-20 23:59:54 +0200389protected void create()
MG Mud User88f12472016-06-24 23:31:02 +0200390{
391 seteuid(getuid());
392 restore_object(CHANNEL_SAVE);
Arathorn19459eb2019-11-30 00:45:51 +0100393
394 if (!channelC)
395 channelC = ([]);
396
397 if (!channelB)
398 channelB = ([]);
399
MG Mud User88f12472016-06-24 23:31:02 +0200400 channels = ([]);
401
Arathorn19459eb2019-11-30 00:45:51 +0100402 /* Die Channel-History wird nicht nur lokal sondern auch noch im Memory
403 gespeichert, dadurch bleibt sie auch ueber ein Reload erhalten.
MG Mud User88f12472016-06-24 23:31:02 +0200404 Der folgende Code versucht, den Zeiger aus dem Memory zu holen. Falls
405 das nicht moeglich ist, wird ein neuer erzeugt und gegebenenfalls im
406 Memory abgelegt. */
407
408 // Hab ich die noetigen Rechte im Memory?
Arathorn19459eb2019-11-30 00:45:51 +0100409 if (call_other(MEMORY, "HaveRights"))
410 {
MG Mud User88f12472016-06-24 23:31:02 +0200411 // Objektpointer laden
Arathorn19459eb2019-11-30 00:45:51 +0100412 channelH = (mixed) call_other(MEMORY, "Load", "History");
MG Mud User88f12472016-06-24 23:31:02 +0200413
414 // Wenns nich geklappt hat, hat der Memory noch keinen Zeiger, dann
Arathorn19459eb2019-11-30 00:45:51 +0100415 if (!mappingp(channelH)) {
MG Mud User88f12472016-06-24 23:31:02 +0200416 // Zeiger erzeugen
417 channelH = ([]);
418 // und in den Memory schreiben
Arathorn19459eb2019-11-30 00:45:51 +0100419 call_other(MEMORY, "Save", "History", channelH);
MG Mud User88f12472016-06-24 23:31:02 +0200420 }
Arathorn19459eb2019-11-30 00:45:51 +0100421 }
422 else
423 {
MG Mud User88f12472016-06-24 23:31:02 +0200424 // Keine Rechte im Memory, dann wird mit einem lokalen Zeiger gearbeitet.
425 channelH = ([]);
426 }
427
428 stats = (["time": time(),
Arathorn19459eb2019-11-30 00:45:51 +0100429 "boot": capitalize(getuid(previous_object()) || "<Unbekannt>")]);
430 new (CMNAME, this_object(), "Zentrale Informationen zu den Ebenen");
MG Mud User88f12472016-06-24 23:31:02 +0200431 initialize();
432 map_objects(efun::users(), "RegisterChannels");
433 this_object()->send(CMNAME, this_object(),
Arathorn19459eb2019-11-30 00:45:51 +0100434 sprintf("%d Ebenen mit %d Teilnehmern initialisiert.",
435 sizeof(channels),
436 CountUser(channels)));
MG Mud User88f12472016-06-24 23:31:02 +0200437}
438
439// reset() and cache_to() - Cache Timeout, remove timed out cached channels
440// SEE: new, send
441private int cache_to(string key, mapping m, int t)
442{
Arathorn19459eb2019-11-30 00:45:51 +0100443 if (!pointerp(m[key]) || m[key][2] + 43200 > t)
444 return 1;
445
446 return (0);
MG Mud User88f12472016-06-24 23:31:02 +0200447}
448
449varargs void reset(int nonstd)
450{
Arathorn19459eb2019-11-30 00:45:51 +0100451 channelC = filter_indices(channelC, #'cache_to, channelC, time());
452
MG Mud User88f12472016-06-24 23:31:02 +0200453 if (save_me_soon)
454 {
Arathorn19459eb2019-11-30 00:45:51 +0100455 save_me_soon = 0;
MG Mud User88f12472016-06-24 23:31:02 +0200456 save_object(CHANNEL_SAVE);
457 }
458}
459
460// name() - define the name of this object.
Arathorn19459eb2019-11-30 00:45:51 +0100461string name()
462{
463 return CMNAME;
464}
465
466string Name()
467{
468 return CMNAME;
469}
MG Mud User88f12472016-06-24 23:31:02 +0200470
471// access() - check access by looking for the right argument types and
472// calling access closures respectively
473// SEE: new, join, leave, send, list, users
474// Note: <pl> is usually an object, only the master supplies a string during
475// runtime error handling.
476varargs private int access(mixed ch, mixed pl, string cmd, string txt)
477{
478 mixed co, m;
479
Arathorn19459eb2019-11-30 00:45:51 +0100480 if (!stringp(ch) || !sizeof(ch = lower_case(ch)) || !channels[ch])
MG Mud User88f12472016-06-24 23:31:02 +0200481 return 0;
Arathorn19459eb2019-11-30 00:45:51 +0100482
483 if (!channels[ch][I_ACCESS] || !previous_object(1) || !extern_call() ||
484 previous_object(1) == this_object() ||
485 (stringp(channels[ch][I_MASTER]) &&
486 previous_object(1) == find_object(channels[ch][I_MASTER])) ||
487 getuid(previous_object(1)) == ROOTID)
MG Mud User88f12472016-06-24 23:31:02 +0200488 return 2;
Arathorn19459eb2019-11-30 00:45:51 +0100489
490 if (!objectp(pl) ||
491 ((previous_object(1) != pl) && (previous_object(1) != this_object())))
MG Mud User88f12472016-06-24 23:31:02 +0200492 return 0;
Arathorn19459eb2019-11-30 00:45:51 +0100493
494 if (pointerp(channelB[getuid(pl)]) &&
495 member(channelB[getuid(pl)], cmd) != -1)
496 return 0;
497
498 if (stringp(channels[ch][I_MASTER]) &&
499 (!(m = find_object(channels[ch][I_MASTER])) ||
500 (!to_object(channels[ch][I_ACCESS]) ||
501 get_type_info(channels[ch][I_ACCESS])[1])))
MG Mud User88f12472016-06-24 23:31:02 +0200502 {
503 string err;
Arathorn19459eb2019-11-30 00:45:51 +0100504
505 if (!objectp(m))
506 err = catch(load_object(channels[ch][I_MASTER]); publish);
507
508 if (!err &&
509 ((!to_object(channels[ch][I_ACCESS]) ||
510 get_type_info(channels[ch][I_ACCESS])[1]) &&
511 !closurep(channels[ch][I_ACCESS] =
512 symbol_function("check", find_object(channels[ch][I_MASTER])))))
MG Mud User88f12472016-06-24 23:31:02 +0200513 {
514 log_file("CHANNEL", sprintf("[%s] %O -> %O\n",
Arathorn19459eb2019-11-30 00:45:51 +0100515 dtime(time()), channels[ch][I_MASTER],
516 err));
MG Mud User88f12472016-06-24 23:31:02 +0200517 channels = m_copy_delete(channels, ch);
518 return 0;
519 }
Arathorn19459eb2019-11-30 00:45:51 +0100520
MG Mud User88f12472016-06-24 23:31:02 +0200521 this_object()->join(ch, find_object(channels[ch][I_MASTER]));
522 }
Arathorn19459eb2019-11-30 00:45:51 +0100523
524 if (closurep(channels[ch][I_ACCESS]))
525 return funcall(channels[ch][I_ACCESS],
526 channels[ch][I_NAME], pl, cmd, &txt);
MG Mud User88f12472016-06-24 23:31:02 +0200527}
528
529// new() - create a new channel
530// a channel with name 'ch' is created, the player is the master
531// info may contain a string which describes the channel or a closure
532// to display up-to-date information, check may contain a closure
533// called when a join/leave/send/list/users message is received
534// SEE: access
MG Mud User88f12472016-06-24 23:31:02 +0200535#define IGNORE "^/xx"
536
537varargs int new(string ch, object pl, mixed info)
538{
539 mixed pls;
540
Arathorn19459eb2019-11-30 00:45:51 +0100541 if (!objectp(pl) || !stringp(ch) || !sizeof(ch) ||
542 channels[lower_case(ch)] || (pl == this_object() && extern_call()) ||
543 sizeof(channels) >= MAX_CHANNELS ||
544 sizeof(regexp(({ object_name(pl) }), IGNORE)) ||
545 (pointerp(channelB[getuid(pl)]) &&
MG Mud User88f12472016-06-24 23:31:02 +0200546 member(channelB[getuid(pl)], C_NEW) != -1))
547 return E_ACCESS_DENIED;
548
Arathorn19459eb2019-11-30 00:45:51 +0100549 if (!info)
550 {
551 if (channelC[lower_case(ch)])
552 {
MG Mud User88f12472016-06-24 23:31:02 +0200553 ch = channelC[lower_case(ch)][0];
554 info = channelC[lower_case(ch)][1];
555 }
Arathorn19459eb2019-11-30 00:45:51 +0100556 else
557 {
558 return E_ACCESS_DENIED;
559 }
MG Mud User88f12472016-06-24 23:31:02 +0200560 }
Arathorn19459eb2019-11-30 00:45:51 +0100561 else
562 {
563 channelC[lower_case(ch)] = ({ ch, info, time() });
564 }
MG Mud User88f12472016-06-24 23:31:02 +0200565
566 pls = ({ pl });
Arathorn19459eb2019-11-30 00:45:51 +0100567 channels[lower_case(ch)] = ({
568 pls,
569 symbol_function("check", pl) || #'check, info,
570 (!living(pl) && !clonep(pl) && pl != this_object()
571 ? object_name(pl)
572 : pl),
573 ch});
MG Mud User88f12472016-06-24 23:31:02 +0200574
575 // ChannelH fuer einen Kanal nur dann initialisieren, wenn es sie noch nich gibt.
Arathorn19459eb2019-11-30 00:45:51 +0100576 if (!pointerp(channelH[lower_case(ch)]))
577 channelH[lower_case(ch)] = ({});
MG Mud User88f12472016-06-24 23:31:02 +0200578
Arathorn19459eb2019-11-30 00:45:51 +0100579 if (pl != this_object())
MG Mud User88f12472016-06-24 23:31:02 +0200580 log_file("CHANNEL.new", sprintf("[%s] %O: %O %O\n",
Arathorn19459eb2019-11-30 00:45:51 +0100581 dtime(time()), ch, pl, info));
582
583 if (!pl->QueryProp(P_INVIS))
MG Mud User88f12472016-06-24 23:31:02 +0200584 this_object()->send(CMNAME, pl,
Arathorn19459eb2019-11-30 00:45:51 +0100585 "laesst die Ebene '" + ch + "' entstehen.", MSG_EMOTE);
586
MG Mud User88f12472016-06-24 23:31:02 +0200587 stats["new"]++;
Arathorn19459eb2019-11-30 00:45:51 +0100588 save_me_soon = 1;
589 return (0);
MG Mud User88f12472016-06-24 23:31:02 +0200590}
591
592// join() - join a channel
593// this function checks whether the player 'pl' is allowed to join
594// the channel 'ch' and add if successful, one cannot join a channel
595// twice
596// SEE: leave, access
597int join(string ch, object pl)
598{
Arathorn19459eb2019-11-30 00:45:51 +0100599 if (!funcall(#'access,&ch, pl, C_JOIN))
600 return E_ACCESS_DENIED;
601
602 if (member(channels[ch][I_MEMBER], pl) != -1)
603 return E_ALREADY_JOINED;
604
MG Mud User88f12472016-06-24 23:31:02 +0200605 channels[ch][I_MEMBER] += ({ pl });
Arathorn19459eb2019-11-30 00:45:51 +0100606 return (0);
MG Mud User88f12472016-06-24 23:31:02 +0200607}
608
609// leave() - leave a channel
610// the access check in this function is just there for completeness
611// one should always be allowed to leave a channel.
612// if there are no players left on the channel it will vanish, unless
613// its master is this object.
614// SEE: join, access
615int leave(string ch, object pl)
616{
617 int pos;
Arathorn19459eb2019-11-30 00:45:51 +0100618
619 if (!funcall(#'access,&ch, pl, C_LEAVE))
620 return E_ACCESS_DENIED;
621
MG Mud User88f12472016-06-24 23:31:02 +0200622 channels[ch][I_MEMBER] -= ({0}); // kaputte Objekte erstmal raus
Arathorn19459eb2019-11-30 00:45:51 +0100623
624 if ((pos = member(channels[ch][I_MEMBER], pl)) == -1)
625 return E_NOT_MEMBER;
626
627 if (pl == channels[ch][I_MASTER] && sizeof(channels[ch][I_MEMBER]) > 1)
MG Mud User88f12472016-06-24 23:31:02 +0200628 {
629 channels[ch][I_MASTER] = channels[ch][I_MEMBER][1];
Arathorn19459eb2019-11-30 00:45:51 +0100630
631 if (!pl->QueryProp(P_INVIS))
632 this_object()->send(ch, pl, "uebergibt die Ebene an " +
633 channels[ch][I_MASTER]->name(WEN) + ".", MSG_EMOTE);
MG Mud User88f12472016-06-24 23:31:02 +0200634 }
635 channels[ch][I_MEMBER][pos..pos] = ({ });
636
Arathorn19459eb2019-11-30 00:45:51 +0100637 if (!sizeof(channels[ch][I_MEMBER]) && !stringp(channels[ch][I_MASTER]))
MG Mud User88f12472016-06-24 23:31:02 +0200638 {
639 // delete the channel that has no members
Arathorn19459eb2019-11-30 00:45:51 +0100640 if (!pl->QueryProp(P_INVIS))
641 this_object()->send(CMNAME, pl,
642 "verlaesst als "+
643 (pl->QueryProp(P_GENDER) == 1 ? "Letzter" : "Letzte")+
644 " die Ebene '"+channels[ch][I_NAME]+"', worauf diese sich in "
645 "einem Blitz oktarinen Lichts aufloest.", MSG_EMOTE);
646
647 channelC[lower_case(ch)] =
648 ({ channels[ch][I_NAME], channels[ch][I_INFO], time() });
MG Mud User88f12472016-06-24 23:31:02 +0200649 m_delete(channels, lower_case(ch));
MG Mud User88f12472016-06-24 23:31:02 +0200650 // Wird ein Channel entfernt, wird auch seine History geloescht
Arathorn19459eb2019-11-30 00:45:51 +0100651 // TODO: this foils the attempts at creating a persistent channel
652 // history via /secure/memory.c
MG Mud User88f12472016-06-24 23:31:02 +0200653 channelH = m_copy_delete(channelH, lower_case(ch));
MG Mud User88f12472016-06-24 23:31:02 +0200654 stats["dispose"]++;
Arathorn19459eb2019-11-30 00:45:51 +0100655 save_me_soon = 1;
MG Mud User88f12472016-06-24 23:31:02 +0200656 }
Arathorn19459eb2019-11-30 00:45:51 +0100657 return (0);
MG Mud User88f12472016-06-24 23:31:02 +0200658}
659
660// send() - send a message to all recipients of the specified channel 'ch'
661// checks if 'pl' is allowed to send a message and sends if success-
662// ful a message with type 'type'
663// 'pl' must be an object, the message is attributed to it. e.g.
664// ignore checks use it. It can be != previous_object()
665// SEE: access, ch.h
666varargs int send(string ch, object pl, string msg, int type)
667{
668 int a;
669
Arathorn19459eb2019-11-30 00:45:51 +0100670 if (!(a = funcall(#'access, &ch, pl, C_SEND, &msg)))
671 return E_ACCESS_DENIED;
672
673 if (a < 2 && member(channels[ch][I_MEMBER], pl) == -1)
674 return E_NOT_MEMBER;
675
676 if (!msg || !stringp(msg) || !sizeof(msg))
677 return E_EMPTY_MESSAGE;
678
679 map_objects(channels[ch][I_MEMBER], "ChannelMessage",
680 ({ channels[ch][I_NAME], pl, msg, type }));
681
682 if (sizeof(channelH[ch]) > MAX_HIST_SIZE)
MG Mud User88f12472016-06-24 23:31:02 +0200683 channelH[ch] = channelH[ch][1..];
Arathorn19459eb2019-11-30 00:45:51 +0100684
685 channelH[ch] +=
686 ({ ({ channels[ch][I_NAME],
687 (stringp(pl)
688 ? pl
689 : (pl->QueryProp(P_INVIS)
690 ? "/(" + capitalize(getuid(pl)) + ")$"
691 : "")
692 + (pl->Name(WER, 2) || "<Unbekannt>")),
693 msg + " <" + strftime("%a, %H:%M:%S") + ">\n",
694 type }) });
695 return (0);
696 }
697
698private void clean(string n, mixed a)
699{
700 a[0] -= ({ 0 });
MG Mud User88f12472016-06-24 23:31:02 +0200701}
702
703// list() - list all channels, that are at least receivable by 'pl'
704// returns a mapping,
705// SEE: access, channels
MG Mud User88f12472016-06-24 23:31:02 +0200706mixed list(object pl)
707{
Arathorn19459eb2019-11-30 00:45:51 +0100708 mapping chs = filter_indices(channels, #'access, pl, C_LIST);
709 walk_mapping(chs, #'clean);
710
711 if (!sizeof(chs))
712 return E_ACCESS_DENIED;
MG Mud User88f12472016-06-24 23:31:02 +0200713 return deep_copy(chs);
714}
715
716// find() - find a channel by its name (may be partial)
717// returns an array for multiple results and 0 for no matching name
718// SEE: access
719mixed find(string ch, object pl)
720{
721 mixed chs, s;
Arathorn19459eb2019-11-30 00:45:51 +0100722
723 if (stringp(ch))
724 ch = lower_case(ch);
725
726 if (!sizeof(regexp(({ch}), "^[<>a-z0-9#-]*$")))
727 return 0; // RUM
728
729 if (!sizeof(chs = regexp(m_indices(channels), "^" + ch + "$")))
730 chs = regexp(m_indices(channels), "^" + ch);
731
732 if ((s = sizeof(chs)) > 1)
733 {
734 if (sizeof(chs = filter(chs, #'access, pl, C_FIND)) == 1)
MG Mud User88f12472016-06-24 23:31:02 +0200735 return channels[chs[0]][I_NAME];
Arathorn19459eb2019-11-30 00:45:51 +0100736 else
737 return chs;
738 }
739
740 return ((s && funcall(#'access,chs[0], pl, C_FIND))
741 ? channels[chs[0]][I_NAME]
742 : 0);
MG Mud User88f12472016-06-24 23:31:02 +0200743}
744
745// history() - get the history of a channel
746// SEE: access
747mixed history(string ch, object pl)
748{
Arathorn19459eb2019-11-30 00:45:51 +0100749 if (!funcall(#'access, &ch, pl, C_JOIN))
MG Mud User88f12472016-06-24 23:31:02 +0200750 return E_ACCESS_DENIED;
751 return deep_copy(channelH[ch]);
752}
753
754// remove - remove a channel (wird aus der Shell aufgerufen)
755// SEE: new
756mixed remove(string ch, object pl)
757{
758 mixed members;
759
Arathorn19459eb2019-11-30 00:45:51 +0100760 if (previous_object() != this_object())
761 {
762 if (!stringp(ch) ||
763 pl != this_player() || this_player() != this_interactive() ||
764 this_interactive() != previous_object() ||
765 !IS_ARCH(this_interactive()))
MG Mud User88f12472016-06-24 23:31:02 +0200766 return E_ACCESS_DENIED;
Arathorn19459eb2019-11-30 00:45:51 +0100767 }
MG Mud User88f12472016-06-24 23:31:02 +0200768
Arathorn19459eb2019-11-30 00:45:51 +0100769 if (channels[lower_case(ch)])
770 {
MG Mud User88f12472016-06-24 23:31:02 +0200771 channels[lower_case(ch)][I_MEMBER] =
Arathorn19459eb2019-11-30 00:45:51 +0100772 filter_objects(channels[lower_case(ch)][I_MEMBER],
773 "QueryProp", P_CHANNELS);
MG Mud User88f12472016-06-24 23:31:02 +0200774 map(channels[lower_case(ch)][I_MEMBER],
Arathorn19459eb2019-11-30 00:45:51 +0100775 function mixed(object listener)
776 {
777 string* chans = listener->QueryProp(P_CHANNELS);
778 chans -= ({lower_case(ch)});
779 ({string*})listener->SetProp(P_CHANNELS, chans);
780 });
MG Mud User88f12472016-06-24 23:31:02 +0200781 channels = m_copy_delete(channels, lower_case(ch));
782
783 // Wird ein Channel entfernt, wird auch seine History geloescht
Arathorn19459eb2019-11-30 00:45:51 +0100784 if (pointerp(channelH[lower_case(ch)]))
785 channelH = m_copy_delete(channelH, lower_case(ch));
MG Mud User88f12472016-06-24 23:31:02 +0200786
787 stats["dispose"]++;
788 }
Arathorn19459eb2019-11-30 00:45:51 +0100789
790 if (!channelC[lower_case(ch)])
MG Mud User88f12472016-06-24 23:31:02 +0200791 return E_ACCESS_DENIED;
Arathorn19459eb2019-11-30 00:45:51 +0100792
MG Mud User88f12472016-06-24 23:31:02 +0200793 channelC = m_copy_delete(channelC, lower_case(ch));
Arathorn19459eb2019-11-30 00:45:51 +0100794 save_me_soon = 1;
795 return (0);
MG Mud User88f12472016-06-24 23:31:02 +0200796}
797
Arathorn19459eb2019-11-30 00:45:51 +0100798// Wird aus der Shell aufgerufen
MG Mud User88f12472016-06-24 23:31:02 +0200799mixed clear_history(string ch)
800{
MG Mud User88f12472016-06-24 23:31:02 +0200801 // Sicherheitsabfragen
Arathorn19459eb2019-11-30 00:45:51 +0100802 if (previous_object() != this_object())
803 {
804 if (!stringp(ch) ||
805 this_player() != this_interactive() ||
806 this_interactive() != previous_object() ||
807 !IS_ARCH(this_interactive()))
MG Mud User88f12472016-06-24 23:31:02 +0200808 return E_ACCESS_DENIED;
Arathorn19459eb2019-11-30 00:45:51 +0100809 }
MG Mud User88f12472016-06-24 23:31:02 +0200810
811 // History des Channels loeschen
Arathorn19459eb2019-11-30 00:45:51 +0100812 if (pointerp(channelH[lower_case(ch)]))
813 channelH[lower_case(ch)] = ({});
MG Mud User88f12472016-06-24 23:31:02 +0200814
815 return 0;
816}