summaryrefslogtreecommitdiff
path: root/appl/spree/engines/gather.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/spree/engines/gather.b')
-rw-r--r--appl/spree/engines/gather.b267
1 files changed, 267 insertions, 0 deletions
diff --git a/appl/spree/engines/gather.b b/appl/spree/engines/gather.b
new file mode 100644
index 00000000..59b7bfb2
--- /dev/null
+++ b/appl/spree/engines/gather.b
@@ -0,0 +1,267 @@
+implement Engine;
+
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sets.m";
+ sets: Sets;
+ Set, set, A, B, All, None: import sets;
+include "../spree.m";
+ spree: Spree;
+ archives: Archives;
+ Attributes, Range, Object, Clique, Member, rand: import spree;
+include "daytime.m";
+ daytime: Daytime;
+include "../gather.m";
+
+clique: ref Clique;
+
+started := 0;
+halted := 0;
+suspended: Set; # set of members currently suspended from the clique.
+count := 0;
+nmembers := 0;
+title := "unknown";
+cliquemod: Gatherengine;
+
+members: Set;
+watchers: Set;
+
+invited: list of string;
+
+# options:
+# <n> cliquemodule opts
+init(srvmod: Spree, g: ref Clique, argv: list of string): string
+{
+ sys = load Sys Sys->PATH;
+ clique = g;
+ spree = srvmod;
+ sets = load Sets Sets->PATH;
+ if (sets == nil) {
+ sys->print("gather: cannot load %s: %r\n", Sets->PATH);
+ return "bad module";
+ }
+ sets->init();
+ daytime = load Daytime Daytime->PATH;
+ if (daytime == nil) {
+ sys->print("gather: cannot load %s: %r\n", Daytime->PATH);
+ return "bad module";
+ }
+ archives = load Archives Archives->PATH;
+ if (archives == nil) {
+ sys->print("gather: cannot load %s: %r\n", Archives->PATH);
+ return "bad module";
+ }
+ archives->init(srvmod);
+ argv = tl argv;
+ n := len argv;
+ if (n < 2)
+ return "bad init options";
+ count = int hd argv;
+ if (count != -1 && count <= 0)
+ return "bad gather count";
+ argv = tl argv;
+ if (count < len clique.archive.members)
+ count = len clique.archive.members;
+ cliquemod = load Gatherengine "/dis/spree/engines/" + hd argv + ".dis";
+ if (cliquemod == nil)
+ return sys->sprint("bad module: %r");
+ title = concat(argv);
+ e := cliquemod->init(srvmod, clique, tl argv, len clique.archive.members > 0);
+ if (e != nil)
+ return e;
+ if (len clique.archive.members > 0) {
+ for (i := 0; i < len clique.archive.members; i++)
+ invited = clique.archive.members[i] :: invited;
+ } else
+ invited = clique.owner() :: nil;
+ for (inv := invited; inv != nil; inv = tl inv)
+ clique.notify(clique.parentid, "invite " + hd inv);
+ clique.notify(clique.parentid, "title (" + title + ")");
+ return nil;
+}
+
+join(p: ref Member, cmd: string, susp: int): string
+{
+sys->print("gather: %s[%d] joining '%s' (suspended: %d)\n", p.name, p.id, cmd, susp);
+ case cmd {
+ "join" =>
+ if (started) {
+ if (!susp || !halted)
+ return "clique has already started";
+ suspended = suspended.del(p.id);
+ if (suspended.eq(None)) {
+ halted = 0;
+ # XXX inform participants that clique is starting again
+ }
+ pset := None.add(p.id);
+ clique.action("clienttype " + cliquemod->clienttype(), nil, nil, pset);
+ clique.breakmsg(pset);
+ return nil;
+ }
+ for (inv := invited; inv != nil; inv = tl inv)
+ if (hd inv == p.name || hd inv == "all")
+ break;
+ if (inv == nil)
+ return "you have not been invited";
+ if (nmembers >= cliquemod->maxmembers() || (count != -1 && nmembers >= count))
+ return "too many members already";
+ if (len clique.archive.members > 0) {
+ for (i := 0; i < len clique.archive.members; i++)
+ if (p.name == clique.archive.members[i])
+ break;
+ if (i == len clique.archive.members)
+ return "you are not part of that clique";
+ }
+ nmembers++;
+ members = members.add(p.id);
+ clique.notify(clique.parentid, "join " + p.name);
+ s := None.add(p.id);
+ # special case for single member cliques: don't need a gather client as we can start right now.
+ if (cliquemod->maxmembers() == 1)
+ return startclique();
+ clique.action("clienttype gather", nil, nil, s);
+ clique.breakmsg(s);
+ clique.action("title " + title, nil, nil, s);
+ clique.action("join " + p.name, nil, nil, All);
+ "watch" =>
+ if (susp)
+ return "you cannot watch if you are playing";
+ watchers = watchers.add(p.id);
+ s := None.add(p.id);
+ if (started)
+ clique.action("clienttype " + cliquemod->clienttype(), nil, nil, s);
+ else
+ clique.action("clienttype gather", nil, nil, s);
+ clique.breakmsg(s);
+ if (!started)
+ clique.action("watch " + p.name, nil, nil, All);
+ * =>
+ return "unknown join request";
+ }
+ return nil;
+}
+
+leave(p: ref Member): int
+{
+ if (members.holds(p.id)) {
+ if (started) {
+ suspended = suspended.add(p.id);
+ if (suspended.eq(members)) {
+ cliquemod->archive();
+ name := spree->newarchivename();
+ e := archives->write(clique,
+ ("title", concat(tl tl clique.archive.argv)) ::
+ ("date", string daytime->now()) :: nil,
+ name, members);
+ if (e != nil)
+ sys->print("warning: cannot archive clique: %s\n", e);
+ else
+ clique.notify(clique.parentid, "archived " + name);
+ clique.hangup();
+ return 1;
+ } else {
+ halted = 1;
+ return 0;
+ }
+ }
+
+ members = members.del(p.id);
+ nmembers--;
+ clique.notify(clique.parentid, "leave " + p.name);
+ if (nmembers == 0)
+ clique.hangup();
+ } else {
+ watchers = watchers.del(p.id);
+ clique.action("unwatch " + p.name, nil, nil, All);
+ }
+ return 1;
+}
+
+notify(nil: int, note: string)
+{
+ (n, toks) := sys->tokenize(note, " ");
+ case hd toks {
+ "invite" =>
+ invited = hd tl toks :: invited;
+ "uninvite" =>
+ inv := invited;
+ for (invited = nil; inv != nil; inv = tl inv)
+ if (hd inv != hd tl toks)
+ invited = hd inv :: invited;
+ * =>
+ sys->print("gather: unknown notification '%s'\n", note);
+ }
+}
+
+command(p: ref Member, cmd: string): string
+{
+ if (halted)
+ return "clique is halted for the time being";
+ if (started) {
+ if (!members.holds(p.id)) {
+sys->print("members (%s) doesn't hold %s[%d]\n", members.str(), p.name, p.id);
+ return "you are only watching";
+ }
+ return cliquemod->command(p, cmd);
+ }
+
+ (n, toks) := sys->tokenize(cmd, " \n");
+ if (n == 0)
+ return "bad command";
+ case hd toks {
+ "start" =>
+ if (len clique.archive.members == 0 && p.name != clique.owner())
+ return "only the owner can start a clique";
+ if (count != -1 && nmembers != count)
+ return "need " + string count + " members";
+ return startclique();
+ "chat" =>
+ clique.action("chat " + p.name + " " + concat(tl toks), nil, nil, All);
+ * =>
+ return "unknown command";
+ }
+ return nil;
+}
+
+startclique(): string
+{
+ # XXX could randomly shuffle members here
+
+ pa := array[nmembers] of ref Member;
+ names := array[nmembers] of string;
+ j := nmembers;
+ for (i := members.limit(); i >= 0; i--)
+ if (members.holds(i)) {
+ pa[--j] = clique.member(i);
+ names[j] = pa[j].name;
+ }
+ e := cliquemod->propose(names);
+ if (e != nil)
+ return e;
+ clique.action("clienttype " + cliquemod->clienttype(), nil, nil, All);
+ clique.breakmsg(All);
+ cliquemod->start(pa, len clique.archive.members > 0);
+ clique.start();
+ started = 1;
+ clique.notify(clique.parentid, "started");
+ clique.notify(clique.parentid, "title " + concat(tl tl clique.archive.argv));
+ return nil;
+}
+
+readfile(f: int, offset: big, n: int): array of byte
+{
+ if (!started)
+ return nil;
+ return cliquemod->readfile(f, offset, n);
+}
+
+concat(l: list of string): string
+{
+ if (l == nil)
+ return nil;
+ s := hd l;
+ for (l = tl l; l != nil; l = tl l)
+ s += " " + hd l;
+ return s;
+}