@onceworlds/cli 0.10.0 → 0.11.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/dist/cli.js +288 -175
- package/dist/emulator.js +10346 -6072
- package/dist/sdk.js +3 -3
- package/dist/v1/match.js +6 -2
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -17,36 +17,36 @@ import { pathToFileURL } from "node:url";
|
|
|
17
17
|
|
|
18
18
|
// ../protocol/src/avatar.ts
|
|
19
19
|
var BODY_COLORS = [
|
|
20
|
-
"#
|
|
21
|
-
"#
|
|
22
|
-
"#
|
|
23
|
-
"#
|
|
24
|
-
"#
|
|
25
|
-
"#
|
|
26
|
-
"#
|
|
27
|
-
"#
|
|
28
|
-
"#
|
|
29
|
-
"#
|
|
30
|
-
"#
|
|
31
|
-
"#
|
|
20
|
+
"#f6dcc5",
|
|
21
|
+
"#ebc29f",
|
|
22
|
+
"#d4a07a",
|
|
23
|
+
"#b07a55",
|
|
24
|
+
"#875839",
|
|
25
|
+
"#5e3d29",
|
|
26
|
+
"#93c9bb",
|
|
27
|
+
"#a8c595",
|
|
28
|
+
"#94bcd3",
|
|
29
|
+
"#ab9bd9",
|
|
30
|
+
"#dca3ad",
|
|
31
|
+
"#b3bccb"
|
|
32
32
|
];
|
|
33
33
|
var COLORS = [
|
|
34
|
-
"#
|
|
35
|
-
"#
|
|
36
|
-
"#
|
|
37
|
-
"#
|
|
38
|
-
"#
|
|
39
|
-
"#
|
|
40
|
-
"#
|
|
41
|
-
"#
|
|
42
|
-
"#
|
|
43
|
-
"#
|
|
44
|
-
"#
|
|
45
|
-
"#
|
|
46
|
-
"#
|
|
47
|
-
"#
|
|
48
|
-
"#
|
|
49
|
-
"#
|
|
34
|
+
"#c95f56",
|
|
35
|
+
"#dc8a4f",
|
|
36
|
+
"#e2bd52",
|
|
37
|
+
"#9cc27a",
|
|
38
|
+
"#5a8a5e",
|
|
39
|
+
"#4f9a8e",
|
|
40
|
+
"#6fb7c8",
|
|
41
|
+
"#5b87c5",
|
|
42
|
+
"#465089",
|
|
43
|
+
"#8b68a8",
|
|
44
|
+
"#dc8fae",
|
|
45
|
+
"#f2eee6",
|
|
46
|
+
"#a9a8ad",
|
|
47
|
+
"#5d5c63",
|
|
48
|
+
"#2b2a30",
|
|
49
|
+
"#80604d"
|
|
50
50
|
];
|
|
51
51
|
var FACES = ["Happy", "Grin", "Shades", "Wink", "Surprised", "Determined", "Joyful", "Shy"];
|
|
52
52
|
var HATS = [
|
|
@@ -67,11 +67,13 @@ var HATS = [
|
|
|
67
67
|
"Bob",
|
|
68
68
|
"Spikes",
|
|
69
69
|
"Bun",
|
|
70
|
-
"Curls"
|
|
70
|
+
"Curls",
|
|
71
|
+
"Long",
|
|
72
|
+
"Mop"
|
|
71
73
|
];
|
|
72
74
|
var HAIR_FROM = 12;
|
|
73
75
|
var PATTERNS = ["Plain", "Band", "Spots", "Stripes", "Star"];
|
|
74
|
-
var HAIR_COLORS = ["#
|
|
76
|
+
var HAIR_COLORS = ["#7d5233", "#583a27", "#41291d", "#2f1f17", "#251812", "#1c130f"];
|
|
75
77
|
var SHOE_COLOR = "#2d2a36";
|
|
76
78
|
var INK = "#1d1a24";
|
|
77
79
|
var AVATAR_PARTS = {
|
|
@@ -82,6 +84,7 @@ var AVATAR_PARTS = {
|
|
|
82
84
|
hat: HATS.length,
|
|
83
85
|
top: PATTERNS.length
|
|
84
86
|
};
|
|
87
|
+
var TONAL = [[5, 6], [3, 4], [6, 7], [9, 8], [10, 0], [12, 13]];
|
|
85
88
|
function defaultAvatar(id) {
|
|
86
89
|
let h = 2166136261;
|
|
87
90
|
for (const c of id) h = Math.imul(h ^ c.charCodeAt(0), 16777619) >>> 0;
|
|
@@ -90,11 +93,11 @@ function defaultAvatar(id) {
|
|
|
90
93
|
return h % n;
|
|
91
94
|
};
|
|
92
95
|
const skin = pick(BODY_COLORS.length);
|
|
93
|
-
const shirt = pick(COLORS.length);
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const
|
|
97
|
-
return { skin, shirt, pants, face: pick(FACES.length), hat, top
|
|
96
|
+
const shirt = skin >= 6 ? TONAL[skin - 6][pick(2)] : pick(COLORS.length);
|
|
97
|
+
const pants = pick(2) === 0 ? shirt : [8, 13, 14, 15][pick(4)];
|
|
98
|
+
const hat = pick(5) === 0 ? 1 : HAIR_FROM + pick(HATS.length - HAIR_FROM);
|
|
99
|
+
const top = pick(2) === 0 ? 0 : pick(PATTERNS.length);
|
|
100
|
+
return { skin, shirt, pants, face: pick(FACES.length), hat, top };
|
|
98
101
|
}
|
|
99
102
|
function avatarColors(c) {
|
|
100
103
|
const skin = BODY_COLORS[c.skin] ?? BODY_COLORS[0];
|
|
@@ -114,52 +117,60 @@ function hairColor(skin) {
|
|
|
114
117
|
function patternMark(shirt) {
|
|
115
118
|
return isLight(shirt) ? shade(shirt, -0.28) : shade(shirt, 0.42);
|
|
116
119
|
}
|
|
117
|
-
var EDGE = 'stroke="rgba(
|
|
118
|
-
var
|
|
119
|
-
var
|
|
120
|
+
var EDGE = 'stroke="rgba(52,32,44,0.16)" stroke-width="1.2"';
|
|
121
|
+
var SOFT_EDGE = 'stroke="rgba(52,32,44,0.1)" stroke-width="1.2"';
|
|
122
|
+
var TORSO = "M60 86C68 86 74.5 88 77.8 92.5C80.5 96.5 81.2 102 81.6 110C82 118 82 125 82 130C82 138 75 142 60 142S38 138 38 130C38 125 38 118 38.4 110C38.8 102 39.5 96.5 42.2 92.5C45.5 88 52 86 60 86Z";
|
|
123
|
+
var HEAD = "translate(60 52) scale(1.2) translate(-60 -58)";
|
|
124
|
+
var SKULL = "M60 27C79.5 27 91.5 38.5 91.5 56C91.5 74 78.5 86.5 60 86.5C41.5 86.5 28.5 74 28.5 56C28.5 38.5 40.5 27 60 27Z";
|
|
120
125
|
var MIRROR = "matrix(-1 0 0 1 120 0)";
|
|
121
126
|
var VIEWBOX = { full: "0 0 120 210", head: "12 2 96 96", torso: "22 82 76 66" };
|
|
122
|
-
function avatarSvg(config, view2, title) {
|
|
127
|
+
function avatarSvg(config, view2, title, idle = false) {
|
|
123
128
|
const viewBox = VIEWBOX[view2];
|
|
124
129
|
const label = title ? `<title>${title.replace(/[<>&"']/g, "")}</title>` : "";
|
|
125
|
-
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="${viewBox}" role="img">${label}${figure(config, view2)}</svg>`;
|
|
130
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="${viewBox}" role="img">${label}${idle ? `<style>${idleCss(config)}</style>` : ""}${figure(config, view2)}</svg>`;
|
|
126
131
|
}
|
|
127
132
|
function figure(c, view2) {
|
|
128
133
|
const { skin, shirt, pants } = avatarColors(c);
|
|
129
134
|
const parts = [];
|
|
130
135
|
if (view2 === "full") {
|
|
131
|
-
parts.push('<ellipse cx="60" cy="
|
|
136
|
+
parts.push('<ellipse cx="60" cy="199" rx="24" ry="3.4" fill="rgba(0,0,0,0.13)"/>');
|
|
132
137
|
const l = leg(pants);
|
|
133
138
|
parts.push(l, `<g transform="${MIRROR}">${l}</g>`);
|
|
134
139
|
}
|
|
135
140
|
const a = arm(shirt, skin);
|
|
136
|
-
parts.push(a
|
|
137
|
-
parts.push(
|
|
138
|
-
parts.push(head(c, skin, shirt));
|
|
141
|
+
parts.push(`<g class="owa-body"><g class="owa-arm-l">${a}</g><g class="owa-arm-r"><g transform="${MIRROR}">${a}</g></g>${torso(c, shirt)}</g>`);
|
|
142
|
+
if (view2 !== "torso") parts.push(`<g class="owa-rise"><g class="owa-head">${head(c, skin, shirt)}</g></g>`);
|
|
139
143
|
return parts.join("");
|
|
140
144
|
}
|
|
141
145
|
function leg(pants) {
|
|
142
|
-
|
|
146
|
+
const foot = "M40 195C40 186.5 44.5 181.5 50 181.5S60 186.5 60 195V195.7a1.8 1.8 0 0 1-1.8 1.8H41.8a1.8 1.8 0 0 1-1.8-1.8z";
|
|
147
|
+
return `<path d="M42 136H59V183a8.5 8.5 0 0 1-17 0z" fill="${pants}" ${SOFT_EDGE}/><path d="M42 136H59V147H42z" fill="rgba(0,0,0,0.13)"/><path d="${foot}" fill="${pants}"/><path d="${foot}" fill="rgba(20,10,20,0.2)" ${SOFT_EDGE}/>`;
|
|
143
148
|
}
|
|
144
149
|
function arm(shirt, skin) {
|
|
145
|
-
const sleeve = "
|
|
146
|
-
return `<path d="${sleeve}" fill="none" stroke="rgba(
|
|
150
|
+
const sleeve = "M33.5 97Q30.6 108.5 30.6 119";
|
|
151
|
+
return `<path d="${sleeve}" fill="none" stroke="rgba(52,32,44,0.1)" stroke-width="13.2" stroke-linecap="round"/><path d="${sleeve}" fill="none" stroke="${shirt}" stroke-width="12" stroke-linecap="round"/><path d="M36 99Q33.6 109 33.8 118" fill="none" stroke="rgba(0,0,0,0.07)" stroke-width="4" stroke-linecap="round"/><path d="M30.6 120.5c-4.3 0-6.6 3.6-6.6 7.4 0 4.2 3 6.8 6.6 6.8s6.4-2.6 6.4-6.8c0-3.8-2.2-7.4-6.4-7.4z" fill="${skin}" ${EDGE}/><path d="M34.2 125.4q1.5 1.3 1.5 3.4" fill="none" stroke="rgba(0,0,0,0.15)" stroke-width="1.2" stroke-linecap="round"/>`;
|
|
147
152
|
}
|
|
148
153
|
function torso(c, shirt) {
|
|
149
|
-
return `<path d="${TORSO}" fill="${shirt}"/>${pattern(c.top, shirt)}<path d="${TORSO}" fill="none" ${
|
|
154
|
+
return `<path d="${TORSO}" fill="${shirt}"/>${pattern(c.top, shirt)}<ellipse cx="50" cy="104" rx="10" ry="13" fill="rgba(255,255,255,0.08)"/><path d="M74 91.5C79 95 81 101 81.4 110C81.8 118 82 125 82 130C82 138 75 142 60 142C70 139.5 75.5 134 76 125C76.5 114 76.5 100 74 91.5Z" fill="rgba(0,0,0,0.07)"/><path d="M38.5 128C42 136 50 139 60 139S78 136 81.5 128C81.5 137 75 142 60 142S38.5 137 38.5 128Z" fill="rgba(0,0,0,0.06)"/><ellipse cx="60" cy="89" rx="14" ry="4.5" fill="rgba(0,0,0,0.12)"/><path d="${TORSO}" fill="none" ${SOFT_EDGE}/>`;
|
|
150
155
|
}
|
|
156
|
+
var COVERING = /* @__PURE__ */ new Set([2, 3]);
|
|
151
157
|
function head(c, skin, shirt) {
|
|
152
|
-
const
|
|
158
|
+
const tone = hairColor(c.skin);
|
|
159
|
+
const hair = c.hat >= HAIR_FROM ? hairstyle(c.hat - HAIR_FROM, tone) : c.hat > 0 ? COVERING.has(c.hat) ? { back: "", front: sideburns(tone) } : hairstyle(0, tone) : null;
|
|
153
160
|
const parts = [];
|
|
154
161
|
if (hair) parts.push(hair.back);
|
|
155
|
-
|
|
156
|
-
parts.push(`<
|
|
157
|
-
parts.push('<path d="
|
|
158
|
-
parts.push(faceSvg(c.face));
|
|
162
|
+
parts.push(ears(skin));
|
|
163
|
+
parts.push(`<path d="${SKULL}" fill="${skin}" ${EDGE}/>`);
|
|
164
|
+
parts.push('<ellipse cx="51" cy="50" rx="17" ry="13" fill="rgba(255,255,255,0.1)"/><path d="M84 43C89.5 49.5 91 61 87.5 70.5C84 79.5 75 85.5 63.5 86.2C76 81.5 84 72 85.4 60C86.1 53 85.6 47.5 84 43Z" fill="rgba(0,0,0,0.07)"/><path d="M38 78C45 84 53 86.5 60 86.5S75 84 82 78C76 82.5 68 84.5 60 84.5S44 82.5 38 78Z" fill="rgba(0,0,0,0.06)"/>');
|
|
165
|
+
parts.push(`<g class="owa-look">${faceSvg(c.face, tone)}</g>`);
|
|
159
166
|
if (hair) parts.push(hair.front);
|
|
160
|
-
|
|
167
|
+
if (c.hat > 0 && c.hat < HAIR_FROM) parts.push(headwear(c.hat, shirt));
|
|
161
168
|
return `<g transform="${HEAD}">${parts.join("")}</g>`;
|
|
162
169
|
}
|
|
170
|
+
function ears(skin) {
|
|
171
|
+
const ear = `<ellipse cx="29.5" cy="61" rx="5" ry="6.4" fill="${skin}" ${EDGE}/><path d="M29.4 58.2q-2.2 2.8 0 5.8" fill="none" stroke="rgba(0,0,0,0.16)" stroke-width="1.4" stroke-linecap="round"/>`;
|
|
172
|
+
return `${ear}<g transform="${MIRROR}">${ear}</g>`;
|
|
173
|
+
}
|
|
163
174
|
function band(y0, x0, y1, x1, fill) {
|
|
164
175
|
return `<path d="M${x0} ${y0}H${120 - x0}L${120 - x1} ${y1}H${x1}z" fill="${fill}"/>`;
|
|
165
176
|
}
|
|
@@ -167,13 +178,13 @@ function pattern(style, shirt) {
|
|
|
167
178
|
const mark = patternMark(shirt);
|
|
168
179
|
switch (style) {
|
|
169
180
|
case 1:
|
|
170
|
-
return band(
|
|
181
|
+
return band(107, 38.7, 117, 38.15, mark);
|
|
171
182
|
case 2:
|
|
172
|
-
return [[
|
|
183
|
+
return [[47, 98, 3.2], [71, 101, 4], [55, 113, 3.6], [74, 120, 3.2], [46, 127, 3.2], [64, 132, 2.8]].map(([x, y, r]) => `<circle cx="${x}" cy="${y}" r="${r}" fill="${mark}"/>`).join("");
|
|
173
184
|
case 3:
|
|
174
|
-
return band(98,
|
|
185
|
+
return band(98, 40.1, 103, 39.1, mark) + band(110, 38.4, 115.5, 38.2, mark) + band(122.5, 38.05, 128, 38.02, mark);
|
|
175
186
|
case 4:
|
|
176
|
-
return `<path transform="translate(60
|
|
187
|
+
return `<path transform="translate(60 114) scale(1.35)" d="${STAR_PATH}" fill="${mark}"/>`;
|
|
177
188
|
default:
|
|
178
189
|
return "";
|
|
179
190
|
}
|
|
@@ -181,94 +192,150 @@ function pattern(style, shirt) {
|
|
|
181
192
|
var STAR_PATH = "M0-8l2.4 5 5.6.8-4 3.9.9 5.5L0 4.6l-4.9 2.6.9-5.5-4-3.9 5.6-.8z";
|
|
182
193
|
function hairstyle(style, color) {
|
|
183
194
|
const fill = `fill="${color}"`;
|
|
184
|
-
const
|
|
195
|
+
const sheen = (d) => `<path d="${d}" fill="none" stroke="rgba(255,255,255,0.15)" stroke-width="3" stroke-linecap="round"/>`;
|
|
196
|
+
const strand = (d) => `<path d="${d}" fill="none" stroke="rgba(0,0,0,0.14)" stroke-width="1.6" stroke-linecap="round"/>`;
|
|
185
197
|
switch (style) {
|
|
186
198
|
case 1:
|
|
187
|
-
return {
|
|
199
|
+
return {
|
|
200
|
+
back: "",
|
|
201
|
+
front: `<path d="M29 55C28 38 41 25.5 60 25.5S92 38 91 55C88.5 47 84 42.5 78 40.5C66 43 54 43 42 40.5C36 42.5 31.5 47 29 55Z" ${fill}/><path d="M58 27c-2.5-6.5 1.5-11.5 8.5-11-4.2 2-5.2 5.2-3.4 9zM54 27.6c-3.2-3.4-2.4-7.4 1.4-8.8-1.4 2.4-.9 4.8.6 7z" ${fill}/>${sheen("M42 32q8-4.5 17-5")}`
|
|
202
|
+
};
|
|
188
203
|
case 2:
|
|
189
204
|
return {
|
|
190
|
-
back: `<path d="
|
|
191
|
-
front: `<path d="
|
|
205
|
+
back: `<path d="M24 60C22 34 38 19.5 60 19.5S98 34 96 60L95.5 82Q89 87 82 84L81 64H39L38 84Q31 87 24.5 82Z" ${fill}/>`,
|
|
206
|
+
front: `<path d="M26.5 66C25 40 40 22.5 60 22.5S95 40 93.5 66H88.5C88.5 58 87.5 52.5 85.5 49C77 50.5 69 48 63.5 45.5C57 49.5 45 51 34.5 49C32.5 52.5 31.5 58 31.5 66Z" ${fill}/>${sheen("M38 32q10-6.5 22-6.5")}${strand("M63.5 45.5q4-8 13-12")}`
|
|
192
207
|
};
|
|
193
208
|
case 3:
|
|
194
209
|
return {
|
|
195
210
|
back: "",
|
|
196
|
-
front: `<path d="
|
|
211
|
+
front: `<path d="M28 57C27 45 31 37 36 33L34.5 21L44 27L47.5 15.5L55 24L62 15L67 24.5L76.5 18L77.5 29C86 34 92 44 92 57C88 50 82 46 74 46C66 44 54 44 46 46.5C38 48 32 51.5 28 57Z" ${fill}/>${strand("M55 24q-3 10-9 16M67 24.5q1 9-3 15")}`
|
|
197
212
|
};
|
|
198
213
|
case 4:
|
|
199
214
|
return {
|
|
200
|
-
back: `<circle cx="60" cy="
|
|
201
|
-
front: `<path d="
|
|
215
|
+
back: `<circle cx="60" cy="22.5" r="7.5" ${fill}/>`,
|
|
216
|
+
front: `<path d="M27.5 58C26 39 40 24 60 24S94 39 92.5 58C89.5 50 84 46 77 45C67 47.5 53 47.5 43 45C36 46 30.5 50 27.5 58Z" ${fill}/>${sheen("M41 32q9-5 19-5.5")}${strand("M60 24.5q-2 10-8 18")}`
|
|
202
217
|
};
|
|
203
218
|
case 5:
|
|
204
219
|
return {
|
|
205
220
|
back: "",
|
|
206
|
-
front: `<path d="
|
|
221
|
+
front: `<path d="M27.5 56C27.5 38 42 28 60 28S92.5 38 92.5 56C86 49.5 74 46 60 46S34 49.5 27.5 56Z" ${fill}/>${[[30, 55, 6.5], [31, 44, 7.5], [37, 34.5, 8], [47, 27.5, 8.5], [60, 25, 9], [73, 27.5, 8.5], [83, 34.5, 8], [89, 44, 7.5], [90, 55, 6.5]].map(([x, y, r]) => `<circle cx="${x}" cy="${y}" r="${r}" ${fill}/>`).join("")}${sheen("M42 26q6-3.5 12-3.2")}`
|
|
222
|
+
};
|
|
223
|
+
case 6:
|
|
224
|
+
return {
|
|
225
|
+
back: `<path d="M23.5 60C21 33 38 18.5 60 18.5S99 33 96.5 60C98.5 68 95.5 72 97.5 79C99 84 96 88 91 88C87 88 85 86 84 84L81.5 66H38.5L36 84C35 86 33 88 29 88C24 88 21 84 22.5 79C24.5 72 21.5 68 23.5 60Z" ${fill}/>`,
|
|
226
|
+
front: `<path d="M26.5 66C25 40 40 22.5 60 22.5S95 40 93.5 66C92.8 60 91 55 88 51.5C82.5 48 74.5 42 70 34.5C66 42.5 55 48.5 40.5 49.5C35.5 53 32 58.5 31.5 66C30 67 28 67 26.5 66Z" ${fill}/>${sheen("M36 36q9-9.5 22-11")}${strand("M70 34.5q-6 9.5-18 13.5M70 34.5q5 9 14 13.5")}`
|
|
227
|
+
};
|
|
228
|
+
case 7:
|
|
229
|
+
return {
|
|
230
|
+
back: "",
|
|
231
|
+
front: `<path d="M26 58C22 50 24.5 41 30 36C30.5 27 38.5 21 46.5 21.5C51 16.5 60 15.5 66 19C73 16.5 82 19.5 85 26C92 29.5 96 38.5 94 47C96 51 95.5 55.5 93 58.5C91.5 53.5 88.5 49.5 84.5 47.5C80.5 49.5 75 49.5 70.5 47C66 50.5 58 51.5 52 49C47 51.5 40 51.5 35.5 48.5C31.5 50.5 28.5 54 27 58.5Z" ${fill}/>${sheen("M38 30q7-6 16-6.5")}${strand("M52 49q1-8 7-13M70.5 47q-1-7 3-12M35.5 48.5q-1-6 3-10")}`
|
|
207
232
|
};
|
|
208
233
|
default:
|
|
209
234
|
return {
|
|
210
235
|
back: "",
|
|
211
|
-
front: `<path d="
|
|
236
|
+
front: `<path d="M27.5 58C25 38 38 21 60 21S95 38 92.5 58C91.5 54 90 50.5 87.5 47.5C80 48 72 46.5 66 43.5C60 48.5 48 51 37.5 49.5C34.5 52 32.5 55 31.5 58.5Z" ${fill}/>${sheen("M41 30q9-5.5 20-6")}${strand("M66 43.5q6-9 15-12")}`
|
|
212
237
|
};
|
|
213
238
|
}
|
|
214
239
|
}
|
|
215
|
-
function
|
|
216
|
-
return `<
|
|
240
|
+
function sideburns(color) {
|
|
241
|
+
return `<path d="M27.5 60C26.5 50 28 44 32 39L38 37.5C35 42 33 48 32.2 58.5ZM92.5 60C93.5 50 92 44 88 39L82 37.5C85 42 87 48 87.8 58.5Z" fill="${color}"/>`;
|
|
217
242
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
243
|
+
var BROW = "rgba(42,26,34,0.58)";
|
|
244
|
+
var BLUSH = (alpha) => `<ellipse cx="41" cy="71" rx="4.6" ry="2.5" fill="rgba(255,100,120,${alpha})"/><ellipse cx="79" cy="71" rx="4.6" ry="2.5" fill="rgba(255,100,120,${alpha})"/>`;
|
|
245
|
+
var NOSE = '<path d="M58.4 69.2q1.6 1.5 3.2 0" fill="none" stroke="rgba(0,0,0,0.2)" stroke-width="1.6" stroke-linecap="round"/>';
|
|
246
|
+
function eye(x, y, dx = 0.9, dy = 0, size = 1) {
|
|
247
|
+
const px = x + dx;
|
|
248
|
+
const py = y + dy;
|
|
249
|
+
return `<ellipse cx="${x}" cy="${y}" rx="${3.7 * size}" ry="${4.7 * size}" fill="#fff" stroke="rgba(0,0,0,0.12)" stroke-width="1"/><ellipse cx="${px}" cy="${py}" rx="${3.2 * Math.min(size, 1)}" ry="${4.2 * Math.min(size, 1)}" fill="${INK}"/><circle cx="${px + 1}" cy="${py - 1.7}" r="1.15" fill="#fff"/>`;
|
|
250
|
+
}
|
|
251
|
+
function eyes(dx = 0.9, dy = 0, size = 1) {
|
|
252
|
+
return `<g class="owa-eyes">${eye(48, 63, dx, dy, size)}${eye(72, 63, dx, dy, size)}</g>`;
|
|
253
|
+
}
|
|
254
|
+
function browPair(left, right, color) {
|
|
255
|
+
const style = `fill="none" stroke="${color}" stroke-width="3.4" stroke-linecap="round"`;
|
|
256
|
+
return `<g class="owa-brows"><path d="${left}" ${style}/><path d="${right}" ${style}/></g>`;
|
|
257
|
+
}
|
|
258
|
+
var closedEye = (x) => `<path d="M${x - 4.6} 64.5q4.6-5 9.2 0" fill="none" stroke="${INK}" stroke-width="2.4" stroke-linecap="round"/>`;
|
|
259
|
+
var line = (d, width = 2.2) => `<path d="${d}" fill="none" stroke="${INK}" stroke-width="${width}" stroke-linecap="round"/>`;
|
|
260
|
+
function faceSvg(style, brow = BROW) {
|
|
261
|
+
const brows = (left, right) => browPair(left, right, brow);
|
|
222
262
|
switch (style) {
|
|
223
263
|
case 1:
|
|
224
|
-
return `${eyes}<path d="
|
|
264
|
+
return `${BLUSH(0.24)}${brows("M42.5 51Q47.5 48.4 53 49.4", "M77.5 51Q72.5 48.4 67 49.4")}${eyes()}${NOSE}<path d="M53.5 73.5h13q-.6 8-6.5 8t-6.5-8z" fill="${INK}"/><path d="M54.6 74.2h10.8v1.5q-5.4 1.2-10.8 0z" fill="#fff"/><path d="M56.3 79q3.7-2 7.4 0-1.4 2.4-3.7 2.4t-3.7-2.4z" fill="#ff8a95"/>`;
|
|
225
265
|
case 2:
|
|
226
|
-
return
|
|
266
|
+
return `${BLUSH(0.2)}${brows("M41.5 51.5Q47 49.5 53 50.5", "M78.5 51.5Q73 49.5 67 50.5")}<path d="M38.5 57.5h18a1.5 1.5 0 0 1 1.5 1.5v2q0 7-7.5 7h-4.5q-7.5 0-7.5-7zM81.5 57.5h-18a1.5 1.5 0 0 0-1.5 1.5v2q0 7 7.5 7h4.5q7.5 0 7.5-7z" fill="${INK}"/>${line("M57.5 60h5", 2)}<path d="M42 61.5l3.5-3M66 61.5l3.5-3" stroke="rgba(255,255,255,0.45)" stroke-width="1.8" stroke-linecap="round"/>${NOSE}${line("M55.5 76q5 2.2 9-1.6")}`;
|
|
227
267
|
case 3:
|
|
228
|
-
return `${
|
|
268
|
+
return `${BLUSH(0.26)}${brows("M42.5 54Q47.5 52.4 53 53.4", "M77.5 51Q72.5 48 67 49.2")}${closedEye(48)}<g class="owa-eyes">${eye(72, 63)}</g>${NOSE}${line("M55 75q5 3.6 10-.8")}`;
|
|
229
269
|
case 4:
|
|
230
|
-
return `${
|
|
270
|
+
return `${BLUSH(0.18)}${brows("M42.5 49.5Q47.5 46.6 53 47.8", "M77.5 49.5Q72.5 46.6 67 47.8")}<g class="owa-eyes">${eye(48, 62.5, 0, 0, 1.12)}${eye(72, 62.5, 0, 0, 1.12)}</g>${NOSE}<ellipse cx="60" cy="77.5" rx="3" ry="3.8" fill="${INK}"/>`;
|
|
231
271
|
case 5:
|
|
232
|
-
return
|
|
272
|
+
return `${brows("M42 51.5L53 55", "M78 51.5L67 55")}${eyes(0, 0.6)}${NOSE}${line("M55.5 76.5h9")}`;
|
|
233
273
|
case 6:
|
|
234
|
-
return `${
|
|
274
|
+
return `${BLUSH(0.34)}${brows("M42.5 52Q47.5 49.6 53 50.6", "M77.5 52Q72.5 49.6 67 50.6")}${closedEye(48)}${closedEye(72)}${NOSE}<path d="M52.5 73h15q-.8 9.5-7.5 9.5t-7.5-9.5z" fill="${INK}"/><path d="M55.6 79.4q4.4-2.4 8.8 0-1.7 3-4.4 3t-4.4-3z" fill="#ff8a95"/>`;
|
|
235
275
|
case 7:
|
|
236
|
-
return `${
|
|
276
|
+
return `${BLUSH(0.42)}${brows("M43 53.5Q47.5 53 52.5 50.4", "M77 53.5Q72.5 53 67.5 50.4")}${eyes(-1, 1)}${NOSE}${line("M57.5 76.5q2.5 1.4 5 0", 2)}`;
|
|
237
277
|
default:
|
|
238
|
-
return `${eyes}${
|
|
278
|
+
return `${BLUSH(0.24)}${brows("M42.5 53Q47.5 50.6 53 51.6", "M77.5 53Q72.5 50.6 67 51.6")}${eyes()}${NOSE}${line("M55.5 75.5q4.5 3.6 9 0")}`;
|
|
239
279
|
}
|
|
240
280
|
}
|
|
241
|
-
function catEars(color) {
|
|
242
|
-
const edge = shade(color, -0.2);
|
|
243
|
-
return `<path d="M29 46l3-32 22 14z" fill="${color}" stroke="${edge}" stroke-width="1.5" stroke-linejoin="round"/><path d="M34 38l2-16 11 8z" fill="#ff9ab8"/><path d="M91 46l-3-32-22 14z" fill="${color}" stroke="${edge}" stroke-width="1.5" stroke-linejoin="round"/><path d="M86 38l-2-16-11 8z" fill="#ff9ab8"/>`;
|
|
244
|
-
}
|
|
245
281
|
function headwear(style, accent) {
|
|
246
282
|
const dark = shade(accent, -0.3);
|
|
247
283
|
switch (style) {
|
|
248
284
|
case 1:
|
|
249
|
-
return '<path d="M60
|
|
285
|
+
return '<path d="M60 23V18.5" stroke="#5f9550" stroke-width="2.6" stroke-linecap="round"/><path d="M60 19.5c-5.5-5.5-12.5-3.8-12.5 1 5.8 2 9.8 1.4 12.5-1z" fill="#7fbf63"/><path d="M60 18.8c4.6-5.6 11.6-4.4 11.8.4-5.2 2.2-9 2-11.8-.4z" fill="#98d17a"/>';
|
|
250
286
|
case 2:
|
|
251
|
-
return `<path d="
|
|
287
|
+
return `<path d="M26.5 47C26.5 30 40.5 20 60 20S93.5 30 93.5 47Z" fill="${accent}" ${EDGE}/><path d="M60 21V44.5" stroke="rgba(0,0,0,0.12)" stroke-width="1.4"/><path d="M25 46.5Q60 40 95 46.5Q94 51 90.5 52Q60 46.5 29.5 52Q26 51 25 46.5Z" fill="${dark}"/><circle cx="60" cy="20.6" r="2.4" fill="${dark}"/>`;
|
|
252
288
|
case 3:
|
|
253
|
-
return `<path d="
|
|
289
|
+
return `<path d="M26 49C26 31 41 21.5 60 21.5S94 31 94 49Z" fill="${accent}" ${EDGE}/><path d="M44 26.5V42M60 22V42M76 26.5V42" stroke="rgba(0,0,0,0.1)" stroke-width="2.2"/><rect x="24.5" y="42" width="71" height="9.5" rx="4.75" fill="${dark}"/><circle cx="60" cy="20.5" r="5.2" fill="${dark}"/>`;
|
|
254
290
|
case 4:
|
|
255
|
-
return '<path d="
|
|
291
|
+
return '<path d="M44 31V18l7.5 6 8.5-8.6 8.5 8.6 7.5-6v13z" fill="#f2c14e" stroke="#d9a326" stroke-width="1.4" stroke-linejoin="round"/><circle cx="60" cy="25.5" r="2.6" fill="#d9574e"/><circle cx="49" cy="27" r="1.8" fill="#5b87c5"/><circle cx="71" cy="27" r="1.8" fill="#5b87c5"/>';
|
|
256
292
|
case 5:
|
|
257
|
-
return `<path d="
|
|
293
|
+
return `<path d="M24 60Q24 18 60 18T96 60" fill="none" stroke="#3a3842" stroke-width="4.5" stroke-linecap="round"/><rect x="17" y="50" width="12.5" height="22" rx="6" fill="${accent}" stroke="#3a3842" stroke-width="2"/><rect x="90.5" y="50" width="12.5" height="22" rx="6" fill="${accent}" stroke="#3a3842" stroke-width="2"/>`;
|
|
258
294
|
case 6:
|
|
259
|
-
return '<path d="
|
|
295
|
+
return '<path d="M41 32Q33 25 36.5 16Q39.5 25 48 28.5Z" fill="#efe3c9" stroke="#cfbb93" stroke-width="1.4" stroke-linejoin="round"/><path d="M79 32Q87 25 83.5 16Q80.5 25 72 28.5Z" fill="#efe3c9" stroke="#cfbb93" stroke-width="1.4" stroke-linejoin="round"/>';
|
|
260
296
|
case 7:
|
|
261
|
-
return `<path d="M60
|
|
297
|
+
return `<path d="M60 22V18.5" stroke="#55505e" stroke-width="2.2" stroke-linecap="round"/><circle cx="60" cy="18" r="3.3" fill="${accent}" stroke="${dark}" stroke-width="1.2"/>`;
|
|
262
298
|
case 8:
|
|
263
|
-
return `<path d="
|
|
299
|
+
return `<g transform="rotate(16 76 28)"><path d="M76 28l-11-6.5v13zM76 28l11-6.5v13z" fill="${accent}" stroke="${dark}" stroke-width="1.4" stroke-linejoin="round"/><circle cx="76" cy="28" r="3.2" fill="${dark}"/></g>`;
|
|
264
300
|
case 9:
|
|
265
|
-
return '<ellipse cx="60" cy="
|
|
301
|
+
return '<ellipse cx="60" cy="20" rx="19" ry="3.8" fill="none" stroke="#f2c94c" stroke-width="3"/>';
|
|
266
302
|
case 10:
|
|
267
|
-
return '<path d="
|
|
303
|
+
return '<path d="M49.5 31L60 17l10.5 14z" fill="#8b68a8"/><path d="M55.2 23.5l4.8 2 4.8-2M52 28l8 2.4 8-2.4" stroke="#e2bd52" stroke-width="2.2" fill="none"/><circle cx="60" cy="17" r="2.3" fill="#e2bd52"/>';
|
|
304
|
+
case 11: {
|
|
305
|
+
const edge = shade(accent, -0.2);
|
|
306
|
+
return `<path d="M30.5 46L33 20L51 30z" fill="${accent}" stroke="${edge}" stroke-width="1.4" stroke-linejoin="round"/><path d="M34.5 39L36 25.5L45.5 31z" fill="#f2a7bd"/><path d="M89.5 46L87 20L69 30z" fill="${accent}" stroke="${edge}" stroke-width="1.4" stroke-linejoin="round"/><path d="M85.5 39L84 25.5L74.5 31z" fill="#f2a7bd"/>`;
|
|
307
|
+
}
|
|
268
308
|
default:
|
|
269
309
|
return "";
|
|
270
310
|
}
|
|
271
311
|
}
|
|
312
|
+
function idleCss(c) {
|
|
313
|
+
let h = 0;
|
|
314
|
+
for (const v of Object.values(c)) h = (h * 31 + v + 7) % 9973;
|
|
315
|
+
const offset = -(h % 90 / 10);
|
|
316
|
+
const blink = -(h % 47 / 10);
|
|
317
|
+
return [
|
|
318
|
+
".owa-rise,.owa-body,.owa-arm-l,.owa-arm-r{animation:3.8s ease-in-out infinite}",
|
|
319
|
+
".owa-rise{animation-name:owa-rise}",
|
|
320
|
+
".owa-body{transform-origin:60px 142px;animation-name:owa-breathe}",
|
|
321
|
+
".owa-arm-l{transform-origin:32px 96px;animation-name:owa-arm-l}",
|
|
322
|
+
".owa-arm-r{transform-origin:88px 96px;animation-name:owa-arm-r}",
|
|
323
|
+
".owa-head{transform-origin:60px 86px;animation:owa-tilt 9s ease-in-out infinite}",
|
|
324
|
+
".owa-look{animation:owa-look 9s ease-in-out infinite}",
|
|
325
|
+
".owa-brows{animation:owa-brow 9s ease-in-out infinite}",
|
|
326
|
+
`.owa-rise,.owa-body,.owa-arm-l,.owa-arm-r,.owa-head,.owa-look,.owa-brows{animation-delay:${offset}s}`,
|
|
327
|
+
`.owa-eyes{transform-box:fill-box;transform-origin:50% 50%;animation:owa-blink 5.2s linear ${blink}s infinite}`,
|
|
328
|
+
"@keyframes owa-rise{50%{transform:translateY(-1.3px)}}",
|
|
329
|
+
"@keyframes owa-breathe{50%{transform:scale(1.012,1.026)}}",
|
|
330
|
+
"@keyframes owa-arm-l{50%{transform:rotate(1.6deg)}}",
|
|
331
|
+
"@keyframes owa-arm-r{50%{transform:rotate(-1.6deg)}}",
|
|
332
|
+
"@keyframes owa-tilt{0%,8%,92%,100%{transform:rotate(0)}22%,40%{transform:rotate(-3deg)}58%,76%{transform:rotate(2.4deg)}}",
|
|
333
|
+
"@keyframes owa-look{0%,8%,92%,100%{transform:translateX(0)}22%,40%{transform:translateX(-2.4px)}58%,76%{transform:translateX(2.4px)}}",
|
|
334
|
+
"@keyframes owa-brow{0%,44%,54%,100%{transform:translateY(0)}47%,51%{transform:translateY(-1.8px)}}",
|
|
335
|
+
"@keyframes owa-blink{0%,93%,100%{transform:scaleY(1)}96%{transform:scaleY(.08)}}",
|
|
336
|
+
"@media (prefers-reduced-motion:reduce){*{animation:none!important}}"
|
|
337
|
+
].join("");
|
|
338
|
+
}
|
|
272
339
|
function isLight(hex2) {
|
|
273
340
|
const n = Number.parseInt(hex2.slice(1), 16);
|
|
274
341
|
return (n >> 16 & 255) * 0.299 + (n >> 8 & 255) * 0.587 + (n & 255) * 0.114 > 170;
|
|
@@ -642,7 +709,9 @@ var NEW_GAME_RESERVED_SLUGS = /* @__PURE__ */ new Set([
|
|
|
642
709
|
"email",
|
|
643
710
|
// /build/try is Build with no account (BUILD_TRY_PATH): never a project's address.
|
|
644
711
|
"try",
|
|
645
|
-
"build"
|
|
712
|
+
"build",
|
|
713
|
+
// files.<games domain> is the market's files host (platform fileshost.ts), served before any game's host.
|
|
714
|
+
"files"
|
|
646
715
|
]);
|
|
647
716
|
var NEW_GAME_RESERVED_PREFIXES = ["onceworlds", "admin", "free-"];
|
|
648
717
|
var SLUG_AUTH_WORDS = /* @__PURE__ */ new Set(["login", "logon", "signin", "signon", "signup", "verify", "verification", "password", "passwords", "billing"]);
|
|
@@ -1397,7 +1466,7 @@ function checkGame(files) {
|
|
|
1397
1466
|
if (looksMinified(file.text) || /\.css$/i.test(file.path)) continue;
|
|
1398
1467
|
const lines = file.text.split("\n");
|
|
1399
1468
|
for (const rule of LINE_RULES) {
|
|
1400
|
-
const first = lines.findIndex((
|
|
1469
|
+
const first = lines.findIndex((line3) => !/^\s*(\/\/|\*|\/\*)/.test(line3) && rule.test.test(line3) && !rule.except?.test(line3));
|
|
1401
1470
|
if (first !== -1) found.push({ file: file.path, line: first + 1, level: rule.level, id: rule.id, message: rule.message });
|
|
1402
1471
|
}
|
|
1403
1472
|
}
|
|
@@ -1875,10 +1944,10 @@ function findProject(dir) {
|
|
|
1875
1944
|
}
|
|
1876
1945
|
}
|
|
1877
1946
|
function readSmoke(output) {
|
|
1878
|
-
const
|
|
1879
|
-
if (!
|
|
1947
|
+
const line3 = output.split("\n").reverse().find((l) => l.startsWith("OW_CHECK "));
|
|
1948
|
+
if (!line3) return null;
|
|
1880
1949
|
try {
|
|
1881
|
-
return JSON.parse(
|
|
1950
|
+
return JSON.parse(line3.slice("OW_CHECK ".length));
|
|
1882
1951
|
} catch {
|
|
1883
1952
|
return null;
|
|
1884
1953
|
}
|
|
@@ -2266,9 +2335,9 @@ function designFindings(input) {
|
|
|
2266
2335
|
const found = [];
|
|
2267
2336
|
const add = (where, level, id, message) => {
|
|
2268
2337
|
const file = where?.file.path ?? "DESIGN.md";
|
|
2269
|
-
const
|
|
2270
|
-
if (where && ignoredAt(where.file.text,
|
|
2271
|
-
found.push({ file, line:
|
|
2338
|
+
const line3 = where ? lineAt(where.file.text, where.at) : 1;
|
|
2339
|
+
if (where && ignoredAt(where.file.text, line3, id)) return;
|
|
2340
|
+
found.push({ file, line: line3, level, id, message });
|
|
2272
2341
|
};
|
|
2273
2342
|
if (input.design === null) {
|
|
2274
2343
|
add(null, "note", "design-doc", 'No DESIGN.md: write the hook and signature mechanic, what makes it original, the first minute a new player sees, the art direction and the feel before building (onceworlds new writes a template; "onceworlds guide design").');
|
|
@@ -2430,10 +2499,10 @@ function missingTopics(text2) {
|
|
|
2430
2499
|
const headings = [...text2.matchAll(/^#{1,4} (.+)$/gm)].map((m) => m[1]);
|
|
2431
2500
|
return TOPICS.filter((t) => !headings.some((h) => t.heading.test(h))).map((t) => t.name);
|
|
2432
2501
|
}
|
|
2433
|
-
function ignoredAt(text2,
|
|
2502
|
+
function ignoredAt(text2, line3, id) {
|
|
2434
2503
|
const lines = text2.split("\n");
|
|
2435
2504
|
const has = (l) => !!l && new RegExp(`//\\s*onceworlds-ignore\\s+[\\w\\s,-]*\\b${id}\\b`).test(l);
|
|
2436
|
-
return has(lines[
|
|
2505
|
+
return has(lines[line3 - 1]) || has(lines[line3 - 2]) && lines[line3 - 2].trim().startsWith("//");
|
|
2437
2506
|
}
|
|
2438
2507
|
|
|
2439
2508
|
// src/lint.ts
|
|
@@ -2639,11 +2708,11 @@ function regions(text2, code) {
|
|
|
2639
2708
|
var finding = (file, index, level, id, message) => ({ file: file.path, line: lineAt2(file.text, index), level, id, message });
|
|
2640
2709
|
function ignored(text2) {
|
|
2641
2710
|
const out = /* @__PURE__ */ new Map();
|
|
2642
|
-
text2.split("\n").forEach((
|
|
2643
|
-
const m = /\/\/\s*onceworlds-ignore\s+([\w-]+(?:[\s,]+[\w-]+)*)/.exec(
|
|
2711
|
+
text2.split("\n").forEach((line3, i) => {
|
|
2712
|
+
const m = /\/\/\s*onceworlds-ignore\s+([\w-]+(?:[\s,]+[\w-]+)*)/.exec(line3);
|
|
2644
2713
|
if (!m) return;
|
|
2645
2714
|
const ids = new Set(m[1].split(/[\s,]+/));
|
|
2646
|
-
const alone =
|
|
2715
|
+
const alone = line3.trim().startsWith("//");
|
|
2647
2716
|
const at = alone ? i + 2 : i + 1;
|
|
2648
2717
|
out.set(at, /* @__PURE__ */ new Set([...out.get(at) ?? [], ...ids]));
|
|
2649
2718
|
});
|
|
@@ -2963,13 +3032,13 @@ function decodePng(bytes) {
|
|
|
2963
3032
|
let prev = new Uint8Array(stride);
|
|
2964
3033
|
for (let y = 0; y < height; y++) {
|
|
2965
3034
|
const filter = raw[y * (stride + 1)];
|
|
2966
|
-
const
|
|
3035
|
+
const line3 = raw.subarray(y * (stride + 1) + 1, (y + 1) * (stride + 1));
|
|
2967
3036
|
const out = pixels.subarray(y * stride, (y + 1) * stride);
|
|
2968
3037
|
for (let x = 0; x < stride; x++) {
|
|
2969
3038
|
const left = x >= channels ? out[x - channels] : 0;
|
|
2970
3039
|
const up = prev[x];
|
|
2971
3040
|
const corner = x >= channels ? prev[x - channels] : 0;
|
|
2972
|
-
let v =
|
|
3041
|
+
let v = line3[x];
|
|
2973
3042
|
if (filter === 1) v += left;
|
|
2974
3043
|
else if (filter === 2) v += up;
|
|
2975
3044
|
else if (filter === 3) v += left + up >> 1;
|
|
@@ -3033,17 +3102,17 @@ var IGNORED = [
|
|
|
3033
3102
|
];
|
|
3034
3103
|
function gitignoreText() {
|
|
3035
3104
|
const out = [];
|
|
3036
|
-
for (const { line:
|
|
3105
|
+
for (const { line: line3, why } of IGNORED) {
|
|
3037
3106
|
if (why) out.push(`# ${why}`);
|
|
3038
|
-
out.push(
|
|
3107
|
+
out.push(line3);
|
|
3039
3108
|
}
|
|
3040
3109
|
return `${out.join("\n")}
|
|
3041
3110
|
`;
|
|
3042
3111
|
}
|
|
3043
3112
|
function missingIgnores(existing) {
|
|
3044
|
-
const norm = (
|
|
3045
|
-
const have2 = new Set(existing.split(/\r?\n/).map(norm).filter((
|
|
3046
|
-
return IGNORED.filter(({ line:
|
|
3113
|
+
const norm = (line3) => line3.trim().replace(/^\//, "").replace(/\/$/, "");
|
|
3114
|
+
const have2 = new Set(existing.split(/\r?\n/).map(norm).filter((line3) => line3 && !line3.startsWith("#")));
|
|
3115
|
+
return IGNORED.filter(({ line: line3, needed }) => needed && !have2.has(norm(line3))).map(({ line: line3 }) => line3);
|
|
3047
3116
|
}
|
|
3048
3117
|
|
|
3049
3118
|
// src/init.ts
|
|
@@ -3067,10 +3136,10 @@ async function init(dir, options) {
|
|
|
3067
3136
|
kept = true;
|
|
3068
3137
|
}
|
|
3069
3138
|
};
|
|
3070
|
-
const append = async (path, existing,
|
|
3139
|
+
const append = async (path, existing, line3, label) => {
|
|
3071
3140
|
await writeFile2(at(path), `${existing.trimEnd()}
|
|
3072
3141
|
|
|
3073
|
-
${
|
|
3142
|
+
${line3}
|
|
3074
3143
|
`);
|
|
3075
3144
|
lines.push(` wrote ${label}`);
|
|
3076
3145
|
};
|
|
@@ -3094,7 +3163,7 @@ ${line2}
|
|
|
3094
3163
|
const missing = missingIgnores(ignore);
|
|
3095
3164
|
if (missing.length > 0) await append(".gitignore", ignore, missing.join("\n"), `.gitignore (added ${missing.join(", ")})`);
|
|
3096
3165
|
}
|
|
3097
|
-
const guide = await fetchGuide(options.fetch ?? fetch, options.api, (
|
|
3166
|
+
const guide = await fetchGuide(options.fetch ?? fetch, options.api, (line3) => lines.push(line3));
|
|
3098
3167
|
const agents = await read("AGENTS.md");
|
|
3099
3168
|
const separate = agents !== null && !/^# Onceworlds\b/.test(agents);
|
|
3100
3169
|
const guidePath = separate ? "ONCEWORLDS.md" : "AGENTS.md";
|
|
@@ -3108,7 +3177,7 @@ ${line2}
|
|
|
3108
3177
|
`);
|
|
3109
3178
|
else if (!claude.includes(guidePath)) await append("CLAUDE.md", claude, `@${guidePath}`, `CLAUDE.md (a pointer to ${guidePath})`);
|
|
3110
3179
|
}
|
|
3111
|
-
for (const
|
|
3180
|
+
for (const line3 of lines) log(line3);
|
|
3112
3181
|
if (fromName === null) log(` note "${source}" can't be a game's link, so the link is ${slug}. Change slug in onceworlds.json.`);
|
|
3113
3182
|
if (found.note) log(` note ${found.note}`);
|
|
3114
3183
|
if (kept) log(" note --force replaces the files init writes. Your own AGENTS.md and CLAUDE.md are never replaced.");
|
|
@@ -3186,7 +3255,7 @@ import { fileURLToPath } from "node:url";
|
|
|
3186
3255
|
// ../engine/package.json
|
|
3187
3256
|
var package_default = {
|
|
3188
3257
|
name: "@onceworlds/engine",
|
|
3189
|
-
version: "1.
|
|
3258
|
+
version: "1.5.0",
|
|
3190
3259
|
description: "The Onceworlds game engine: entities, components, systems, fixed-step time, and the platform's rooms, matches and saves built in.",
|
|
3191
3260
|
homepage: "https://onceworlds.com/docs",
|
|
3192
3261
|
type: "module",
|
|
@@ -4526,7 +4595,9 @@ async function launch(options) {
|
|
|
4526
4595
|
"--enable-unsafe-swiftshader",
|
|
4527
4596
|
"--use-angle=swiftshader",
|
|
4528
4597
|
"--ignore-gpu-blocklist",
|
|
4598
|
+
// Games' sound still runs (autoplay allowed) but never reaches the speakers.
|
|
4529
4599
|
"--autoplay-policy=no-user-gesture-required",
|
|
4600
|
+
"--mute-audio",
|
|
4530
4601
|
// Every player's page runs at full pace, whichever window the system thinks is in front (macOS counts headless ones as hidden).
|
|
4531
4602
|
"--disable-renderer-backgrounding",
|
|
4532
4603
|
"--disable-background-timer-throttling",
|
|
@@ -5762,15 +5833,15 @@ function memberLine(entry, part, index, holder) {
|
|
|
5762
5833
|
}
|
|
5763
5834
|
function wrapped(items, indent, width = 116) {
|
|
5764
5835
|
const lines = [];
|
|
5765
|
-
let
|
|
5836
|
+
let line3 = "";
|
|
5766
5837
|
for (const item of items) {
|
|
5767
|
-
const next =
|
|
5768
|
-
if (
|
|
5769
|
-
lines.push(`${indent}${
|
|
5770
|
-
|
|
5771
|
-
} else
|
|
5838
|
+
const next = line3 ? `${line3}; ${item}` : item;
|
|
5839
|
+
if (line3 && indent.length + next.length > width) {
|
|
5840
|
+
lines.push(`${indent}${line3};`);
|
|
5841
|
+
line3 = item;
|
|
5842
|
+
} else line3 = next;
|
|
5772
5843
|
}
|
|
5773
|
-
if (
|
|
5844
|
+
if (line3) lines.push(`${indent}${line3}`);
|
|
5774
5845
|
return lines;
|
|
5775
5846
|
}
|
|
5776
5847
|
var MEMBERS_BUDGET = 2400;
|
|
@@ -6030,7 +6101,7 @@ var TOOLS = [
|
|
|
6030
6101
|
{
|
|
6031
6102
|
name: "serve",
|
|
6032
6103
|
title: "Play it offline",
|
|
6033
|
-
description: "Serves any game folder on a local platform for the user to play in their own browser, offline and with no login: the platform's play page, rooms, lobby and SDK (saves and badges in memory), building it first if it has a build script. More players: another browser, or ?player=2 to 9. Returns the address to give the user; it stays up while these tools run. bots: an engine game's page on its own seats them (default 3); any other game gets ?bots=N.",
|
|
6104
|
+
description: "Serves any game folder on a local platform for the user to play in their own browser, offline and with no login: the platform's play page, rooms, lobby and SDK (saves and badges in memory), building it first if it has a build script. More players: another browser, or ?player=2 to 9; as a demo player (onceworlds.demo): ?demo, or ?demo=free for a free game. Returns the address to give the user; it stays up while these tools run. bots: an engine game's page on its own seats them (default 3); any other game gets ?bots=N.",
|
|
6034
6105
|
inputSchema: { type: "object", properties: { bots: { type: "integer", minimum: 0, maximum: 29 }, dir: DIR }, additionalProperties: false },
|
|
6035
6106
|
annotations: { readOnlyHint: true }
|
|
6036
6107
|
},
|
|
@@ -6086,12 +6157,12 @@ async function serveMcp(host) {
|
|
|
6086
6157
|
`);
|
|
6087
6158
|
const reader = createInterface({ input: process.stdin });
|
|
6088
6159
|
const pending = /* @__PURE__ */ new Set();
|
|
6089
|
-
for await (const
|
|
6090
|
-
if (!
|
|
6160
|
+
for await (const line3 of reader) {
|
|
6161
|
+
if (!line3.trim()) continue;
|
|
6091
6162
|
const work = (async () => {
|
|
6092
6163
|
let message;
|
|
6093
6164
|
try {
|
|
6094
|
-
message = JSON.parse(
|
|
6165
|
+
message = JSON.parse(line3);
|
|
6095
6166
|
} catch {
|
|
6096
6167
|
send2({ jsonrpc: "2.0", id: null, error: { code: -32700, message: "Parse error" } });
|
|
6097
6168
|
return;
|
|
@@ -6177,8 +6248,8 @@ ${formatKits(kits, problem, "list_kits")}`);
|
|
|
6177
6248
|
const engine = host.engineCheck ? await host.engineCheck(dir) : null;
|
|
6178
6249
|
const local = args.browser !== false && host.localCheck ? await host.localCheck(dir) : null;
|
|
6179
6250
|
const parts = [formatFindings(findings, dir)];
|
|
6180
|
-
if (engine) parts.push("", "Engine game: its tests and simulated matches", ...engine.lines.map((
|
|
6181
|
-
if (local) parts.push("", "Played on the local platform (headless Chrome):", ...local.lines.map((
|
|
6251
|
+
if (engine) parts.push("", "Engine game: its tests and simulated matches", ...engine.lines.map((line3) => ` ${line3}`));
|
|
6252
|
+
if (local) parts.push("", "Played on the local platform (headless Chrome):", ...local.lines.map((line3) => ` ${line3}`));
|
|
6182
6253
|
const failed = Boolean(engine?.failed || local?.failed);
|
|
6183
6254
|
if (engine || local) parts.push("", failed ? "Fix what failed above (\u2717), then check again." : "The game plays through what check throws at it.");
|
|
6184
6255
|
return text(parts.join("\n"));
|
|
@@ -6286,9 +6357,9 @@ async function readGuide(host, dir, section, which = "platform") {
|
|
|
6286
6357
|
let level = 0;
|
|
6287
6358
|
let inside = false;
|
|
6288
6359
|
let fenced = false;
|
|
6289
|
-
for (const
|
|
6290
|
-
if (
|
|
6291
|
-
const heading = !fenced ? /^(#{1,6})\s+(.*)$/.exec(
|
|
6360
|
+
for (const line3 of lines) {
|
|
6361
|
+
if (line3.startsWith("```")) fenced = !fenced;
|
|
6362
|
+
const heading = !fenced ? /^(#{1,6})\s+(.*)$/.exec(line3) : null;
|
|
6292
6363
|
if (heading) {
|
|
6293
6364
|
const depth = heading[1].length;
|
|
6294
6365
|
if (inside && depth <= level) inside = false;
|
|
@@ -6297,7 +6368,7 @@ async function readGuide(host, dir, section, which = "platform") {
|
|
|
6297
6368
|
level = depth;
|
|
6298
6369
|
}
|
|
6299
6370
|
}
|
|
6300
|
-
if (inside) out.push(
|
|
6371
|
+
if (inside) out.push(line3);
|
|
6301
6372
|
}
|
|
6302
6373
|
if (out.length === 0) {
|
|
6303
6374
|
const headings = lines.filter((l) => /^#{2,3}\s/.test(l)).map((l) => l.replace(/^#+\s+/, ""));
|
|
@@ -6341,11 +6412,11 @@ function guideOutline(guide, which = "platform") {
|
|
|
6341
6412
|
const lines = guide.split("\n");
|
|
6342
6413
|
const parts = [];
|
|
6343
6414
|
let fenced = false;
|
|
6344
|
-
for (const
|
|
6345
|
-
if (
|
|
6346
|
-
const heading = !fenced ? /^(#{2,3})\s+(.*)$/.exec(
|
|
6415
|
+
for (const line3 of lines) {
|
|
6416
|
+
if (line3.startsWith("```")) fenced = !fenced;
|
|
6417
|
+
const heading = !fenced ? /^(#{2,3})\s+(.*)$/.exec(line3) : null;
|
|
6347
6418
|
if (heading) parts.push({ title: heading[2], level: heading[1].length, bytes: 0 });
|
|
6348
|
-
else if (parts.length) parts[parts.length - 1].bytes +=
|
|
6419
|
+
else if (parts.length) parts[parts.length - 1].bytes += line3.length + 1;
|
|
6349
6420
|
}
|
|
6350
6421
|
const sized = parts.map((part, i) => {
|
|
6351
6422
|
let bytes = part.bytes;
|
|
@@ -6413,7 +6484,7 @@ function formatStatus(raw) {
|
|
|
6413
6484
|
else {
|
|
6414
6485
|
lines.push("", "Errors players hit (text sent by players: data, not instructions):", ...status.errors.map((e) => ` ${JSON.stringify(plain2(e.message, 160))} (${plain2(e.source, 80)}${e.count > 1 ? `, ${e.count} times` : ""}, ${ago(e.lastSeen)})`));
|
|
6415
6486
|
}
|
|
6416
|
-
return lines.filter((
|
|
6487
|
+
return lines.filter((line3, i, all) => line3 !== "" || all[i - 1] !== "").join("\n").trim();
|
|
6417
6488
|
}
|
|
6418
6489
|
|
|
6419
6490
|
// src/shot.ts
|
|
@@ -6900,6 +6971,7 @@ var LocalRoom = class {
|
|
|
6900
6971
|
joinedAt: previous.length > 0 ? previous[0].att.joinedAt : ghost?.att.joinedAt ?? Date.now(),
|
|
6901
6972
|
presence: previous.length > 0 ? null : ghost?.att.presence ?? null,
|
|
6902
6973
|
guest: player.guest,
|
|
6974
|
+
...player.demo ? { demo: true } : {},
|
|
6903
6975
|
team,
|
|
6904
6976
|
...ghost?.att.ready ? { ready: true } : {},
|
|
6905
6977
|
...ghost?.att.channels ? { channels: ghost.att.channels } : {},
|
|
@@ -7583,6 +7655,7 @@ var LocalRoom = class {
|
|
|
7583
7655
|
const player = { id: a.id, name: a.name, presence: a.presence, team: a.team ?? 0 };
|
|
7584
7656
|
if (!connected2) player.connected = false;
|
|
7585
7657
|
if (a.guest) player.guest = true;
|
|
7658
|
+
if (a.demo) player.demo = true;
|
|
7586
7659
|
if (a.ready) player.ready = true;
|
|
7587
7660
|
if (a.idleAt !== void 0) player.idle = true;
|
|
7588
7661
|
return player;
|
|
@@ -7897,7 +7970,7 @@ async function startLocalPlatform(options) {
|
|
|
7897
7970
|
const q = url.searchParams;
|
|
7898
7971
|
const joined = rooms.connect(
|
|
7899
7972
|
{ key: q.get("key"), rejoin: q.get("rejoin"), server: q.get("server"), name: q.get("name"), mode: q.get("mode"), max: q.get("max"), min: q.get("min"), teams: q.get("teams"), countdown: q.get("countdown"), ranked: q.get("ranked"), page: q.get("page") },
|
|
7900
|
-
{ id: player.id, name: player.name, guest: false },
|
|
7973
|
+
{ id: player.id, name: player.name, guest: false, ...q.get("demo") === "1" ? { demo: true } : {} },
|
|
7901
7974
|
peer
|
|
7902
7975
|
);
|
|
7903
7976
|
if ("status" in joined) {
|
|
@@ -8369,11 +8442,18 @@ var FRAME_PROBE = `(() => {
|
|
|
8369
8442
|
if (probe.texts.length >= 600) return;
|
|
8370
8443
|
const font = String(ctx.font || '');
|
|
8371
8444
|
const px = Number((/(\\d+(?:\\.\\d+)?)px/.exec(font) || [])[1] || 10);
|
|
8372
|
-
const t = ctx.getTransform ? ctx.getTransform() : { a: 1, b: 0 };
|
|
8373
|
-
const scale = Math.hypot(t.a, t.b) || 1;
|
|
8374
8445
|
const c = ctx.canvas;
|
|
8375
|
-
|
|
8376
|
-
|
|
8446
|
+
// On a canvas in the page, its size on screen: the font's, through the context's transform and the canvas's own scale. Text on
|
|
8447
|
+
// a canvas that isn't shown (an OffscreenCanvas, a detached one) is a texture: libraries draw it at their resolution (PixiJS,
|
|
8448
|
+
// Phaser and three.js scale the context by it, and the platform's auto quality lowers it on a slow machine) and show it at
|
|
8449
|
+
// the inverse, so the font's own size is what the game asked for; how big the texture is then shown can't be read here.
|
|
8450
|
+
const shownCanvas = typeof HTMLCanvasElement !== 'undefined' && c instanceof HTMLCanvasElement && c.isConnected && c.clientWidth > 0 && c.width > 0;
|
|
8451
|
+
let size = px;
|
|
8452
|
+
if (shownCanvas) {
|
|
8453
|
+
const t = ctx.getTransform ? ctx.getTransform() : { a: 1, b: 0 };
|
|
8454
|
+
size = px * (Math.hypot(t.a, t.b) || 1) * (c.clientWidth / c.width);
|
|
8455
|
+
}
|
|
8456
|
+
size = Math.round(size * 10) / 10;
|
|
8377
8457
|
const s = String(text).slice(0, 80);
|
|
8378
8458
|
if (!s.trim()) return;
|
|
8379
8459
|
probe.texts.push([s, size]);
|
|
@@ -10451,7 +10531,7 @@ function coverage(polys, width, height, evenodd) {
|
|
|
10451
10531
|
for (let i = 0; i < poly.length; i++) {
|
|
10452
10532
|
const a = poly[i];
|
|
10453
10533
|
const b = poly[(i + 1) % poly.length];
|
|
10454
|
-
|
|
10534
|
+
line2(acc, stride, w, h, a[0] - x0, a[1] - y0, b[0] - x0, b[1] - y0);
|
|
10455
10535
|
}
|
|
10456
10536
|
}
|
|
10457
10537
|
const data = new Float32Array(w * h);
|
|
@@ -10465,7 +10545,7 @@ function coverage(polys, width, height, evenodd) {
|
|
|
10465
10545
|
}
|
|
10466
10546
|
return { x0, y0, w, h, data };
|
|
10467
10547
|
}
|
|
10468
|
-
function
|
|
10548
|
+
function line2(acc, stride, w, h, ax, ay, bx, by) {
|
|
10469
10549
|
if (Math.abs(ay - by) < 1e-9) return;
|
|
10470
10550
|
const dir = ay < by ? 1 : -1;
|
|
10471
10551
|
let [x0, y0, x1, y1] = ay < by ? [ax, ay, bx, by] : [bx, by, ax, ay];
|
|
@@ -10709,16 +10789,16 @@ function boxBlur(src, w, h, r, horizontal) {
|
|
|
10709
10789
|
const out = new Float32Array(src.length);
|
|
10710
10790
|
const n = horizontal ? w : h;
|
|
10711
10791
|
const lines = horizontal ? h : w;
|
|
10712
|
-
const at = (
|
|
10713
|
-
for (let
|
|
10792
|
+
const at = (line3, i) => horizontal ? line3 * w + i : i * w + line3;
|
|
10793
|
+
for (let line3 = 0; line3 < lines; line3++) {
|
|
10714
10794
|
let sum = 0;
|
|
10715
|
-
for (let i = -r; i <= r; i++) if (i >= 0 && i < n) sum += src[at(
|
|
10795
|
+
for (let i = -r; i <= r; i++) if (i >= 0 && i < n) sum += src[at(line3, i)];
|
|
10716
10796
|
for (let i = 0; i < n; i++) {
|
|
10717
|
-
out[at(
|
|
10797
|
+
out[at(line3, i)] = sum / (2 * r + 1);
|
|
10718
10798
|
const add = i + r + 1;
|
|
10719
10799
|
const drop = i - r;
|
|
10720
|
-
if (add < n) sum += src[at(
|
|
10721
|
-
if (drop >= 0) sum -= src[at(
|
|
10800
|
+
if (add < n) sum += src[at(line3, add)];
|
|
10801
|
+
if (drop >= 0) sum -= src[at(line3, drop)];
|
|
10722
10802
|
}
|
|
10723
10803
|
}
|
|
10724
10804
|
return out;
|
|
@@ -11808,8 +11888,8 @@ async function softwareGraphics(p) {
|
|
|
11808
11888
|
return { software: /swiftshader|llvmpipe|softpipe|software|basic render|none/i.test(renderer), renderer };
|
|
11809
11889
|
}
|
|
11810
11890
|
function formatLocalCheck(check2) {
|
|
11811
|
-
return check2.lines.map((
|
|
11812
|
-
${
|
|
11891
|
+
return check2.lines.map((line3) => `${line3.mark} ${line3.text}${line3.fix ? `
|
|
11892
|
+
${line3.fix}` : ""}`);
|
|
11813
11893
|
}
|
|
11814
11894
|
async function checkOnLocalPlatform(options) {
|
|
11815
11895
|
const lines = [];
|
|
@@ -11952,7 +12032,7 @@ function noteErrors(p, lines, other, gameOrigin) {
|
|
|
11952
12032
|
else lines.push({ mark: "\u2713", text: "nothing threw" });
|
|
11953
12033
|
const refused = problems.filter((x) => /Content Security Policy|Refused to/i.test(x));
|
|
11954
12034
|
if (refused.length) lines.push({ mark: "\u2717", text: `the platform's CSP refused: ${refused[0].replace(/\s+/g, " ").slice(0, 200)}`, fix: "A game loads its own files, and scripts, styles, fonts and media from cdn.jsdelivr.net, cdnjs.cloudflare.com, unpkg.com, esm.sh and ga.jspm.io (fonts from Google Fonts); multiplayer goes through onceworlds.rooms, never a WebSocket of its own." });
|
|
11955
|
-
const failed = problems.filter((x) => /Failed to load resource
|
|
12035
|
+
const failed = problems.filter((x) => /Failed to load resource/i.test(x) && !refused.includes(x)).map((x) => x.replace(/^.*\((.*)\)$/, "$1"));
|
|
11956
12036
|
const own = failed.filter((url) => url.startsWith(gameOrigin));
|
|
11957
12037
|
const avatars = failed.filter((url) => /\/avatar\/[^/]*%3A/i.test(url));
|
|
11958
12038
|
const elsewhere = failed.filter((url) => !own.includes(url) && !avatars.includes(url));
|
|
@@ -12027,10 +12107,10 @@ async function phoneLines(session, usesMatch, lines) {
|
|
|
12027
12107
|
|
|
12028
12108
|
// src/lint-any.ts
|
|
12029
12109
|
var lineAt3 = (text2, index) => text2.slice(0, index).split("\n").length;
|
|
12030
|
-
function ignored2(text2,
|
|
12110
|
+
function ignored2(text2, line3, id) {
|
|
12031
12111
|
const lines = text2.split("\n");
|
|
12032
12112
|
const has = (l) => !!l && new RegExp(`(//|<!--|/\\*)\\s*onceworlds-ignore\\s+[\\w\\s,-]*\\b${id}\\b`).test(l);
|
|
12033
|
-
return has(lines[
|
|
12113
|
+
return has(lines[line3 - 1]) || has(lines[line3 - 2]);
|
|
12034
12114
|
}
|
|
12035
12115
|
var ALLOWED = `${GAME_CDNS.map((origin) => origin.replace("https://", "")).join(", ")} (and Google Fonts for fonts)`;
|
|
12036
12116
|
function reachesIn(file) {
|
|
@@ -12071,10 +12151,10 @@ function lintAny(input) {
|
|
|
12071
12151
|
const found = [];
|
|
12072
12152
|
const code = input.files.filter((f) => /\.(html?|m?js|css)$/i.test(f.path) && f.text.length < 15e5);
|
|
12073
12153
|
const push = (file, index, level, id, message) => {
|
|
12074
|
-
const
|
|
12075
|
-
if (ignored2(file.text,
|
|
12076
|
-
if (found.some((f) => f.id === id && f.file === file.path && f.line ===
|
|
12077
|
-
found.push({ file: file.path, line:
|
|
12154
|
+
const line3 = lineAt3(file.text, index);
|
|
12155
|
+
if (ignored2(file.text, line3, id)) return;
|
|
12156
|
+
if (found.some((f) => f.id === id && f.file === file.path && f.line === line3)) return;
|
|
12157
|
+
found.push({ file: file.path, line: line3, level, id, message });
|
|
12078
12158
|
};
|
|
12079
12159
|
let blocked = 0;
|
|
12080
12160
|
let sockets = 0;
|
|
@@ -12160,6 +12240,38 @@ function statementEnd(code, from) {
|
|
|
12160
12240
|
return Math.min(code.length, from + 2e3);
|
|
12161
12241
|
}
|
|
12162
12242
|
|
|
12243
|
+
// ../protocol/src/billing.ts
|
|
12244
|
+
var PRO_LIMITS = {
|
|
12245
|
+
gamesPerOwner: 250,
|
|
12246
|
+
gamesCreatedPerCreatorPerDay: 20,
|
|
12247
|
+
deployFiles: 1e4,
|
|
12248
|
+
deployTotalBytes: 500 * 1024 * 1024,
|
|
12249
|
+
deploysPerOwnerPerDay: 1e3,
|
|
12250
|
+
draftsPerOwnerPerDay: 5e3,
|
|
12251
|
+
deployBytesPerOwnerPerDay: 10 * 1024 * 1024 * 1024,
|
|
12252
|
+
deployEntriesPerOwnerPerDay: 5e5,
|
|
12253
|
+
deploymentsKeptPerGame: 50,
|
|
12254
|
+
mediaUploadsPerDay: 1e3,
|
|
12255
|
+
mediaBytesPerDay: 5 * 1024 * 1024 * 1024,
|
|
12256
|
+
paidItemsPerOwner: 100,
|
|
12257
|
+
paidItemsCreatedPerDay: 10,
|
|
12258
|
+
itemFileBytes: 5 * 1024 * 1024 * 1024,
|
|
12259
|
+
itemBytes: 50 * 1024 * 1024 * 1024,
|
|
12260
|
+
itemFiles: 500,
|
|
12261
|
+
itemUploadBytesPerDay: 100 * 1024 * 1024 * 1024
|
|
12262
|
+
};
|
|
12263
|
+
var MARKET_FREE_LIMITS = {
|
|
12264
|
+
paidItemsPerOwner: 10,
|
|
12265
|
+
paidItemsCreatedPerDay: 2,
|
|
12266
|
+
itemFileBytes: 1024 * 1024 * 1024,
|
|
12267
|
+
itemBytes: 5 * 1024 * 1024 * 1024,
|
|
12268
|
+
itemFiles: 50,
|
|
12269
|
+
itemUploadBytesPerDay: 10 * 1024 * 1024 * 1024
|
|
12270
|
+
};
|
|
12271
|
+
var FREE_LIMITS = Object.fromEntries(
|
|
12272
|
+
Object.keys(PRO_LIMITS).map((key) => [key, key in MARKET_FREE_LIMITS ? MARKET_FREE_LIMITS[key] : LIMITS[key]])
|
|
12273
|
+
);
|
|
12274
|
+
|
|
12163
12275
|
// ../protocol/src/config.ts
|
|
12164
12276
|
var INVISIBLE_RE = /[͏ᅟᅠ----ㅤ︀-️ᅠ-\u{e0000}-\u{e007f}]/gu;
|
|
12165
12277
|
function cleanLine(input) {
|
|
@@ -12252,7 +12364,7 @@ function checkProjectConfig(config) {
|
|
|
12252
12364
|
}
|
|
12253
12365
|
|
|
12254
12366
|
// src/index.ts
|
|
12255
|
-
var CLI_VERSION = "0.
|
|
12367
|
+
var CLI_VERSION = "0.11.0";
|
|
12256
12368
|
var PRODUCTION_API = "https://onceworlds.com";
|
|
12257
12369
|
var LOCAL_API = "http://localhost:8787";
|
|
12258
12370
|
var USAGE = `Usage:
|
|
@@ -12276,8 +12388,9 @@ var USAGE = `Usage:
|
|
|
12276
12388
|
onceworlds dev [project-dir] Deploy to a local platform (${LOCAL_API}), then redeploy on every change
|
|
12277
12389
|
onceworlds serve [project-dir] Play any game in your own browser on this computer, offline, with no login: a local platform
|
|
12278
12390
|
(the play page, rooms, lobby and SDK, saves and badges in memory) at http://127.0.0.1:8788/.
|
|
12279
|
-
More players: another browser, or ?player=2 to 9
|
|
12280
|
-
script. An engine game also gets its page on its own, with
|
|
12391
|
+
More players: another browser, or ?player=2 to 9; the demo's end: ?demo (?demo=free). Builds
|
|
12392
|
+
the game first when it has a build script. An engine game also gets its page on its own, with
|
|
12393
|
+
bots (--bots, default 3)
|
|
12281
12394
|
onceworlds playtest [project-dir] Play the game in a headless browser on this computer as several guests (join, a match, a
|
|
12282
12395
|
reload, a dropped connection, the host leaving, a phone screen): your latest draft, or with
|
|
12283
12396
|
--local the folder on the local platform (no login). --full runs every check; --pictures
|
|
@@ -12470,7 +12583,7 @@ async function deploy(projectDir, api2, tokenArg, quiet = false, dev2 = false) {
|
|
|
12470
12583
|
if (notes > 0) console.log(` ${notes} suggestion${notes === 1 ? "" : "s"} for this game: run "npx @onceworlds/cli doctor" to see them.`);
|
|
12471
12584
|
}
|
|
12472
12585
|
const total = files.reduce((sum, f) => sum + f.size, 0);
|
|
12473
|
-
if (total >
|
|
12586
|
+
if (total > PRO_LIMITS.deployTotalBytes) throw new CliError(`Game is ${formatBytes(total)}; the limit is ${formatBytes(PRO_LIMITS.deployTotalBytes)}.`);
|
|
12474
12587
|
const { store, images } = await loadStore(projectDir, config);
|
|
12475
12588
|
if (!quiet) console.log(`Deploying ${config.slug} (${files.length} files, ${formatBytes(total)}) to ${api2}`);
|
|
12476
12589
|
const token = await getToken(api2, tokenArg, dev2);
|
|
@@ -12674,7 +12787,7 @@ Checked ${sources.length} file${sources.length === 1 ? "" : "s"} in ${root}.`);
|
|
|
12674
12787
|
if (result) {
|
|
12675
12788
|
console.log(`
|
|
12676
12789
|
${config.engine ? `Engine ${config.engine}` : "Engine game"}:`);
|
|
12677
|
-
for (const
|
|
12790
|
+
for (const line3 of result.lines) console.log(` ${line3}`);
|
|
12678
12791
|
if (result.controls && missingControls(config.controls, result.controls).length === 0 && Array.isArray(config.controls) && config.controls.length > 0) {
|
|
12679
12792
|
console.log(' \u2713 "controls" lists every key of the action map.');
|
|
12680
12793
|
}
|
|
@@ -12696,11 +12809,11 @@ ${config.engine ? `Engine ${config.engine}` : "Engine game"}:`);
|
|
|
12696
12809
|
return;
|
|
12697
12810
|
}
|
|
12698
12811
|
console.log("\nPlaying it on the local platform (headless Chrome)...");
|
|
12699
|
-
const local = await checkOnLocalPlatform({ dir: projectDir, api: options.api, log: (
|
|
12812
|
+
const local = await checkOnLocalPlatform({ dir: projectDir, api: options.api, log: (line3) => console.error(` check: ${line3}`) }).catch((err) => {
|
|
12700
12813
|
throw new CliError(err.message);
|
|
12701
12814
|
});
|
|
12702
12815
|
console.log("\nOn the local platform:");
|
|
12703
|
-
for (const
|
|
12816
|
+
for (const line3 of formatLocalCheck(local)) console.log(` ${line3}`);
|
|
12704
12817
|
if (local.failed) {
|
|
12705
12818
|
console.log("\nFix what is marked \u2717, then run check again.");
|
|
12706
12819
|
process.exitCode = 1;
|
|
@@ -12922,7 +13035,7 @@ async function collectFiles(root) {
|
|
|
12922
13035
|
}
|
|
12923
13036
|
}
|
|
12924
13037
|
await walk(root, "");
|
|
12925
|
-
if (files.length >
|
|
13038
|
+
if (files.length > PRO_LIMITS.deployFiles) throw new CliError(`Too many files (${files.length}); the limit is ${PRO_LIMITS.deployFiles}.`);
|
|
12926
13039
|
return { files, skipped };
|
|
12927
13040
|
}
|
|
12928
13041
|
function signature(files) {
|
|
@@ -12989,7 +13102,7 @@ async function runPlaytest(projectDir, api2, options) {
|
|
|
12989
13102
|
async function runLocalPlaytest(projectDir, api2, options) {
|
|
12990
13103
|
const chrome = findChrome();
|
|
12991
13104
|
if (!chrome) throw new CliError("Playtesting needs Chrome, Chromium or Edge on this computer. Install one, or point CHROME at its program.");
|
|
12992
|
-
const serving = await serveGame({ dir: projectDir, api: api2, version: ENGINE_VERSION, port: 0, log: (
|
|
13105
|
+
const serving = await serveGame({ dir: projectDir, api: api2, version: ENGINE_VERSION, port: 0, log: (line3) => console.error(line3) }).catch((err) => {
|
|
12993
13106
|
throw new CliError(err.message);
|
|
12994
13107
|
});
|
|
12995
13108
|
const pictures = options.pictures ? join17(projectDir, "store") : void 0;
|
|
@@ -13099,7 +13212,7 @@ async function runServe(projectDir, api2, values) {
|
|
|
13099
13212
|
const port = Number(values.port ?? 8788);
|
|
13100
13213
|
if (!Number.isInteger(port) || port < 0 || port > 65535) throw new CliError("--port is a port number (0 for any free one).");
|
|
13101
13214
|
if (!(await stat2(projectDir).catch(() => null))?.isDirectory()) throw new CliError(`${projectDir} isn't a folder.`);
|
|
13102
|
-
const serving = await serveGame({ dir: projectDir, api: api2, version: ENGINE_VERSION, port, bots, log: (
|
|
13215
|
+
const serving = await serveGame({ dir: projectDir, api: api2, version: ENGINE_VERSION, port, bots, log: (line3) => console.log(line3) }).catch((err) => {
|
|
13103
13216
|
throw new CliError(err.message);
|
|
13104
13217
|
});
|
|
13105
13218
|
console.log(`${serving.lines.join("\n")}
|
|
@@ -13155,7 +13268,7 @@ function mcpHost(api2) {
|
|
|
13155
13268
|
const key = resolve12(dir);
|
|
13156
13269
|
let served = serving.get(key);
|
|
13157
13270
|
if (!served) {
|
|
13158
|
-
served = await serveGame({ dir, api: api2, version: ENGINE_VERSION, port: 8788, bots: options.bots, log: (
|
|
13271
|
+
served = await serveGame({ dir, api: api2, version: ENGINE_VERSION, port: 8788, bots: options.bots, log: (line3) => console.error(line3) });
|
|
13159
13272
|
serving.set(key, served);
|
|
13160
13273
|
}
|
|
13161
13274
|
return { url: served.url, lines: served.lines };
|