Skip to content

Commit c2260d1

Browse files
committed
debug for graceful shutdown
1 parent 02fa79e commit c2260d1

File tree

1 file changed

+72
-46
lines changed

1 file changed

+72
-46
lines changed

src/structures/Manager.ts

Lines changed: 72 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -197,48 +197,45 @@ export class Manager extends EventEmitter {
197197
* @returns {string} The path to the player's JSON file
198198
*/
199199
private async getPlayerFilePath(guildId: string): Promise<string> {
200-
// Get the directory path to where the player's JSON file will be saved
201200
const configDir = path.join(process.cwd(), "magmastream", "dist", "sessionData", "players");
202-
// Make sure the directory exists, create it if it doesn't
201+
203202
try {
204203
await fs.mkdir(configDir, { recursive: true });
204+
return path.join(configDir, `${guildId}.json`);
205205
} catch (err) {
206-
console.error("Error creating directory:", err);
207-
throw err; // Re-throw to let the caller handle it
206+
console.error("Error ensuring player data directory exists:", err);
207+
throw new Error(`Failed to resolve player file path for guild ${guildId}`);
208208
}
209-
210-
// Generate the full path to the player's JSON file
211-
return path.join(configDir, `${guildId}.json`);
212209
}
213210

214211
/**
215212
* Saves player states to the JSON file.
216213
* @param {string} guildId - The guild ID of the player to save
217214
*/
218215
public async savePlayerState(guildId: string): Promise<void> {
219-
console.log("Entering savePlayerState method for guild:", guildId); // Check if function is called
220-
221-
// Make sure getPlayerFilePath is awaited
222-
const playerStateFilePath = await this.getPlayerFilePath(guildId);
216+
console.log(`Attempting to save player state for guild: ${guildId}`);
223217

224-
console.log("Players map:", this.players);
225-
console.log("Guild ID:", guildId);
226-
const player = this.players.get(guildId);
218+
try {
219+
const playerStateFilePath = await this.getPlayerFilePath(guildId);
220+
console.log(`Resolved file path: ${playerStateFilePath}`);
227221

228-
// If the player does not exist or is disconnected, or the voice channel is not specified, do not save the player state
229-
if (!player || player.state === StateTypes.Disconnected || !player.voiceChannelId) {
230-
// Clean up any inactive players
231-
return this.cleanupInactivePlayers();
232-
}
222+
const player = this.players.get(guildId);
223+
if (!player || player.state === StateTypes.Disconnected || !player.voiceChannelId) {
224+
console.warn(`Skipping save for inactive player: ${guildId}`);
225+
return;
226+
}
233227

234-
// Serialize the player instance to avoid circular references
235-
const serializedPlayer = this.serializePlayer(player) as unknown as Player;
228+
console.log(`Serializing player state for: ${guildId}`);
229+
const serializedPlayer = this.serializePlayer(player);
236230

237-
// Write the serialized player state to the JSON file
238-
fs.writeFile(playerStateFilePath, JSON.stringify(serializedPlayer, null, 2), "utf-8");
231+
console.log(`Writing player state to file: ${playerStateFilePath}`);
232+
await fs.writeFile(playerStateFilePath, JSON.stringify(serializedPlayer, null, 2), "utf-8");
233+
console.log(`Successfully saved player state for: ${guildId}`);
239234

240-
// Emit a debug event to indicate the player state has been saved
241-
this.emit("debug", `[MANAGER] Saving player: ${guildId} at location: ${playerStateFilePath}`);
235+
this.emit("debug", `[MANAGER] Player state saved: ${guildId}`);
236+
} catch (error) {
237+
console.error(`Error saving player state for guild ${guildId}:`, error);
238+
}
242239
}
243240

244241
/**
@@ -405,36 +402,37 @@ export class Manager extends EventEmitter {
405402
* Optionally, it also calls {@link cleanupInactivePlayers} to remove any stale player state files.
406403
* After saving and cleaning up, it exits the process.
407404
*/
408-
private async handleShutdown(): Promise<void> {
405+
public async handleShutdown(): Promise<void> {
409406
console.warn("\x1b[31m%s\x1b[0m", "MAGMASTREAM WARNING: Shutting down! Please wait, saving active players...");
410407

411-
// Create an array of promises for saving player states
412-
const savePromises = Array.from(this.players.keys()).map((guildId) => {
413-
return new Promise<void>(async (resolve, reject) => {
408+
try {
409+
const savePromises = Array.from(this.players.keys()).map(async (guildId) => {
410+
console.log(`Saving state for guild: ${guildId}`); // Debugging
414411
try {
415-
await this.savePlayerState(guildId); // Await the save operation
416-
resolve();
412+
await this.savePlayerState(guildId);
417413
} catch (error) {
418414
console.error(`Error saving player state for guild ${guildId}:`, error);
419-
reject(error); // Reject the promise to propagate the error
420415
}
421416
});
422-
});
423-
424-
// Wait for all save operations to complete and check for errors
425-
const results = await Promise.allSettled(savePromises);
426-
const errors = results.filter((result) => result.status === "rejected");
427417

428-
if (errors.length > 0) {
429-
console.error("`\x1b[31m%s\x1b[0m", `MAGMASTREAM ERROR: ${errors.length} player states failed to save.`);
430-
}
418+
console.log("Waiting for all player states to save...");
419+
await Promise.allSettled(savePromises);
420+
console.log("All player states saved.");
431421

432-
// Clean up inactive players here
433-
this.cleanupInactivePlayers();
422+
console.log("Cleaning up inactive players...");
423+
await this.cleanupInactivePlayers();
424+
console.log("Cleanup complete.");
434425

435-
console.warn("\x1b[32m%s\x1b[0m", "MAGMASTREAM INFO: Shutting down complete, exiting...");
426+
console.warn("\x1b[32m%s\x1b[0m", "MAGMASTREAM INFO: Shutting down complete, exiting...");
436427

437-
setTimeout(() => process.exit(errors.length > 0 ? 1 : 0), 100);
428+
setTimeout(() => {
429+
console.log("Exiting process...");
430+
process.exit(0);
431+
}, 500);
432+
} catch (error) {
433+
console.error("Unexpected error during shutdown:", error);
434+
process.exit(1);
435+
}
438436
}
439437

440438
/**
@@ -489,8 +487,36 @@ export class Manager extends EventEmitter {
489487
for (const nodeOptions of this.options.nodes) new (Structure.get("Node"))(nodeOptions);
490488
}
491489

492-
process.on("SIGINT", async () => await this.handleShutdown());
493-
process.on("SIGTERM", async () => await this.handleShutdown());
490+
process.on("SIGINT", async () => {
491+
console.warn("\x1b[33mSIGINT received! Graceful shutdown initiated...\x1b[0m");
492+
493+
try {
494+
await this.handleShutdown();
495+
console.warn("\x1b[32mShutdown complete. Waiting for Node.js event loop to empty...\x1b[0m");
496+
497+
// Prevent forced exit by Windows
498+
setTimeout(() => {
499+
console.log("Exiting now...");
500+
process.exit(0);
501+
}, 2000);
502+
} catch (error) {
503+
console.error("Error during shutdown:", error);
504+
process.exit(1);
505+
}
506+
});
507+
508+
process.on("SIGTERM", async () => {
509+
console.warn("\x1b[33mSIGTERM received! Graceful shutdown initiated...\x1b[0m");
510+
511+
try {
512+
await this.handleShutdown();
513+
console.warn("\x1b[32mShutdown complete. Exiting now...\x1b[0m");
514+
process.exit(0);
515+
} catch (error) {
516+
console.error("Error during SIGTERM shutdown:", error);
517+
process.exit(1);
518+
}
519+
});
494520
}
495521

496522
/**

0 commit comments

Comments
 (0)