@myvillage/cli 2.8.1 → 2.8.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myvillage/cli",
3
- "version": "2.8.1",
3
+ "version": "2.8.2",
4
4
  "description": "MyVillageOS CLI for community developers",
5
5
  "type": "module",
6
6
  "bin": {
@@ -141,7 +141,8 @@ function requestHostContext(timeoutMs) {
141
141
  // dev server.
142
142
 
143
143
  const DEFAULT_DEV_SERVER = 'https://myvillage-realtime-staging.myvillage-realtime.workers.dev';
144
- const CODE_ALPHABET = 'ABCDEFGHJKMNPQRSTUVWXYZ23456789';
144
+ // No vowels (or look-alikes), so random codes can't spell words.
145
+ const CODE_ALPHABET = 'BCDFGHJKMNPQRSTVWXZ23456789';
145
146
 
146
147
  export class MultiplayerError extends Error {
147
148
  constructor(code, message, status) {
@@ -185,7 +186,8 @@ class Multiplayer {
185
186
  this._ticket = null;
186
187
  /** The current match (see MatchView in the platform docs), or null. */
187
188
  this.match = null;
188
- this.me = { displayName: ctx ? ctx.player.displayName : options.name || 'Player ' + randomCode().slice(0, 3) };
189
+ // Digits only: random letters can spell words.
190
+ this.me = { displayName: ctx ? ctx.player.displayName : options.name || 'Player ' + (100 + Math.floor(Math.random() * 900)) };
189
191
  }
190
192
 
191
193
  // ── lobby plumbing ──
@@ -208,8 +210,14 @@ class Multiplayer {
208
210
  async quickMatch({ lobby } = {}) {
209
211
  this._requireAvailable();
210
212
  // Finished match rooms can't be reused, so dev quick match rotates rooms
211
- // every 10 minutes; tabs that press it within the same window meet.
212
- if (this.mode === 'dev') return this._devMatch('QUICK' + Math.floor(Date.now() / 600000).toString(36).toUpperCase(), false, lobby);
213
+ // every 10 minutes, and connect() steps through numbered rooms in that
214
+ // window past any that are finished or full. Tabs follow the same
215
+ // sequence, so they still meet.
216
+ if (this.mode === 'dev') {
217
+ this._devQuickBase = 'QUICK' + Math.floor(Date.now() / 600000).toString(36).toUpperCase();
218
+ this._devQuickTry = 0;
219
+ return this._devMatch(this._devQuickBase, false, lobby);
220
+ }
213
221
  return this._enter(await this._api('POST', `/api/v1/games/${this._game()}/matches/quick`, { platform: this._ctx.platform, lobby }));
214
222
  }
215
223
 
@@ -294,10 +302,23 @@ class Multiplayer {
294
302
  async connect() {
295
303
  this._requireAvailable();
296
304
  if (!this.match) throw new MultiplayerError('no_match', 'Host, join or quick match first.');
297
- const ticket = await this._freshTicket();
298
305
  const { ArenaClient } = await import('./myvillage-realtime.js');
299
- const net = new ArenaClient({ wsUrl: ticket.wsUrl, ticket: ticket.ticket });
300
- await net.connect();
306
+ let net;
307
+ for (;;) {
308
+ const ticket = await this._freshTicket();
309
+ net = new ArenaClient({ wsUrl: ticket.wsUrl, ticket: ticket.ticket });
310
+ try {
311
+ await net.connect();
312
+ break;
313
+ } catch (err) {
314
+ // Dev quick match: that room is finished or full — try the next one.
315
+ const devQuick = this.mode === 'dev' && this._devQuickBase && this.match.code.startsWith(this._devQuickBase);
316
+ if (!devQuick || this._devQuickTry >= 20 || !/finished|full/i.test(String(err && err.message))) throw err;
317
+ this._devQuickTry++;
318
+ this._ticket = null;
319
+ this._devMatch(this._devQuickBase + '-' + this._devQuickTry, false, this.match.lobby);
320
+ }
321
+ }
301
322
  this._ticket = null; // single use: a reconnect fetches a fresh one
302
323
  this._net = net;
303
324
  return net;