Skip to content
This repository has been archived by the owner on Jun 27, 2024. It is now read-only.

Commit

Permalink
[2.2.0] Release
Browse files Browse the repository at this point in the history
## [2.2.0]

* [Server] Implement timed server stops similar to timed server restarts
* [Config] Only save EQEmu config when the values change
* [Hot Reload] Update file watching logic to only watch files that end in extensions `.pl` `.lua` without hyper extensions eg (`.pl.swp` or `.lua.swp`)
  • Loading branch information
Akkadius committed Jan 9, 2023
1 parent 8ec264d commit 298ffa2
Show file tree
Hide file tree
Showing 11 changed files with 227 additions and 114 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [2.2.0]

* [Server] Implement timed server stops similar to timed server restarts
* [Config] Only save EQEmu config when the values change
* [Hot Reload] Update file watching logic to only watch files that end in extensions `.pl` `.lua` without hyper extensions eg (`.pl.swp` or `.lua.swp`)

## [2.1.2]

* Fix edge case where the server config can possibly be saved with no data
Expand Down
2 changes: 1 addition & 1 deletion app.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ async () => {
/**
* Hot Reload Service
*/
hotReloadService.startListener();
hotReloadService.startWatchers();

/**
* NetStat Listeners
Expand Down
2 changes: 1 addition & 1 deletion app/commands/hot-reload-listener.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module.exports = {
* @param options
*/
startListener: function (options) {
require('../core/hot-reload-service').startListener(options);
require('../core/hot-reload-service').startWatchers(options);
},

};
13 changes: 10 additions & 3 deletions app/core/eqemu-config-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,11 +176,18 @@ module.exports = {
* @returns {module.exports}
*/
setAdminPanelConfig(accessor, value) {
dot.override = true;
if (this.getAdminPanelConfig(accessor) !== value) {
console.log(chalk`{green [{bold EQEmuConfig}] [setAdminPanelConfig] Setting [${accessor}] to [${value}] }`);

dot.str('web-admin.' + accessor, value, this.getServerConfig());
dot.override = true;
dot.str('web-admin.' + accessor, value, this.getServerConfig());

this.saveServerConfig(this.serverConfig);
this.saveServerConfig(this.serverConfig);

return this
}

console.log(chalk`{yellow [{bold EQEmuConfig}] [setAdminPanelConfig] Failed to set [${accessor}] to [${value}] because it was already set }`);

return this;
},
Expand Down
163 changes: 95 additions & 68 deletions app/core/hot-reload-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ const zoneRepository = use('/app/repositories/zoneRepository');
module.exports = {
zones: null,

// pointer to the watcher(s)
questWatcher: null,
pluginWatcher: null,
luaModulesWatcher: null,

loadZones: async function () {
this.zones = await zoneRepository.all();

Expand All @@ -40,8 +45,7 @@ module.exports = {
/**
* @return {boolean}
*/
startListener: async function (options) {

startWatchers: async function (options) {
await eqemuConfigService.init();
await database.init();
await this.loadZones();
Expand All @@ -53,83 +57,106 @@ module.exports = {

debug('Starting listener for [%s]', pathManager.getEmuQuestsPath())

let self = this;

/**
* Lua Modules
*/
watch(pathManager.getEmuLuaModulesPath(), { recursive: true }, function (evt, file) {
if (!["lua", "pl"].includes(file.split('.').pop())) {
return false
}

if (evt === 'update') {
const changedFile = path.dirname(file).split(path.sep).pop() + '/' + path.basename(file);

self.message(util.format(
chalk`[{bold lua_modules}] Reloading [{bold All Zones}] File [{bold %s}]`,
changedFile
));
serverDataService.hotReloadZoneQuests('all');
}
});

/**
* Plugins
*/
watch(pathManager.getEmuPluginsPath(), { recursive: true }, function (evt, file) {
if (!["lua", "pl"].includes(file.split('.').pop())) {
return false
}
this.startLuaModulesWatcher()
this.startPluginsWatcher()
this.startQuestWatcher()
},

if (evt === 'update') {
const changedFile = path.dirname(file).split(path.sep).pop() + '/' + path.basename(file);
stopWatchers: function() {
this.message('Stopping watchers')

self.message(util.format(
chalk`[{bold plugins}] Reloading [{bold All Zones}] File [{bold %s}]`,
changedFile
));
let watchers = [
this.luaModulesWatcher,
this.pluginWatcher,
this.questWatcher,
];

serverDataService.hotReloadZoneQuests('all');
for (let w of watchers) {
if (!w.isClosed()) {
w.close()
}
});
}
},

/**
* Quests
*/
watch(pathManager.getEmuQuestsPath(), { recursive: true }, function (evt, file) {
if (!["lua", "pl"].includes(file.split('.').pop())) {
return false
startLuaModulesWatcher: function() {
this.luaModulesWatcher = watch(
pathManager.getEmuLuaModulesPath(),
{
recursive: true,
filter: f => this.watchedFiles(f)
}, (e, file) => {
if (e === 'update') {
const changedFile = path.dirname(file).split(path.sep).pop() + '/' + path.basename(file);

this.message(util.format(
chalk`[{bold lua_modules}] Reloading [{bold All Zones}] File [{bold %s}]`,
changedFile
));
serverDataService.hotReloadZoneQuests('all');
}
}
);
},

self.message(util.format(chalk`[{bold Quests}] File [{bold %s}] changed`, file));

if (evt === 'update') {
const changedFile = path.dirname(file).split(path.sep).pop() + '/' + path.basename(file);
const changedZone = path.dirname(file).split(path.sep).pop();

const changedData = util.format(
chalk`File [{bold %s}]`,
changedFile
);

/**
* Zone
*/
if (self.doesZoneExist(changedZone)) {
self.message(util.format(chalk`[{bold zone}] Reloading [{bold %s}] %s`, changedZone, changedData));
serverDataService.hotReloadZoneQuests(changedZone);
}
startPluginsWatcher: function() {
this.pluginWatcher = watch(
pathManager.getEmuPluginsPath(),
{
recursive: true,
filter: f => this.watchedFiles(f)
}, (e, file) => {
if (e === 'update') {
const changedFile = path.dirname(file).split(path.sep).pop() + '/' + path.basename(file);

this.message(util.format(
chalk`[{bold plugins}] Reloading [{bold All Zones}] File [{bold %s}]`,
changedFile
));

/**
* Global
*/
if (changedZone === 'global') {
self.message(util.format(chalk`[{bold global}] Reloading [{bold %s}] %s`, 'All Zones', changedData));
serverDataService.hotReloadZoneQuests('all');
}
}
});
);
},

startQuestWatcher: function() {
this.questWatcher = watch(
pathManager.getEmuQuestsPath(),
{
recursive: true,
filter: f => this.watchedFiles(f)
}, (e, file) => {
this.message(util.format(chalk`[{bold Quests}] File [{bold %s}] changed`, file));

if (e === 'update') {
const changedFile = path.dirname(file).split(path.sep).pop() + '/' + path.basename(file);
const changedZone = path.dirname(file).split(path.sep).pop();

const changedData = util.format(
chalk`File [{bold %s}]`,
changedFile
);

/**
* Zone
*/
if (this.doesZoneExist(changedZone)) {
this.message(util.format(chalk`[{bold zone}] Reloading [{bold %s}] %s`, changedZone, changedData));
serverDataService.hotReloadZoneQuests(changedZone);
}

/**
* Global
*/
if (changedZone === 'global') {
this.message(util.format(chalk`[{bold global}] Reloading [{bold %s}] %s`, 'All Zones', changedData));
serverDataService.hotReloadZoneQuests('all');
}
}
}
);
},
watchedFiles: function(f) {
return f.endsWith('.pl') || f.endsWith('.lua')
}
};
51 changes: 48 additions & 3 deletions app/core/server-process-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ module.exports = {
launchOptions: {},
watchDogTimer: null,
cancelTimedRestart: false,
cancelTimedShutdown: false,
lastProcessPollTime: Math.floor(new Date() / 1000),

/**
Expand Down Expand Up @@ -218,9 +219,53 @@ module.exports = {
/**
* @returns {Promise<void>}
*/
stopServer: async function () {
stopServer: async function (options = []) {
this.init([], true);

// reset timed restart if triggered restart again
this.cancelTimedShutdown = false;

// delayed restart
if (options.timer && options.timer > 0) {
const startTime = Math.floor(new Date() / 1000);
const endTime = startTime + parseInt(options.timer);
let shutdownTime = false;
let lastWarningTime = Math.floor(new Date() / 1000) - 1000;

while (!shutdownTime) {
const minutesRemaining = (endTime - Math.floor(new Date() / 1000)) / 60;
const unixTimeNow = Math.floor(new Date() / 1000);

if ((lastWarningTime + 30) <= unixTimeNow) {
lastWarningTime = unixTimeNow;

const worldMessage = util.format(
'[SERVER MESSAGE] The world will be coming down in [%s] minute(s), please log out before this time...',
Number((minutesRemaining).toFixed(2))
);

await serverDataService.messageWorld(worldMessage);

debug('Sending world message | %s', worldMessage);
}

if (unixTimeNow > endTime) {
shutdownTime = true;
}

if (this.cancelTimedShutdown) {
break;
}

await this.sleep(1000);
}
}

if (this.cancelTimedShutdown) {
debug('Shutdown cancelled');
return false;
}

await this.pollProcessList();

debug('[stopServer] Stopping server...');
Expand Down Expand Up @@ -394,7 +439,8 @@ module.exports = {
async cancelRestart(options = []) {
if (options.cancel) {
this.cancelTimedRestart = true;
await serverDataService.messageWorld('[SERVER MESSAGE] Server restart cancelled');
this.cancelTimedShutdown = true;
await serverDataService.messageWorld('[SERVER MESSAGE] Server stop cancelled');
}
},

Expand All @@ -409,7 +455,6 @@ module.exports = {

// delayed restart
if (options.timer && options.timer > 0) {

const startTime = Math.floor(new Date() / 1000);
const endTime = startTime + parseInt(options.timer);
let rebootTime = false;
Expand Down
Loading

0 comments on commit 298ffa2

Please sign in to comment.