sam-coder-cli 1.0.23 → 1.0.25
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/bin/multiplayer-client.js +147 -65
- package/package.json +1 -1
|
@@ -272,49 +272,78 @@ class MultiplayerClient extends EventEmitter {
|
|
|
272
272
|
}
|
|
273
273
|
|
|
274
274
|
async connect() {
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
this.serverUrl = await ensureServerRunning(this.port);
|
|
278
|
-
|
|
279
|
-
this.ws = new WebSocket(this.serverUrl);
|
|
280
|
-
|
|
281
|
-
this.ws.on('open', () => {
|
|
282
|
-
this.connected = true;
|
|
283
|
-
this.emit('connected');
|
|
284
|
-
console.log(chalk.green('✅ Connected to multiplayer server'));
|
|
285
|
-
|
|
286
|
-
// Send client info immediately after connection
|
|
287
|
-
this.updateClientInfo({
|
|
288
|
-
name: this.name,
|
|
289
|
-
role: this.role,
|
|
290
|
-
model: this.model
|
|
291
|
-
});
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
this.ws.on('message', (data) => {
|
|
275
|
+
return new Promise((resolve, reject) => {
|
|
276
|
+
const connectWithRetry = async () => {
|
|
295
277
|
try {
|
|
296
|
-
|
|
297
|
-
this.
|
|
278
|
+
// If no server URL is provided, try to start a local server
|
|
279
|
+
if (this.serverUrl === 'ws://localhost:8080') {
|
|
280
|
+
try {
|
|
281
|
+
this.serverUrl = await ensureServerRunning(8080);
|
|
282
|
+
} catch (error) {
|
|
283
|
+
console.error(chalk.red('Failed to start local server:'), error.message);
|
|
284
|
+
reject(error);
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
console.log(chalk.blue(`Connecting to ${this.serverUrl}...`));
|
|
290
|
+
this.ws = new WebSocket(this.serverUrl);
|
|
291
|
+
|
|
292
|
+
const connectionTimeout = setTimeout(() => {
|
|
293
|
+
this.ws.terminate();
|
|
294
|
+
reject(new Error('Connection timeout'));
|
|
295
|
+
}, 10000); // 10 seconds timeout
|
|
296
|
+
|
|
297
|
+
this.ws.on('open', () => {
|
|
298
|
+
clearTimeout(connectionTimeout);
|
|
299
|
+
this.connected = true;
|
|
300
|
+
console.log(chalk.green('✅ Connected to multiplayer server'));
|
|
301
|
+
|
|
302
|
+
// Send client info
|
|
303
|
+
this.send({
|
|
304
|
+
type: 'client_info',
|
|
305
|
+
clientId: this.clientId,
|
|
306
|
+
name: this.name,
|
|
307
|
+
role: this.role,
|
|
308
|
+
model: this.model
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
this.emit('connected');
|
|
312
|
+
resolve();
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
this.ws.on('message', (data) => {
|
|
316
|
+
try {
|
|
317
|
+
const message = JSON.parse(data);
|
|
318
|
+
this.handleMessage(message);
|
|
319
|
+
} catch (error) {
|
|
320
|
+
console.error('Error parsing message:', error);
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
this.ws.on('close', () => {
|
|
325
|
+
this.connected = false;
|
|
326
|
+
this.emit('disconnected');
|
|
327
|
+
console.log(chalk.yellow('\n🔌 Disconnected from multiplayer server'));
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
this.ws.on('error', (error) => {
|
|
331
|
+
clearTimeout(connectionTimeout);
|
|
332
|
+
console.error('WebSocket error:', error.message);
|
|
333
|
+
if (!this.connected) {
|
|
334
|
+
reject(error);
|
|
335
|
+
}
|
|
336
|
+
this.emit('error', error);
|
|
337
|
+
});
|
|
338
|
+
|
|
298
339
|
} catch (error) {
|
|
299
|
-
console.error('
|
|
340
|
+
console.error('Connection error:', error.message);
|
|
341
|
+
reject(error);
|
|
300
342
|
}
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
this.ws.on('close', () => {
|
|
304
|
-
this.connected = false;
|
|
305
|
-
this.emit('disconnected');
|
|
306
|
-
console.log(chalk.yellow('\n🔌 Disconnected from multiplayer server'));
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
this.ws.on('error', (error) => {
|
|
310
|
-
console.error('WebSocket error:', error);
|
|
311
|
-
this.emit('error', error);
|
|
312
|
-
});
|
|
343
|
+
};
|
|
313
344
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
this.emit('error', error);
|
|
317
|
-
}
|
|
345
|
+
connectWithRetry();
|
|
346
|
+
});
|
|
318
347
|
}
|
|
319
348
|
|
|
320
349
|
handleMessage(message) {
|
|
@@ -675,38 +704,91 @@ class MultiplayerClient extends EventEmitter {
|
|
|
675
704
|
return client ? (client.name || client.clientInfo?.name || 'Unknown') : 'Unknown';
|
|
676
705
|
}
|
|
677
706
|
|
|
678
|
-
createSession() {
|
|
679
|
-
if (!this.
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
this.send({
|
|
685
|
-
type: 'create_session',
|
|
686
|
-
sessionId: this.sessionId,
|
|
687
|
-
clientInfo: {
|
|
688
|
-
name: this.name,
|
|
689
|
-
role: this.role,
|
|
690
|
-
model: this.model
|
|
707
|
+
async createSession() {
|
|
708
|
+
if (!this.connected) {
|
|
709
|
+
try {
|
|
710
|
+
await this.connect();
|
|
711
|
+
} catch (error) {
|
|
712
|
+
throw new Error(`Failed to connect to server: ${error.message}`);
|
|
691
713
|
}
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
return new Promise((resolve, reject) => {
|
|
717
|
+
this.sessionId = uuidv4();
|
|
718
|
+
this.isHost = true;
|
|
719
|
+
|
|
720
|
+
const onSessionCreated = (data) => {
|
|
721
|
+
if (data.sessionId === this.sessionId) {
|
|
722
|
+
this.off('session_created', onSessionCreated);
|
|
723
|
+
this.off('error', onError);
|
|
724
|
+
resolve(this.sessionId);
|
|
725
|
+
}
|
|
726
|
+
};
|
|
727
|
+
|
|
728
|
+
const onError = (error) => {
|
|
729
|
+
this.off('session_created', onSessionCreated);
|
|
730
|
+
this.off('error', onError);
|
|
731
|
+
reject(error);
|
|
732
|
+
};
|
|
733
|
+
|
|
734
|
+
this.once('session_created', onSessionCreated);
|
|
735
|
+
this.once('error', onError);
|
|
736
|
+
|
|
737
|
+
this.send({
|
|
738
|
+
type: 'create_session',
|
|
739
|
+
sessionId: this.sessionId,
|
|
740
|
+
clientInfo: {
|
|
741
|
+
name: this.name,
|
|
742
|
+
role: this.role,
|
|
743
|
+
model: this.model
|
|
744
|
+
}
|
|
745
|
+
});
|
|
692
746
|
});
|
|
693
|
-
return this.sessionId;
|
|
694
747
|
}
|
|
695
748
|
|
|
696
|
-
joinSession(sessionId) {
|
|
697
|
-
if (!
|
|
698
|
-
throw new Error('
|
|
749
|
+
async joinSession(sessionId) {
|
|
750
|
+
if (!sessionId) {
|
|
751
|
+
throw new Error('Session ID is required');
|
|
699
752
|
}
|
|
700
|
-
|
|
701
|
-
this.
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
name: this.name,
|
|
707
|
-
role: this.role,
|
|
708
|
-
model: this.model
|
|
753
|
+
|
|
754
|
+
if (!this.connected) {
|
|
755
|
+
try {
|
|
756
|
+
await this.connect();
|
|
757
|
+
} catch (error) {
|
|
758
|
+
throw new Error(`Failed to connect to server: ${error.message}`);
|
|
709
759
|
}
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
return new Promise((resolve, reject) => {
|
|
763
|
+
this.sessionId = sessionId;
|
|
764
|
+
this.isHost = false;
|
|
765
|
+
|
|
766
|
+
const onSessionJoined = (data) => {
|
|
767
|
+
if (data.sessionId === this.sessionId) {
|
|
768
|
+
this.off('session_joined', onSessionJoined);
|
|
769
|
+
this.off('error', onError);
|
|
770
|
+
resolve();
|
|
771
|
+
}
|
|
772
|
+
};
|
|
773
|
+
|
|
774
|
+
const onError = (error) => {
|
|
775
|
+
this.off('session_joined', onSessionJoined);
|
|
776
|
+
this.off('error', onError);
|
|
777
|
+
reject(error);
|
|
778
|
+
};
|
|
779
|
+
|
|
780
|
+
this.once('session_joined', onSessionJoined);
|
|
781
|
+
this.once('error', onError);
|
|
782
|
+
|
|
783
|
+
this.send({
|
|
784
|
+
type: 'join_session',
|
|
785
|
+
sessionId: this.sessionId,
|
|
786
|
+
clientInfo: {
|
|
787
|
+
name: this.name,
|
|
788
|
+
role: this.role,
|
|
789
|
+
model: this.model
|
|
790
|
+
}
|
|
791
|
+
});
|
|
710
792
|
});
|
|
711
793
|
}
|
|
712
794
|
}
|