@zaisma/game-server 1.10.0 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/bench/brand/README.md +22 -17
- package/bench/brand/app-icon-1024.png +0 -0
- package/bench/brand/app-icon-192.png +0 -0
- package/bench/brand/app-icon-512.png +0 -0
- package/bench/brand/app-icon-dark-512.png +0 -0
- package/bench/brand/app-icon-maskable-512.png +0 -0
- package/bench/brand/apple-touch-icon.png +0 -0
- package/bench/brand/favicon-32.png +0 -0
- package/bench/brand/favicon.ico +0 -0
- package/bench/brand/favicon.svg +1 -1
- package/bench/brand/manifest.webmanifest +26 -0
- package/bench/brand/share-card-dark.png +0 -0
- package/bench/brand/share-card-dark.svg +1 -0
- package/bench/brand/share-card-light.png +0 -0
- package/bench/brand/share-card-light.svg +1 -0
- package/bench/brand/stripe-icon.png +0 -0
- package/bench/brand/zaisma-icon-dark.svg +1 -0
- package/bench/brand/zaisma-icon-light.svg +1 -0
- package/bench/brand/zaisma-mark-glass-black-2048.png +0 -0
- package/bench/brand/zaisma-mark-glass-black.svg +1 -0
- package/bench/brand/zaisma-mark-glass-white-2048.png +0 -0
- package/bench/brand/zaisma-mark-glass-white.svg +1 -0
- package/bench/brand/zaisma-wordmark-glass-black-2400.png +0 -0
- package/bench/brand/zaisma-wordmark-glass-black.svg +1 -0
- package/bench/brand/zaisma-wordmark-glass-white-2400.png +0 -0
- package/bench/brand/zaisma-wordmark-glass-white.svg +1 -0
- package/bench/glass.js +3904 -1907
- package/bin/fast-lane.js +212 -0
- package/bin/local-play-certificate.js +11 -6
- package/bin/node-parts.js +9 -1
- package/bin/node.d.ts +12 -0
- package/bin/node.js +59 -15
- package/package.json +13 -1
- package/sdk/zaisma.d.ts +53 -7
- package/sdk/zaisma.js +196 -8
- package/src/qr-code.d.ts +5 -4
- package/src/qr-code.js +11 -10
- package/src/room.d.ts +18 -3
- package/src/room.js +12 -12
- package/bench/brand/zaisma-app-icon.svg +0 -1
- package/bench/brand/zaisma-mark-metal-2048.png +0 -0
- package/bench/brand/zaisma-mark-metal.svg +0 -1
- package/bench/brand/zaisma-mark-tricolor.svg +0 -1
- package/bench/brand/zaisma-wordmark-metal-2400.png +0 -0
- package/bench/brand/zaisma-wordmark-metal.svg +0 -1
package/sdk/zaisma.js
CHANGED
|
@@ -18,16 +18,20 @@
|
|
|
18
18
|
var OWN_SERVER_URL = /^(wss|https):\/\/[^/]/;
|
|
19
19
|
|
|
20
20
|
// What each call takes. Anything else is refused, so a misspelled or old option never goes quietly unused.
|
|
21
|
-
var JOIN_OPTIONS = ["roomCode", "serverUrl", "isListed", "friendJoinsEnabled", "name", "labels", "joinData", "idleTimeoutSec", "idleCheckEnabled", "isSpectator"];
|
|
21
|
+
var JOIN_OPTIONS = ["roomCode", "serverUrl", "transport", "isListed", "friendJoinsEnabled", "name", "labels", "joinData", "idleTimeoutSec", "idleCheckEnabled", "isSpectator"];
|
|
22
22
|
// quickJoin finds Zaisma rooms, so it takes no serverUrl.
|
|
23
23
|
var QUICK_JOIN_OPTIONS = ["isListed", "friendJoinsEnabled", "name", "labels", "joinData", "idleTimeoutSec", "idleCheckEnabled", "region"];
|
|
24
24
|
var LIST_OPTIONS = ["region", "labels"];
|
|
25
|
-
// move.join joins the room the move names, on this room's server, as a player: the game server moves only players.
|
|
26
|
-
var MOVE_JOIN_OPTIONS = JOIN_OPTIONS.filter(function (name) { return name !== "roomCode" && name !== "serverUrl" && name !== "isSpectator"; });
|
|
25
|
+
// move.join joins the room the move names, on this room's server and over its transport, as a player: the game server moves only players.
|
|
26
|
+
var MOVE_JOIN_OPTIONS = JOIN_OPTIONS.filter(function (name) { return name !== "roomCode" && name !== "serverUrl" && name !== "transport" && name !== "isSpectator"; });
|
|
27
27
|
var KICK_OPTIONS = ["keepOut"];
|
|
28
28
|
var SEND_OPTIONS = ["delivery", "latestKey"];
|
|
29
29
|
var SMOOTHING_OPTIONS = ["delayMs"];
|
|
30
30
|
var DELIVERIES = ["reliable", "latest_only"];
|
|
31
|
+
/** How a room on the game's own server is reached: the WebSocket, or the fast lane (WebTransport), which carries latest-only sends as datagrams. */
|
|
32
|
+
var TRANSPORTS = ["websocket", "webtransport"];
|
|
33
|
+
/** A fast lane that isn't up in this long gives way to the WebSocket, for the rest of the room's life. */
|
|
34
|
+
var FAST_LANE_CONNECT_TIMEOUT_MS = 3000;
|
|
31
35
|
var ROOM_INFO_FIELDS = ["name", "labels"];
|
|
32
36
|
var ROOM_EVENTS = ["message", "bytes", "state", "players", "connectionStatus", "ended", "ending", "endingCancelled", "kicked", "settings", "move", "moved", "idle", "away", "back", "muted", "error", "versionUpdate", "chat"];
|
|
33
37
|
/** Chat (room.chat): the longest message, the lines a room keeps in memory for reports, and how many of them a report carries. */
|
|
@@ -37,6 +41,7 @@
|
|
|
37
41
|
var SAVES_EVENTS = ["conflict", "developerChange"];
|
|
38
42
|
var ITEMS_EVENTS = ["purchase", "refund", "sheetOpen", "sheetClose"];
|
|
39
43
|
var VERSIONS_EVENTS = ["update"];
|
|
44
|
+
var PAGE_EVENTS = ["coveredBy"];
|
|
40
45
|
var BUY_OPTIONS = ["itemCount", "gameAccountId"];
|
|
41
46
|
/** The room messages this SDK speaks (zaisma.d.ts: the wire protocol), sent in the hello. */
|
|
42
47
|
var PROTOCOL_VERSION_NUMBER = 1;
|
|
@@ -424,6 +429,11 @@
|
|
|
424
429
|
this.isSpectator = setup.isSpectator === true;
|
|
425
430
|
this.spectatorCount = 0;
|
|
426
431
|
this._url = setup.url;
|
|
432
|
+
// The fast lane's address (joinRoom({ transport: "webtransport" }) on an own server), or null, and whether this room gave it up for the WebSocket.
|
|
433
|
+
this._fastLaneUrl = setup.fastLaneUrl || null;
|
|
434
|
+
this._isFastLaneOff = false;
|
|
435
|
+
// What this connection uses now: "webtransport" once the fast lane opens, else "websocket".
|
|
436
|
+
this.transport = "websocket";
|
|
427
437
|
this._pass = setup.pass;
|
|
428
438
|
this._made = { isListed: this.isListed, friendJoinsEnabled: setup.friendJoinsEnabled === false ? false : undefined, name: setup.name, labels: setup.labels };
|
|
429
439
|
this._joinData = setup.joinData === undefined ? null : setup.joinData;
|
|
@@ -472,6 +482,135 @@
|
|
|
472
482
|
}
|
|
473
483
|
Room.prototype = Object.create(Emitter.prototype);
|
|
474
484
|
|
|
485
|
+
/**
|
|
486
|
+
* The fast lane to the game's own server (WebTransport), with what the room uses on a WebSocket: readyState,
|
|
487
|
+
* bufferedAmount, send(text or bytes), close(code, reason), onopen, onmessage and onclose; and sendDatagram
|
|
488
|
+
* for latest-only sends, and onfail when it never came up. One stream carries every frame in order, each
|
|
489
|
+
* [4-byte big-endian payload length][1-byte kind: 0 text, 1 bytes][payload]; a datagram is [kind][payload]
|
|
490
|
+
* (the wire: packages/game-server/src/room.ts, above ServerMessage).
|
|
491
|
+
*/
|
|
492
|
+
function FastLane(url) {
|
|
493
|
+
var lane = this;
|
|
494
|
+
this.readyState = 0;
|
|
495
|
+
this.bufferedAmount = 0;
|
|
496
|
+
this.onopen = this.onmessage = this.onclose = this.onfail = null;
|
|
497
|
+
this._writer = null;
|
|
498
|
+
this._datagrams = null;
|
|
499
|
+
this._datagramWriter = null;
|
|
500
|
+
var session;
|
|
501
|
+
function fail(reason) {
|
|
502
|
+
if (lane.readyState !== 0) return;
|
|
503
|
+
lane.readyState = 3;
|
|
504
|
+
clearTimeout(timer);
|
|
505
|
+
try { session.close(); } catch (error) { /* fine */ }
|
|
506
|
+
if (lane.onfail) lane.onfail(reason);
|
|
507
|
+
}
|
|
508
|
+
var timer = setTimeout(function () { fail("no answer in " + FAST_LANE_CONNECT_TIMEOUT_MS / 1000 + " seconds"); }, FAST_LANE_CONNECT_TIMEOUT_MS);
|
|
509
|
+
try {
|
|
510
|
+
session = new WebTransport(url);
|
|
511
|
+
} catch (error) {
|
|
512
|
+
session = null;
|
|
513
|
+
// Called from the room's _connect: it hears the failure once that has set it up.
|
|
514
|
+
setTimeout(function () { fail(error && error.message ? error.message : "refused"); }, 0);
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
this._session = session;
|
|
518
|
+
session.ready.then(function () { return session.createBidirectionalStream(); }).then(function (stream) {
|
|
519
|
+
if (lane.readyState !== 0) return;
|
|
520
|
+
clearTimeout(timer);
|
|
521
|
+
lane._writer = stream.writable.getWriter();
|
|
522
|
+
lane._datagrams = session.datagrams;
|
|
523
|
+
lane._datagramWriter = (typeof session.datagrams.createWritable === "function" ? session.datagrams.createWritable() : session.datagrams.writable).getWriter();
|
|
524
|
+
lane.readyState = 1;
|
|
525
|
+
session.closed.then(function (closeInfo) { lane._closed(closeInfo && typeof closeInfo.closeCode === "number" ? closeInfo.closeCode : 1005, closeInfo && closeInfo.reason ? closeInfo.reason : ""); }, function () { lane._closed(1006, ""); });
|
|
526
|
+
lane._read(stream.readable);
|
|
527
|
+
lane._readDatagrams(session.datagrams.readable);
|
|
528
|
+
if (lane.onopen) lane.onopen();
|
|
529
|
+
}, function (error) {
|
|
530
|
+
fail(error && error.message ? error.message : "refused");
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
FastLane.prototype._closed = function (code, reason) {
|
|
534
|
+
if (this.readyState === 3) return;
|
|
535
|
+
this.readyState = 3;
|
|
536
|
+
if (this.onclose) this.onclose({ code: code, reason: reason });
|
|
537
|
+
};
|
|
538
|
+
/** A frame from the stream or a datagram, to the room as a WebSocket's message: text as a string, bytes as an ArrayBuffer. */
|
|
539
|
+
FastLane.prototype._take = function (kind, payload) {
|
|
540
|
+
if (this.readyState !== 1 || !this.onmessage) return;
|
|
541
|
+
if (kind === 0) this.onmessage({ data: new TextDecoder().decode(payload) });
|
|
542
|
+
else if (kind === 1) this.onmessage({ data: payload.slice().buffer });
|
|
543
|
+
};
|
|
544
|
+
FastLane.prototype._read = function (readable) {
|
|
545
|
+
var lane = this;
|
|
546
|
+
var reader = readable.getReader();
|
|
547
|
+
var buffered = new Uint8Array(0);
|
|
548
|
+
function next() {
|
|
549
|
+
reader.read().then(function (read) {
|
|
550
|
+
if (read.done) return;
|
|
551
|
+
var joined = new Uint8Array(buffered.length + read.value.length);
|
|
552
|
+
joined.set(buffered);
|
|
553
|
+
joined.set(read.value, buffered.length);
|
|
554
|
+
buffered = joined;
|
|
555
|
+
while (buffered.length >= 5) {
|
|
556
|
+
var payloadSizeBytes = new DataView(buffered.buffer, buffered.byteOffset, 4).getUint32(0);
|
|
557
|
+
if (buffered.length < 5 + payloadSizeBytes) break;
|
|
558
|
+
lane._take(buffered[4], buffered.subarray(5, 5 + payloadSizeBytes));
|
|
559
|
+
buffered = buffered.subarray(5 + payloadSizeBytes);
|
|
560
|
+
}
|
|
561
|
+
next();
|
|
562
|
+
}, function () { /* the session closed: closed says how */ });
|
|
563
|
+
}
|
|
564
|
+
next();
|
|
565
|
+
};
|
|
566
|
+
FastLane.prototype._readDatagrams = function (readable) {
|
|
567
|
+
var lane = this;
|
|
568
|
+
var reader = readable.getReader();
|
|
569
|
+
function next() {
|
|
570
|
+
reader.read().then(function (read) {
|
|
571
|
+
if (read.done) return;
|
|
572
|
+
if (read.value.length) lane._take(read.value[0], read.value.subarray(1));
|
|
573
|
+
next();
|
|
574
|
+
}, function () { /* the session closed */ });
|
|
575
|
+
}
|
|
576
|
+
next();
|
|
577
|
+
};
|
|
578
|
+
/** A text or bytes, as [kind][payload]. */
|
|
579
|
+
function fastLanePayload(message) {
|
|
580
|
+
var payload = typeof message === "string" ? new TextEncoder().encode(message) : new Uint8Array(message);
|
|
581
|
+
var framed = new Uint8Array(1 + payload.length);
|
|
582
|
+
framed[0] = typeof message === "string" ? 0 : 1;
|
|
583
|
+
framed.set(payload, 1);
|
|
584
|
+
return framed;
|
|
585
|
+
}
|
|
586
|
+
/** On the stream, in order, after everything sent before it. */
|
|
587
|
+
FastLane.prototype.send = function (message) {
|
|
588
|
+
if (this.readyState !== 1) return;
|
|
589
|
+
var payload = fastLanePayload(message);
|
|
590
|
+
var frame = new Uint8Array(4 + payload.length);
|
|
591
|
+
new DataView(frame.buffer).setUint32(0, payload.length - 1);
|
|
592
|
+
frame.set(payload, 4);
|
|
593
|
+
var lane = this;
|
|
594
|
+
this.bufferedAmount += frame.length;
|
|
595
|
+
this._writer.write(frame).then(function () { lane.bufferedAmount -= frame.length; }, function () { /* the session closed */ });
|
|
596
|
+
};
|
|
597
|
+
/** As a datagram, when it fits one: false when it doesn't, for the stream to carry it instead. */
|
|
598
|
+
FastLane.prototype.sendDatagram = function (message) {
|
|
599
|
+
if (this.readyState !== 1) return false;
|
|
600
|
+
var datagram = fastLanePayload(message);
|
|
601
|
+
var maximumSizeBytes = this._datagrams.maxDatagramSize;
|
|
602
|
+
if (typeof maximumSizeBytes !== "number" || datagram.length > maximumSizeBytes) return false;
|
|
603
|
+
this._datagramWriter.write(datagram).catch(function () { /* dropped, like a lost one */ });
|
|
604
|
+
return true;
|
|
605
|
+
};
|
|
606
|
+
FastLane.prototype.close = function (code, reason) {
|
|
607
|
+
if (this.readyState === 3) return;
|
|
608
|
+
var wasConnecting = this.readyState === 0;
|
|
609
|
+
this.readyState = 3;
|
|
610
|
+
if (!this._session) return;
|
|
611
|
+
try { this._session.close(wasConnecting ? undefined : { closeCode: typeof code === "number" ? code : 1000, reason: reason || "" }); } catch (error) { /* fine */ }
|
|
612
|
+
};
|
|
613
|
+
|
|
475
614
|
/** The link that opens this room: on Zaisma, the game's page with ?room=, and &server= for a room on the game's own server. */
|
|
476
615
|
function inviteUrlFor(roomCode, serverUrl) {
|
|
477
616
|
if (!config.site || !config.game) return null;
|
|
@@ -527,13 +666,23 @@
|
|
|
527
666
|
|
|
528
667
|
Room.prototype._connect = function () {
|
|
529
668
|
var room = this;
|
|
669
|
+
var isFastLane = Boolean(this._fastLaneUrl && !this._isFastLaneOff && typeof WebTransport === "function");
|
|
530
670
|
// From a tester link, Zaisma rooms hear which one, so its players leave when it's turned off.
|
|
531
|
-
var ws = new WebSocket(this._url + (config.tester && !this._serverUrl ? "?tester=" + config.tester : ""));
|
|
532
|
-
ws.binaryType = "arraybuffer";
|
|
671
|
+
var ws = isFastLane ? new FastLane(this._fastLaneUrl) : new WebSocket(this._url + (config.tester && !this._serverUrl ? "?tester=" + config.tester : ""));
|
|
672
|
+
if (!isFastLane) ws.binaryType = "arraybuffer";
|
|
533
673
|
this._ws = ws;
|
|
534
674
|
this._isWelcomed = false;
|
|
535
675
|
watch();
|
|
676
|
+
// The fast lane didn't come up (no UDP on this network, a server without it): the WebSocket, from now on.
|
|
677
|
+
ws.onfail = function (reason) {
|
|
678
|
+
if (room._ws !== ws) return;
|
|
679
|
+
room._isFastLaneOff = true;
|
|
680
|
+
if (typeof console !== "undefined") console.warn("Zaisma: the fast lane (WebTransport) to " + room._fastLaneUrl + " didn't connect (" + reason + "), so this room uses the WebSocket.");
|
|
681
|
+
room._ws = null;
|
|
682
|
+
if (!room._done) room._connect();
|
|
683
|
+
};
|
|
536
684
|
ws.onopen = function () {
|
|
685
|
+
room.transport = isFastLane ? "webtransport" : "websocket";
|
|
537
686
|
room._pingAtMs = room._pongAtMs = room._heardAtMs = Date.now();
|
|
538
687
|
room._probeIntervalSec = null;
|
|
539
688
|
var hello = { type: "hello", protocolVersionNumber: PROTOCOL_VERSION_NUMBER, pass: room._pass, room: room._made, joinData: room._joinData, idleTimeoutSec: room._clientIdle.idleTimeoutSec, idleCheckEnabled: room._clientIdle.idleCheckEnabled };
|
|
@@ -872,6 +1021,11 @@
|
|
|
872
1021
|
|
|
873
1022
|
/** A latest-only send: now, after anything still waiting, or into its slot while the socket is backed up. */
|
|
874
1023
|
Room.prototype._sendLatest = function (slotKey, payload) {
|
|
1024
|
+
// On the fast lane, a datagram: a late one is lost rather than waited for, so it skips the slots.
|
|
1025
|
+
if (this._canSend() && !this._queue.length && this._ws.sendDatagram && this._ws.sendDatagram(payload)) {
|
|
1026
|
+
this._sentAtMs = Date.now();
|
|
1027
|
+
return;
|
|
1028
|
+
}
|
|
875
1029
|
if (this._canSend() && !this._queue.length && !(this._ws.bufferedAmount >= BACKED_UP_SIZE_BYTES)) {
|
|
876
1030
|
this._sendWaiting();
|
|
877
1031
|
this._ws.send(payload);
|
|
@@ -1042,6 +1196,7 @@
|
|
|
1042
1196
|
*/
|
|
1043
1197
|
Room.prototype._move = function (message) {
|
|
1044
1198
|
var serverUrl = this._serverUrl;
|
|
1199
|
+
var transport = this._fastLaneUrl ? "webtransport" : undefined;
|
|
1045
1200
|
var room = this;
|
|
1046
1201
|
return {
|
|
1047
1202
|
roomCode: message.roomCode,
|
|
@@ -1049,7 +1204,7 @@
|
|
|
1049
1204
|
join: function (joinOptions) {
|
|
1050
1205
|
var refused = unknownOption(joinOptions, MOVE_JOIN_OPTIONS, "move.join");
|
|
1051
1206
|
if (refused) return Promise.reject(refused);
|
|
1052
|
-
return connect(message.roomCode, Object.assign({}, joinOptions || {}, { serverUrl: serverUrl }));
|
|
1207
|
+
return connect(message.roomCode, Object.assign({}, joinOptions || {}, { serverUrl: serverUrl, transport: transport }));
|
|
1053
1208
|
},
|
|
1054
1209
|
};
|
|
1055
1210
|
};
|
|
@@ -1070,6 +1225,7 @@
|
|
|
1070
1225
|
var fromRoomCode = this.roomCode;
|
|
1071
1226
|
this.roomCode = roomCode;
|
|
1072
1227
|
this._url = this._url.replace(/[A-Z0-9]{6}$/, roomCode);
|
|
1228
|
+
if (this._fastLaneUrl) this._fastLaneUrl = this._fastLaneUrl.replace(/[A-Z0-9]{6}$/, roomCode);
|
|
1073
1229
|
this.inviteUrl = inviteUrlFor(roomCode, this._serverUrl);
|
|
1074
1230
|
// A room the game server moves players to is listed only if the game server lists it.
|
|
1075
1231
|
this._made = { isListed: false };
|
|
@@ -1371,6 +1527,7 @@
|
|
|
1371
1527
|
var idleTimeoutSec = joinOptions.idleTimeoutSec;
|
|
1372
1528
|
if (idleTimeoutSec !== undefined && !(typeof idleTimeoutSec === "number" && idleTimeoutSec >= MINIMUM_IDLE_TIMEOUT_SEC && idleTimeoutSec <= MAXIMUM_IDLE_TIMEOUT_SEC)) return Promise.reject(codedError("bad_idle_timeout", "idleTimeoutSec runs from " + MINIMUM_IDLE_TIMEOUT_SEC + " to " + MAXIMUM_IDLE_TIMEOUT_SEC + "."));
|
|
1373
1529
|
if (serverUrl && !SERVER_URL.test(serverUrl)) return Promise.reject(codedError("bad_server_url", "serverUrl takes a full address starting with wss://, ws://, https:// or http://, like \"wss://play.example.com\"."));
|
|
1530
|
+
if (joinOptions.transport !== undefined && TRANSPORTS.indexOf(joinOptions.transport) === -1) return Promise.reject(codedError("bad_transport", "transport is " + JSON.stringify(joinOptions.transport) + '. Give "websocket" (the default) or "webtransport" (the fast lane to your own server).'));
|
|
1374
1531
|
if (roomCode && !ROOM_CODE.test(roomCode)) return Promise.reject(codedError("bad_room_code", JSON.stringify(roomCode) + " isn't a room code: room codes are six characters like \"K7Q2MX\", from 2 to 9 and A to Z without I or O."));
|
|
1375
1532
|
|
|
1376
1533
|
var roomCodeReady = roomCode
|
|
@@ -1380,10 +1537,13 @@
|
|
|
1380
1537
|
: post("/v1/rooms", { gameSlug: config.game }).then(function (answer) { return answer.roomCode; });
|
|
1381
1538
|
return Promise.all([roomCodeReady, getPass()]).then(function (ready) {
|
|
1382
1539
|
var base = (serverUrl || config.rooms).replace(/^http/, "ws");
|
|
1540
|
+
var path = "/v1/rooms/" + config.game + "/" + ready[0];
|
|
1383
1541
|
var room = new Room({
|
|
1384
1542
|
roomCode: ready[0],
|
|
1385
1543
|
pass: ready[1].pass,
|
|
1386
|
-
url: base +
|
|
1544
|
+
url: base + path,
|
|
1545
|
+
// The fast lane reaches only the game's own secure servers: Zaisma's rooms take the WebSocket, whatever transport says.
|
|
1546
|
+
fastLaneUrl: joinOptions.transport === "webtransport" && serverUrl && OWN_SERVER_URL.test(serverUrl) ? serverUrl.replace(/^wss:/, "https:") + path : null,
|
|
1387
1547
|
isListed: joinOptions.isListed,
|
|
1388
1548
|
friendJoinsEnabled: joinOptions.friendJoinsEnabled,
|
|
1389
1549
|
name: joinOptions.name,
|
|
@@ -2023,6 +2183,34 @@
|
|
|
2023
2183
|
});
|
|
2024
2184
|
}
|
|
2025
2185
|
|
|
2186
|
+
/*
|
|
2187
|
+
* Whether Zaisma's page covers the game: "sheet" while one of its sheets is open over it (buying, Players, Report), "card" while one of
|
|
2188
|
+
* its cards is over the game's corner, null otherwise, and always null outside Zaisma's page. The page says so whenever it changes
|
|
2189
|
+
* (zaisma:covered), and only the page is heard. A game can pause while a sheet covers it.
|
|
2190
|
+
*/
|
|
2191
|
+
var pageEvents = new Emitter(PAGE_EVENTS, "Zaisma.page has");
|
|
2192
|
+
var coveredBy = null;
|
|
2193
|
+
var zaismaPage = {
|
|
2194
|
+
on: function (event, listener) {
|
|
2195
|
+
pageEvents.on(event, listener);
|
|
2196
|
+
return zaismaPage;
|
|
2197
|
+
},
|
|
2198
|
+
off: function (event, listener) {
|
|
2199
|
+
pageEvents.off(event, listener);
|
|
2200
|
+
return zaismaPage;
|
|
2201
|
+
},
|
|
2202
|
+
};
|
|
2203
|
+
Object.defineProperty(zaismaPage, "coveredBy", { enumerable: true, get: function () { return coveredBy; } });
|
|
2204
|
+
if (config.site && window.parent !== window) {
|
|
2205
|
+
window.addEventListener("message", function (event) {
|
|
2206
|
+
if (event.origin !== config.site || event.source !== window.parent || !event.data || event.data.type !== "zaisma:covered") return;
|
|
2207
|
+
var now = event.data.coveredBy === "sheet" || event.data.coveredBy === "card" ? event.data.coveredBy : null;
|
|
2208
|
+
if (now === coveredBy) return;
|
|
2209
|
+
coveredBy = now;
|
|
2210
|
+
pageEvents._emit("coveredBy", now);
|
|
2211
|
+
});
|
|
2212
|
+
}
|
|
2213
|
+
|
|
2026
2214
|
/*
|
|
2027
2215
|
* The player's cloud saves, for games that draw their own save screens. The game page on Zaisma holds
|
|
2028
2216
|
* the player's session and answers (apps/site/src/saves-client.ts); the game never talks to a server.
|
|
@@ -2506,5 +2694,5 @@
|
|
|
2506
2694
|
});
|
|
2507
2695
|
|
|
2508
2696
|
var friends = { list: listFriends, setRoom: setOwnRoom, sendRequest: sendFriendRequest };
|
|
2509
|
-
window.Zaisma = { sdkVersionNumber: 1, gameSlug: config.game || null, player: player, joinRoom: joinRoom, rooms: rooms, server: server, invite: invite, checkChat: checkChat, openPlayersPanel: openPlayersPanel, links: links, items: items, friends: friends, saves: saves, versions: versions };
|
|
2697
|
+
window.Zaisma = { sdkVersionNumber: 1, gameSlug: config.game || null, player: player, joinRoom: joinRoom, rooms: rooms, server: server, invite: invite, checkChat: checkChat, openPlayersPanel: openPlayersPanel, links: links, items: items, friends: friends, saves: saves, versions: versions, page: zaismaPage };
|
|
2510
2698
|
})();
|
package/src/qr-code.d.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* QR codes for game and room links (docs/DECISIONS.md, "QR codes"): players get one behind Share,
|
|
3
|
-
* developers on the dashboard and through the API and MCP. Byte mode, error correction level
|
|
4
|
-
*
|
|
3
|
+
* developers on the dashboard and through the API and MCP. Byte mode, error correction level H, so a
|
|
4
|
+
* small picture over its middle (a game's icon) still leaves it readable, versions 1 to 10 (up to 119
|
|
5
|
+
* bytes, far more than a link needs). Written here rather than taken from a
|
|
5
6
|
* package so the site's page and the API draw the same code with no dependency; it follows ISO/IEC
|
|
6
7
|
* 18004 the way Project Nayuki's reference encoder does.
|
|
7
8
|
*/
|
|
8
9
|
export declare const QR_CODE_LIMITS: {
|
|
9
|
-
/** Version 10 at level
|
|
10
|
-
readonly maximumTextBytes:
|
|
10
|
+
/** Version 10 at level H holds 119 bytes. */
|
|
11
|
+
readonly maximumTextBytes: 119;
|
|
11
12
|
/** Light modules around the code, as the standard asks. */
|
|
12
13
|
readonly quietZoneModuleCount: 4;
|
|
13
14
|
};
|
package/src/qr-code.js
CHANGED
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* QR codes for game and room links (docs/DECISIONS.md, "QR codes"): players get one behind Share,
|
|
3
|
-
* developers on the dashboard and through the API and MCP. Byte mode, error correction level
|
|
4
|
-
*
|
|
3
|
+
* developers on the dashboard and through the API and MCP. Byte mode, error correction level H, so a
|
|
4
|
+
* small picture over its middle (a game's icon) still leaves it readable, versions 1 to 10 (up to 119
|
|
5
|
+
* bytes, far more than a link needs). Written here rather than taken from a
|
|
5
6
|
* package so the site's page and the API draw the same code with no dependency; it follows ISO/IEC
|
|
6
7
|
* 18004 the way Project Nayuki's reference encoder does.
|
|
7
8
|
*/
|
|
8
9
|
export const QR_CODE_LIMITS = {
|
|
9
|
-
/** Version 10 at level
|
|
10
|
-
maximumTextBytes:
|
|
10
|
+
/** Version 10 at level H holds 119 bytes. */
|
|
11
|
+
maximumTextBytes: 119,
|
|
11
12
|
/** Light modules around the code, as the standard asks. */
|
|
12
13
|
quietZoneModuleCount: 4,
|
|
13
14
|
};
|
|
14
|
-
// Level
|
|
15
|
-
const ERROR_CORRECTION_CODEWORD_COUNTS_PER_BLOCK = [0,
|
|
16
|
-
const ERROR_CORRECTION_BLOCK_COUNTS = [0, 1, 1,
|
|
17
|
-
/** Error correction level
|
|
18
|
-
const
|
|
15
|
+
// Level H: error correction codewords per block and blocks, by version (index 0 unused).
|
|
16
|
+
const ERROR_CORRECTION_CODEWORD_COUNTS_PER_BLOCK = [0, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28];
|
|
17
|
+
const ERROR_CORRECTION_BLOCK_COUNTS = [0, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8];
|
|
18
|
+
/** Error correction level H (high)'s two bits in the format information. */
|
|
19
|
+
const HIGH_CORRECTION_FORMAT_NUMBER = 2;
|
|
19
20
|
const HIGHEST_VERSION_NUMBER = 10;
|
|
20
21
|
const isBitSet = (bitsNumber, bitIndex) => ((bitsNumber >>> bitIndex) & 1) !== 0;
|
|
21
22
|
function rawDataModuleCount(versionNumber) {
|
|
@@ -226,7 +227,7 @@ export function qrCodeModules(text) {
|
|
|
226
227
|
}
|
|
227
228
|
}));
|
|
228
229
|
const drawFormat = (maskIndex) => {
|
|
229
|
-
const formatNumber = (
|
|
230
|
+
const formatNumber = (HIGH_CORRECTION_FORMAT_NUMBER << 3) | maskIndex;
|
|
230
231
|
let remainderNumber = formatNumber;
|
|
231
232
|
for (let stepIndex = 0; stepIndex < 10; stepIndex++)
|
|
232
233
|
remainderNumber = (remainderNumber << 1) ^ ((remainderNumber >>> 9) * 0x537);
|
package/src/room.d.ts
CHANGED
|
@@ -828,6 +828,17 @@ export interface RoomSettings {
|
|
|
828
828
|
* sentAt}; the sender gets {type: "chat_sent", chatNumber, sentAt}, {type: "chat_stopped", chatNumber,
|
|
829
829
|
* reason, message}, or an error with its chatNumber (chat_not_allowed, chat_empty, chat_too_long,
|
|
830
830
|
* chat_too_fast). chat_sent never says who read it. welcome says canChat: whether this player can chat at all.
|
|
831
|
+
*
|
|
832
|
+
* The fast lane (WebTransport, own servers only: joinRoom({ transport: "webtransport" })): the client opens
|
|
833
|
+
* a session at https://<server>/v1/rooms/<game>/<code>, the server checks its Origin as for the WebSocket,
|
|
834
|
+
* and the client opens one bidirectional stream first. That stream carries every frame the WebSocket
|
|
835
|
+
* carries, in order, each as [4-byte big-endian payload length][1-byte kind: 0 text, 1 bytes][payload]:
|
|
836
|
+
* text is the same UTF-8 JSON (hello, probe, probe_answer, ...) or "ping"/"pong", bytes the same binary
|
|
837
|
+
* frame (bytesFrame from the room, raw bytes from the client). A datagram is [1-byte kind][payload] with
|
|
838
|
+
* the same payloads, and carries only "latest_only" sends, both ways, when it fits the session's
|
|
839
|
+
* maxDatagramSize; a bigger one goes on the stream. A server takes datagrams only from a player it let in.
|
|
840
|
+
* Close codes and reasons go in the session's close info (closeCode, reason), the same numbers as the
|
|
841
|
+
* WebSocket's. Zaisma's own rooms don't take WebTransport: the SDK uses the WebSocket for them.
|
|
831
842
|
*/
|
|
832
843
|
export type ServerMessage = {
|
|
833
844
|
type: "welcome";
|
|
@@ -1051,9 +1062,13 @@ export interface ClientIdle {
|
|
|
1051
1062
|
}
|
|
1052
1063
|
/** How the room reaches players: a Durable Object's sockets, or a Node server's. */
|
|
1053
1064
|
export interface Transport {
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1065
|
+
/**
|
|
1066
|
+
* A text. delivery says how the game sent it: "latest_only" may go as a datagram on the fast lane to
|
|
1067
|
+
* your own server (WebTransport); everything else, and every send on a WebSocket, goes in order.
|
|
1068
|
+
*/
|
|
1069
|
+
send(playerId: string, text: string, delivery?: Delivery): void;
|
|
1070
|
+
/** A binary frame (bytesFrame), with its delivery as for send. Without it, rooms can't send bytes. */
|
|
1071
|
+
sendBytes?(playerId: string, frame: Uint8Array, delivery?: Delivery): void;
|
|
1057
1072
|
close(playerId: string, closeCode: number, reason: string): void;
|
|
1058
1073
|
/** Set up a reserved room, then send each of its players a "move" message. Without it, rooms can't reserve. */
|
|
1059
1074
|
reserve?(roomCode: string, start: RoomStart): void;
|
package/src/room.js
CHANGED
|
@@ -2162,7 +2162,7 @@ export class RoomCore {
|
|
|
2162
2162
|
deliver(playerIds, outgoing, delivery) {
|
|
2163
2163
|
const ids = playerIds.filter((id) => !this.isLost(id));
|
|
2164
2164
|
if (!delivery.isLatestOnly)
|
|
2165
|
-
return this.sendOut(ids, outgoing, ids.filter((id) => this.isBackedUp(id)).length);
|
|
2165
|
+
return this.sendOut(ids, outgoing, ids.filter((id) => this.isBackedUp(id)).length, "reliable");
|
|
2166
2166
|
const ready = [];
|
|
2167
2167
|
for (const id of ids) {
|
|
2168
2168
|
if (!this.isBackedUp(id)) {
|
|
@@ -2181,20 +2181,20 @@ export class RoomCore {
|
|
|
2181
2181
|
if (slots.size > ROOM_LIMITS.maximumLatestOnlySlotCount)
|
|
2182
2182
|
slots.delete(slots.keys().next().value);
|
|
2183
2183
|
}
|
|
2184
|
-
this.sendOut(ready, outgoing, 0);
|
|
2184
|
+
this.sendOut(ready, outgoing, 0, "latest_only");
|
|
2185
2185
|
}
|
|
2186
2186
|
/** A player's waiting latest-only messages, least recently replaced first. */
|
|
2187
2187
|
sendLatest(playerId) {
|
|
2188
2188
|
const slots = this.latestSlots.get(playerId);
|
|
2189
2189
|
this.latestSlots.delete(playerId);
|
|
2190
2190
|
for (const outgoing of slots?.values() ?? [])
|
|
2191
|
-
this.sendOut([playerId], outgoing, 0);
|
|
2191
|
+
this.sendOut([playerId], outgoing, 0, "latest_only");
|
|
2192
2192
|
}
|
|
2193
|
-
sendOut(playerIds, outgoing, backedUpMessageCount) {
|
|
2193
|
+
sendOut(playerIds, outgoing, backedUpMessageCount, delivery) {
|
|
2194
2194
|
if ("frame" in outgoing)
|
|
2195
|
-
this.sendFrame(playerIds, outgoing.frame, backedUpMessageCount);
|
|
2195
|
+
this.sendFrame(playerIds, outgoing.frame, backedUpMessageCount, delivery);
|
|
2196
2196
|
else
|
|
2197
|
-
this.sendEach(playerIds, outgoing.text, outgoing.messageType, backedUpMessageCount);
|
|
2197
|
+
this.sendEach(playerIds, outgoing.text, outgoing.messageType, backedUpMessageCount, delivery);
|
|
2198
2198
|
}
|
|
2199
2199
|
/**
|
|
2200
2200
|
* The match moves to a version published "now": every player hears it's coming at appliesAt, and when
|
|
@@ -2267,7 +2267,7 @@ export class RoomCore {
|
|
|
2267
2267
|
return [...this.players.values()].map((player) => ({ ...shownPlayer(player), awayReason: this.kept.get(player.id) ?? null }));
|
|
2268
2268
|
}
|
|
2269
2269
|
/** Send a binary frame to some players, counting it like text: toward each one's backlog, and the room's traffic. */
|
|
2270
|
-
sendFrame(given, frame, backedUpMessageCount = 0) {
|
|
2270
|
+
sendFrame(given, frame, backedUpMessageCount = 0, delivery = "reliable") {
|
|
2271
2271
|
if (!this.transport.sendBytes)
|
|
2272
2272
|
throw new Error("Sending bytes isn't available here");
|
|
2273
2273
|
const playerIds = given.filter((id) => !this.isLost(id));
|
|
@@ -2275,7 +2275,7 @@ export class RoomCore {
|
|
|
2275
2275
|
this.depth++;
|
|
2276
2276
|
try {
|
|
2277
2277
|
for (const id of playerIds) {
|
|
2278
|
-
this.transport.sendBytes(id, frame);
|
|
2278
|
+
this.transport.sendBytes(id, frame, delivery);
|
|
2279
2279
|
this.sent(id, frame.length);
|
|
2280
2280
|
}
|
|
2281
2281
|
}
|
|
@@ -2291,14 +2291,14 @@ export class RoomCore {
|
|
|
2291
2291
|
* Send one text to several players, counting its bytes once. A player it drops for falling behind
|
|
2292
2292
|
* leaves once everyone has it, so nobody hears of the leave before the rest of this text.
|
|
2293
2293
|
*/
|
|
2294
|
-
sendEach(playerIds, text, messageType = ZAISMA_MESSAGE_TYPE, backedUpMessageCount = 0) {
|
|
2294
|
+
sendEach(playerIds, text, messageType = ZAISMA_MESSAGE_TYPE, backedUpMessageCount = 0, delivery = "reliable") {
|
|
2295
2295
|
const byteCount = utf8ByteCount(text);
|
|
2296
2296
|
const ids = [...playerIds].filter((id) => !this.isLost(id));
|
|
2297
2297
|
this.countTraffic("outbound", messageType, ids.length, byteCount, backedUpMessageCount);
|
|
2298
2298
|
this.depth++;
|
|
2299
2299
|
try {
|
|
2300
2300
|
for (const id of ids)
|
|
2301
|
-
this.sendText(id, text, byteCount, null);
|
|
2301
|
+
this.sendText(id, text, byteCount, null, delivery);
|
|
2302
2302
|
}
|
|
2303
2303
|
finally {
|
|
2304
2304
|
this.depth--;
|
|
@@ -2311,10 +2311,10 @@ export class RoomCore {
|
|
|
2311
2311
|
* every ROOM_TUNING.probeSentSizeBytes, they're probed, so a backed-up connection shows as a late answer.
|
|
2312
2312
|
* A player whose connection dropped gets nothing: nothing is sent, counted or probed until they're back.
|
|
2313
2313
|
*/
|
|
2314
|
-
sendText(playerId, text, byteCount = utf8ByteCount(text), messageType = ZAISMA_MESSAGE_TYPE) {
|
|
2314
|
+
sendText(playerId, text, byteCount = utf8ByteCount(text), messageType = ZAISMA_MESSAGE_TYPE, delivery = "reliable") {
|
|
2315
2315
|
if (this.isLost(playerId))
|
|
2316
2316
|
return;
|
|
2317
|
-
this.transport.send(playerId, text);
|
|
2317
|
+
this.transport.send(playerId, text, delivery);
|
|
2318
2318
|
// sendEach counts its text once for everyone (messageType null).
|
|
2319
2319
|
if (messageType !== null)
|
|
2320
2320
|
this.countTraffic("outbound", messageType, 1, byteCount);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" role="img" aria-label="Zaisma"><rect width="1024" height="1024" rx="230" fill="#1C1C1C"/><g transform="translate(132 113) scale(0.17728)"><defs><linearGradient id="ag" x1="0" y1="721" x2="0" y2="3779" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#cdcdcd"/><stop offset="1" stop-color="#333"/></linearGradient><clipPath id="acb"><path d="M1884.08,3033.91c767.645,0 1535.29,0 2302.93,0c33.233,248.238 66.466,496.475 99.699,744.712l-4286.71,-0c627.367,-243.599 1255.99,-491.89 1884.08,-744.712Z"/></clipPath><clipPath id="acm"><path d="M415.598,3484.54c777.297,-719.363 1475.76,-1400.24 2108.05,-2044.32c404.715,-158.588 821.55,-323.759 1248.51,-495.17c-547.607,610.584 -1151.51,1254.04 -1823.08,1932.14c-520.17,209.105 -1032.22,411.7 -1533.48,607.345Z"/></clipPath><clipPath id="act"><path d="M2575.7,1305.06l-1740.58,0c21.608,-194.559 43.215,-389.119 64.823,-583.678l3147.9,0c-485.752,195.366 -976.426,389.899 -1472.14,583.678Z"/></clipPath></defs><path d="M1884.08,3033.91c767.645,0 1535.29,0 2302.93,0c33.233,248.238 66.466,496.475 99.699,744.712l-4286.71,-0c627.367,-243.599 1255.99,-491.89 1884.08,-744.712Z" fill="url(#ag)"/><g clip-path="url(#acb)" fill="none" stroke-width="250" stroke-linecap="square"><path d="M4187.01,3033.91c33.233,248.238 66.466,496.475 99.699,744.712l-4286.71,-0" stroke="#000"/><path d="M-0,3778.62c627.367,-243.599 1255.99,-491.89 1884.08,-744.712c767.645,0 1535.29,0 2302.93,0" stroke="#fff" stroke-linejoin="miter"/></g><path d="M415.598,3484.54c777.297,-719.363 1475.76,-1400.24 2108.05,-2044.32c404.715,-158.588 821.55,-323.759 1248.51,-495.17c-547.607,610.584 -1151.51,1254.04 -1823.08,1932.14c-520.17,209.105 -1032.22,411.7 -1533.48,607.345Z" fill="url(#ag)"/><g clip-path="url(#acm)" fill="none" stroke-width="250" stroke-linecap="square"><path d="M3772.16,945.051c-547.607,610.584 -1151.51,1254.04 -1823.08,1932.14c-520.17,209.105 -1032.22,411.7 -1533.48,607.345" stroke="#000"/><path d="M415.598,3484.54c777.297,-719.363 1475.76,-1400.24 2108.05,-2044.32c404.715,-158.588 821.55,-323.759 1248.51,-495.17" stroke="#fff" stroke-linejoin="miter"/></g><path d="M2575.7,1305.06l-1740.58,0c21.608,-194.559 43.215,-389.119 64.823,-583.678l3147.9,0c-485.752,195.366 -976.426,389.899 -1472.14,583.678Z" fill="url(#ag)"/><g clip-path="url(#act)" fill="none" stroke-width="250" stroke-linecap="square"><path d="M4047.85,721.381c-485.752,195.366 -976.426,389.899 -1472.14,583.678c-580.193,0 -1160.39,0 -1740.58,0" stroke="#000"/><path d="M835.121,1305.06c21.608,-194.559 43.215,-389.119 64.823,-583.678l3147.9,0" stroke="#fff" stroke-linejoin="miter"/></g></g></svg>
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-120 600 4527 3300" role="img" aria-label="Zaisma"><defs><linearGradient id="mg" x1="0" y1="721" x2="0" y2="3779" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#cdcdcd"/><stop offset="1" stop-color="#333"/></linearGradient><clipPath id="mcb"><path d="M1884.08,3033.91c767.645,0 1535.29,0 2302.93,0c33.233,248.238 66.466,496.475 99.699,744.712l-4286.71,-0c627.367,-243.599 1255.99,-491.89 1884.08,-744.712Z"/></clipPath><clipPath id="mcm"><path d="M415.598,3484.54c777.297,-719.363 1475.76,-1400.24 2108.05,-2044.32c404.715,-158.588 821.55,-323.759 1248.51,-495.17c-547.607,610.584 -1151.51,1254.04 -1823.08,1932.14c-520.17,209.105 -1032.22,411.7 -1533.48,607.345Z"/></clipPath><clipPath id="mct"><path d="M2575.7,1305.06l-1740.58,0c21.608,-194.559 43.215,-389.119 64.823,-583.678l3147.9,0c-485.752,195.366 -976.426,389.899 -1472.14,583.678Z"/></clipPath></defs><path d="M1884.08,3033.91c767.645,0 1535.29,0 2302.93,0c33.233,248.238 66.466,496.475 99.699,744.712l-4286.71,-0c627.367,-243.599 1255.99,-491.89 1884.08,-744.712Z" fill="url(#mg)"/><g clip-path="url(#mcb)" fill="none" stroke-width="250" stroke-linecap="square"><path d="M4187.01,3033.91c33.233,248.238 66.466,496.475 99.699,744.712l-4286.71,-0" stroke="#000"/><path d="M-0,3778.62c627.367,-243.599 1255.99,-491.89 1884.08,-744.712c767.645,0 1535.29,0 2302.93,0" stroke="#fff" stroke-linejoin="miter"/></g><path d="M415.598,3484.54c777.297,-719.363 1475.76,-1400.24 2108.05,-2044.32c404.715,-158.588 821.55,-323.759 1248.51,-495.17c-547.607,610.584 -1151.51,1254.04 -1823.08,1932.14c-520.17,209.105 -1032.22,411.7 -1533.48,607.345Z" fill="url(#mg)"/><g clip-path="url(#mcm)" fill="none" stroke-width="250" stroke-linecap="square"><path d="M3772.16,945.051c-547.607,610.584 -1151.51,1254.04 -1823.08,1932.14c-520.17,209.105 -1032.22,411.7 -1533.48,607.345" stroke="#000"/><path d="M415.598,3484.54c777.297,-719.363 1475.76,-1400.24 2108.05,-2044.32c404.715,-158.588 821.55,-323.759 1248.51,-495.17" stroke="#fff" stroke-linejoin="miter"/></g><path d="M2575.7,1305.06l-1740.58,0c21.608,-194.559 43.215,-389.119 64.823,-583.678l3147.9,0c-485.752,195.366 -976.426,389.899 -1472.14,583.678Z" fill="url(#mg)"/><g clip-path="url(#mct)" fill="none" stroke-width="250" stroke-linecap="square"><path d="M4047.85,721.381c-485.752,195.366 -976.426,389.899 -1472.14,583.678c-580.193,0 -1160.39,0 -1740.58,0" stroke="#000"/><path d="M835.121,1305.06c21.608,-194.559 43.215,-389.119 64.823,-583.678l3147.9,0" stroke="#fff" stroke-linejoin="miter"/></g></svg>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-120 600 4527 3300" role="img" aria-label="Zaisma"><clipPath id="_clip1"><rect x="0" y="0" width="4500" height="4500"/></clipPath><g clip-path="url(#_clip1)"><g><g><path d="M1884.08,3033.91c767.645,0 1535.29,0 2302.93,0c33.233,248.238 66.466,496.475 99.699,744.712l-4286.71,-0c627.367,-243.599 1255.99,-491.89 1884.08,-744.712Z" style="fill:#fff;fill-rule:nonzero;"/><clipPath id="_clip2"><path d="M1884.08,3033.91c767.645,0 1535.29,0 2302.93,0c33.233,248.238 66.466,496.475 99.699,744.712l-4286.71,-0c627.367,-243.599 1255.99,-491.89 1884.08,-744.712Z" clip-rule="nonzero"/></clipPath><g clip-path="url(#_clip2)"><path d="M4187.01,3033.91c33.233,248.238 66.466,496.475 99.699,744.712l-4286.71,-0" style="fill:none;fill-rule:nonzero;stroke:#171c32;stroke-width:250px;"/><path d="M-0,3778.62c627.367,-243.599 1255.99,-491.89 1884.08,-744.712c767.645,0 1535.29,0 2302.93,0" style="fill:none;fill-rule:nonzero;stroke:#6577ce;stroke-width:250px;stroke-linejoin:miter;"/></g><path d="M415.598,3484.54c777.297,-719.363 1475.76,-1400.24 2108.05,-2044.32c404.715,-158.588 821.55,-323.759 1248.51,-495.17c-547.607,610.584 -1151.51,1254.04 -1823.08,1932.14c-520.17,209.105 -1032.22,411.7 -1533.48,607.345Z" style="fill:#fff;fill-rule:nonzero;"/><clipPath id="_clip3"><path d="M415.598,3484.54c777.297,-719.363 1475.76,-1400.24 2108.05,-2044.32c404.715,-158.588 821.55,-323.759 1248.51,-495.17c-547.607,610.584 -1151.51,1254.04 -1823.08,1932.14c-520.17,209.105 -1032.22,411.7 -1533.48,607.345Z" clip-rule="nonzero"/></clipPath><g clip-path="url(#_clip3)"><path d="M3772.16,945.051c-547.607,610.584 -1151.51,1254.04 -1823.08,1932.14c-520.17,209.105 -1032.22,411.7 -1533.48,607.345" style="fill:none;fill-rule:nonzero;stroke:#17321a;stroke-width:250px;"/><path d="M415.598,3484.54c777.297,-719.363 1475.76,-1400.24 2108.05,-2044.32c404.715,-158.588 821.55,-323.759 1248.51,-495.17" style="fill:none;fill-rule:nonzero;stroke:#53ae48;stroke-width:250px;stroke-linejoin:miter;"/></g><path d="M2575.7,1305.06l-1740.58,0c21.608,-194.559 43.215,-389.119 64.823,-583.678l3147.9,0c-485.752,195.366 -976.426,389.899 -1472.14,583.678Z" style="fill:#fff;fill-rule:nonzero;"/><clipPath id="_clip4"><path d="M2575.7,1305.06l-1740.58,0c21.608,-194.559 43.215,-389.119 64.823,-583.678l3147.9,0c-485.752,195.366 -976.426,389.899 -1472.14,583.678Z" clip-rule="nonzero"/></clipPath><g clip-path="url(#_clip4)"><path d="M4047.85,721.381c-485.752,195.366 -976.426,389.899 -1472.14,583.678c-580.193,0 -1160.39,0 -1740.58,0" style="fill:none;fill-rule:nonzero;stroke:#321718;stroke-width:250px;"/><path d="M835.121,1305.06c21.608,-194.559 43.215,-389.119 64.823,-583.678l3147.9,0" style="fill:none;fill-rule:nonzero;stroke:#af5053;stroke-width:250px;"/></g></g></g></g></svg>
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-120 1080 9988 2820" role="img" aria-label="Zaisma"><defs><linearGradient id="wg" x1="0" y1="721" x2="0" y2="3779" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#cdcdcd"/><stop offset="1" stop-color="#333"/></linearGradient><linearGradient id="wh" x1="0" y1="1879" x2="0" y2="3779" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#cdcdcd"/><stop offset="1" stop-color="#333"/></linearGradient><clipPath id="wcb"><path d="M1884.08,3033.91c767.645,0 1535.29,0 2302.93,0c33.233,248.238 66.466,496.475 99.699,744.712l-4286.71,-0c627.367,-243.599 1255.99,-491.89 1884.08,-744.712Z"/></clipPath><clipPath id="wcm"><path d="M415.598,3484.54c777.297,-719.363 1475.76,-1400.24 2108.05,-2044.32c404.715,-158.588 821.55,-323.759 1248.51,-495.17c-547.607,610.584 -1151.51,1254.04 -1823.08,1932.14c-520.17,209.105 -1032.22,411.7 -1533.48,607.345Z"/></clipPath><clipPath id="wct"><path d="M2575.7,1305.06l-1740.58,0c21.608,-194.559 43.215,-389.119 64.823,-583.678l3147.9,0c-485.752,195.366 -976.426,389.899 -1472.14,583.678Z"/></clipPath></defs><g transform="translate(0 3779) scale(0.62132) translate(0 -3779)"><path d="M1884.08,3033.91c767.645,0 1535.29,0 2302.93,0c33.233,248.238 66.466,496.475 99.699,744.712l-4286.71,-0c627.367,-243.599 1255.99,-491.89 1884.08,-744.712Z" fill="url(#wg)"/><g clip-path="url(#wcb)" fill="none" stroke-width="250" stroke-linecap="square"><path d="M4187.01,3033.91c33.233,248.238 66.466,496.475 99.699,744.712l-4286.71,-0" stroke="#000"/><path d="M-0,3778.62c627.367,-243.599 1255.99,-491.89 1884.08,-744.712c767.645,0 1535.29,0 2302.93,0" stroke="#fff" stroke-linejoin="miter"/></g><path d="M415.598,3484.54c777.297,-719.363 1475.76,-1400.24 2108.05,-2044.32c404.715,-158.588 821.55,-323.759 1248.51,-495.17c-547.607,610.584 -1151.51,1254.04 -1823.08,1932.14c-520.17,209.105 -1032.22,411.7 -1533.48,607.345Z" fill="url(#wg)"/><g clip-path="url(#wcm)" fill="none" stroke-width="250" stroke-linecap="square"><path d="M3772.16,945.051c-547.607,610.584 -1151.51,1254.04 -1823.08,1932.14c-520.17,209.105 -1032.22,411.7 -1533.48,607.345" stroke="#000"/><path d="M415.598,3484.54c777.297,-719.363 1475.76,-1400.24 2108.05,-2044.32c404.715,-158.588 821.55,-323.759 1248.51,-495.17" stroke="#fff" stroke-linejoin="miter"/></g><path d="M2575.7,1305.06l-1740.58,0c21.608,-194.559 43.215,-389.119 64.823,-583.678l3147.9,0c-485.752,195.366 -976.426,389.899 -1472.14,583.678Z" fill="url(#wg)"/><g clip-path="url(#wct)" fill="none" stroke-width="250" stroke-linecap="square"><path d="M4047.85,721.381c-485.752,195.366 -976.426,389.899 -1472.14,583.678c-580.193,0 -1160.39,0 -1740.58,0" stroke="#000"/><path d="M835.121,1305.06c21.608,-194.559 43.215,-389.119 64.823,-583.678l3147.9,0" stroke="#fff" stroke-linejoin="miter"/></g></g><g transform="translate(-1953 0)"><path d="M4957,1879 L5877,1879 L6132,3779 L5212,3779 L4837,3449 L4765,2909 L5051,2579 L5601,2579 L5557,2249 L4677,2249 Z M5140,2949 L5650,2949 L5712,3409 L5202,3409 Z M6077,1879 L6447,1879 L6702,3779 L6332,3779 Z M5991,1239 L6541,1239 L6412,1619 L6042,1619 Z M6647,1879 L7847,1879 L7667,2249 L7067,2249 L7120,2644 L7950,2644 L8102,3779 L6902,3779 L7082,3409 L7682,3409 L7629,3014 L6799,3014 Z M8091,2209 L8377,1879 L9997,1879 L10252,3779 L9882,3779 L9677,2249 L9257,2249 L9462,3779 L9092,3779 L8887,2249 L8467,2249 L8672,3779 L8302,3779 Z M10527,1879 L11447,1879 L11702,3779 L10782,3779 L10407,3449 L10335,2909 L10621,2579 L11171,2579 L11127,2249 L10247,2249 Z M10710,2949 L11220,2949 L11282,3409 L10772,3409 Z" fill="#000" fill-rule="evenodd"/><path d="M4957.0,1879.0 L5877.0,1879.0 L5809.1,1956.7 L4995.6,1956.7 Z M4837.0,3449.0 L4765.0,2909.0 L4846.6,2933.4 L4910.1,3409.9 Z M4765.0,2909.0 L5051.0,2579.0 L5086.5,2656.7 L4846.6,2933.4 Z M5051.0,2579.0 L5601.0,2579.0 L5689.7,2656.7 L5086.5,2656.7 Z M5601.0,2579.0 L5557.0,2249.0 L5625.0,2171.3 L5689.7,2656.7 Z M4677.0,2249.0 L4957.0,1879.0 L4995.6,1956.7 L4833.2,2171.3 Z M5650.0,2949.0 L5712.0,3409.0 L5800.8,3486.7 L5717.9,2871.3 Z M5712.0,3409.0 L5202.0,3409.0 L5134.1,3486.7 L5800.8,3486.7 Z M6077.0,1879.0 L6447.0,1879.0 L6379.1,1956.7 L6165.8,1956.7 Z M6332.0,3779.0 L6077.0,1879.0 L6165.8,1956.7 L6399.9,3701.3 Z M5991.0,1239.0 L6541.0,1239.0 L6432.6,1316.7 L6079.8,1316.7 Z M6042.0,1619.0 L5991.0,1239.0 L6079.8,1316.7 L6109.9,1541.3 Z M6647.0,1879.0 L7847.0,1879.0 L7722.8,1956.7 L6735.8,1956.7 Z M7120.0,2644.0 L7950.0,2644.0 L7882.0,2721.7 L7052.1,2721.7 Z M6902.0,3779.0 L7082.0,3409.0 L7130.6,3486.7 L7026.2,3701.3 Z M7082.0,3409.0 L7682.0,3409.0 L7770.8,3486.7 L7130.6,3486.7 Z M7682.0,3409.0 L7629.0,3014.0 L7696.9,2936.3 L7770.8,3486.7 Z M6799.0,3014.0 L6647.0,1879.0 L6735.8,1956.7 L6867.0,2936.3 Z M8091.0,2209.0 L8377.0,1879.0 L8412.5,1956.7 L8172.6,2233.4 Z M8377.0,1879.0 L9997.0,1879.0 L9929.1,1956.7 L8412.5,1956.7 Z M9882.0,3779.0 L9677.0,2249.0 L9745.0,2171.3 L9950.0,3701.3 Z M9092.0,3779.0 L8887.0,2249.0 L8955.0,2171.3 L9160.0,3701.3 Z M8302.0,3779.0 L8091.0,2209.0 L8172.6,2233.4 L8369.9,3701.3 Z M10527.0,1879.0 L11447.0,1879.0 L11379.1,1956.7 L10565.6,1956.7 Z M10407.0,3449.0 L10335.0,2909.0 L10416.6,2933.4 L10480.1,3409.9 Z M10335.0,2909.0 L10621.0,2579.0 L10656.5,2656.7 L10416.6,2933.4 Z M10621.0,2579.0 L11171.0,2579.0 L11259.7,2656.7 L10656.5,2656.7 Z M11171.0,2579.0 L11127.0,2249.0 L11195.0,2171.3 L11259.7,2656.7 Z M10247.0,2249.0 L10527.0,1879.0 L10565.6,1956.7 L10403.2,2171.3 Z M11220.0,2949.0 L11282.0,3409.0 L11370.8,3486.7 L11287.9,2871.3 Z M11282.0,3409.0 L10772.0,3409.0 L10704.1,3486.7 L11370.8,3486.7 Z" fill="#fff" stroke="#fff" stroke-width="2" stroke-linejoin="miter"/><path d="M5877.0,1879.0 L6132.0,3779.0 L6043.2,3701.3 L5809.1,1956.7 Z M6132.0,3779.0 L5212.0,3779.0 L5241.3,3701.3 L6043.2,3701.3 Z M5212.0,3779.0 L4837.0,3449.0 L4910.1,3409.9 L5241.3,3701.3 Z M5557.0,2249.0 L4677.0,2249.0 L4833.2,2171.3 L5625.0,2171.3 Z M5140.0,2949.0 L5650.0,2949.0 L5717.9,2871.3 L5051.2,2871.3 Z M5202.0,3409.0 L5140.0,2949.0 L5051.2,2871.3 L5134.1,3486.7 Z M6447.0,1879.0 L6702.0,3779.0 L6613.2,3701.3 L6379.1,1956.7 Z M6702.0,3779.0 L6332.0,3779.0 L6399.9,3701.3 L6613.2,3701.3 Z M6541.0,1239.0 L6412.0,1619.0 L6356.3,1541.3 L6432.6,1316.7 Z M6412.0,1619.0 L6042.0,1619.0 L6109.9,1541.3 L6356.3,1541.3 Z M7847.0,1879.0 L7667.0,2249.0 L7618.4,2171.3 L7722.8,1956.7 Z M7667.0,2249.0 L7067.0,2249.0 L6978.2,2171.3 L7618.4,2171.3 Z M7067.0,2249.0 L7120.0,2644.0 L7052.1,2721.7 L6978.2,2171.3 Z M7950.0,2644.0 L8102.0,3779.0 L8013.2,3701.3 L7882.0,2721.7 Z M8102.0,3779.0 L6902.0,3779.0 L7026.2,3701.3 L8013.2,3701.3 Z M7629.0,3014.0 L6799.0,3014.0 L6867.0,2936.3 L7696.9,2936.3 Z M9997.0,1879.0 L10252.0,3779.0 L10163.2,3701.3 L9929.1,1956.7 Z M10252.0,3779.0 L9882.0,3779.0 L9950.0,3701.3 L10163.2,3701.3 Z M9677.0,2249.0 L9257.0,2249.0 L9168.2,2171.3 L9745.0,2171.3 Z M9257.0,2249.0 L9462.0,3779.0 L9373.2,3701.3 L9168.2,2171.3 Z M9462.0,3779.0 L9092.0,3779.0 L9160.0,3701.3 L9373.2,3701.3 Z M8887.0,2249.0 L8467.0,2249.0 L8378.2,2171.3 L8955.0,2171.3 Z M8467.0,2249.0 L8672.0,3779.0 L8583.2,3701.3 L8378.2,2171.3 Z M8672.0,3779.0 L8302.0,3779.0 L8369.9,3701.3 L8583.2,3701.3 Z M11447.0,1879.0 L11702.0,3779.0 L11613.2,3701.3 L11379.1,1956.7 Z M11702.0,3779.0 L10782.0,3779.0 L10811.3,3701.3 L11613.2,3701.3 Z M10782.0,3779.0 L10407.0,3449.0 L10480.1,3409.9 L10811.3,3701.3 Z M11127.0,2249.0 L10247.0,2249.0 L10403.2,2171.3 L11195.0,2171.3 Z M10710.0,2949.0 L11220.0,2949.0 L11287.9,2871.3 L10621.2,2871.3 Z M10772.0,3409.0 L10710.0,2949.0 L10621.2,2871.3 L10704.1,3486.7 Z" fill="#000" stroke="#000" stroke-width="2" stroke-linejoin="miter"/><path d="M4995.6,1956.7 L5809.1,1956.7 L6043.2,3701.3 L5241.3,3701.3 L4910.1,3409.9 L4846.6,2933.4 L5086.5,2656.7 L5689.7,2656.7 L5625.0,2171.3 L4833.2,2171.3 Z M5051.2,2871.3 L5717.9,2871.3 L5800.8,3486.7 L5134.1,3486.7 Z M6165.8,1956.7 L6379.1,1956.7 L6613.2,3701.3 L6399.9,3701.3 Z M6079.8,1316.7 L6432.6,1316.7 L6356.3,1541.3 L6109.9,1541.3 Z M6735.8,1956.7 L7722.8,1956.7 L7618.4,2171.3 L6978.2,2171.3 L7052.1,2721.7 L7882.0,2721.7 L8013.2,3701.3 L7026.2,3701.3 L7130.6,3486.7 L7770.8,3486.7 L7696.9,2936.3 L6867.0,2936.3 Z M8172.6,2233.4 L8412.5,1956.7 L9929.1,1956.7 L10163.2,3701.3 L9950.0,3701.3 L9745.0,2171.3 L9168.2,2171.3 L9373.2,3701.3 L9160.0,3701.3 L8955.0,2171.3 L8378.2,2171.3 L8583.2,3701.3 L8369.9,3701.3 Z M10565.6,1956.7 L11379.1,1956.7 L11613.2,3701.3 L10811.3,3701.3 L10480.1,3409.9 L10416.6,2933.4 L10656.5,2656.7 L11259.7,2656.7 L11195.0,2171.3 L10403.2,2171.3 Z M10621.2,2871.3 L11287.9,2871.3 L11370.8,3486.7 L10704.1,3486.7 Z" fill="url(#wh)" fill-rule="evenodd"/></g></svg>
|