Permalink
Browse files

Implement most of `/lvp settings`

  • Loading branch information...
RussellLVP committed Oct 19, 2016
1 parent 2d94cc2 commit c6b779bb75a3e5f866a9eae038e1e3145b7b349d
View
@@ -222,6 +222,9 @@
"LVP_PLAYGROUND_OPTION_NO_CHANGE": "@error The {FF8282}%s{FFFFFF} feature has already been %s.",
"LVP_RELOAD_NOT_ELIGIBLE": "@error The {FF8282}%s{FFFFFF} feature is not eligible for live reloading.",
"LVP_RELOAD_RELOADED": "@success The %s feature has been reloaded.",
"LVP_SETTING_INVALID_NUMBER": "Whuh? {FFA500}%s{A9C4E4} is not a number, you silly billy!",
"LVP_SETTING_TOGGLED": "The {FFA500}%s{A9C4E4} setting has been %s.",
"LVP_SETTING_UPDATED": "The {FFA500}%s{A9C4E4} setting has been updated to {FFA500}%s{A9C4E4}.",
"POSITIONING_CURRENT_POSITION": "Your current position is X: %d, Y: %d Z: %d, Rotation: %d",
"POSITIONING_OTHER_USAGE_POS": "{FF9900}Other usage{FFFFFF}: /pos [x] [y] [z]",
@@ -9,6 +9,7 @@ server and the commands offered on the server.
- **/lvp access**: Changes access rights for one of the commands in this feature.
- **/lvp party [on/off]**: Toggles the Party Mode on or off. Only available to Management.
- **/lvp settings**: Enables Management to change various run-time settings on the server.
## Commands
The following commands are available as part of the feature:
@@ -16,10 +16,13 @@ class Playground extends Feature {
// Used for announcing changes in feature availability to players.
const announce = this.defineDependency('announce');
// The Playground feature provides an interface in the mutable settings.
const settings = this.defineDependency('settings');
this.access_ = new PlaygroundAccessTracker();
this.manager_ = new PlaygroundManager();
this.commands_ = new PlaygroundCommands(this.manager_, this.access_, announce);
this.commands_ = new PlaygroundCommands(this.manager_, this.access_, announce, settings);
// Activate the features that should be activated by default.
this.manager_.initialize();
@@ -8,6 +8,7 @@ const Menu = require('components/menu/menu.js');
const MessageBox = require('components/dialogs/message_box.js');
const PlaygroundAccessTracker = require('features/playground/playground_access_tracker.js');
const Question = require('components/dialogs/question.js');
const Setting = require('features/settings/setting.js');
// Utility function to capitalize the first letter of a |string|.
function capitalizeFirstLetter(string) {
@@ -16,9 +17,10 @@ function capitalizeFirstLetter(string) {
// A series of general commands that don't fit in any particular
class PlaygroundCommands {
constructor(manager, access, announce) {
constructor(manager, access, announce, settings) {
this.manager_ = manager;
this.announce_ = announce;
this.settings_ = settings;
this.access_ = access;
this.commands_ = new Map();
@@ -77,6 +79,9 @@ class PlaygroundCommands {
.restrict(Player.LEVEL_MANAGEMENT)
.parameters([ { name: 'feature', type: CommandBuilder.WORD_PARAMETER } ])
.build(PlaygroundCommands.prototype.onPlaygroundReloadCommand.bind(this))
.sub('settings')
.restrict(Player.LEVEL_MANAGEMENT)
.build(PlaygroundCommands.prototype.onPlaygroundSettingsCommand.bind(this))
.build(PlaygroundCommands.prototype.onPlaygroundCommand.bind(this));
}
@@ -330,6 +335,145 @@ class PlaygroundCommands {
}
// Displays a series of menus to Management members that want to inspect or make changes to how
// certain features on the server work, for example, the abuse and housing systems.
async onPlaygroundSettingsCommand(player) {
const categories = new Map();
const menu = new Menu('Choose a category of settings', ['Category', 'Settings']);
// Identify the categories of settings that exist on the server.
for (const setting of this.settings_().getSettings()) {
if (!categories.has(setting.category))
categories.set(setting.category, new Set());
categories.get(setting.category).add(setting);
}
const sortedCategories = Array.from(categories.keys()).sort();
for (const category of sortedCategories) {
const settings = categories.get(category);
const settingsLabel = settings.size == 1 ? '1 setting'
: settings.size + ' settings';
// Adds a menu item to display the entries in the |category|.
menu.addItem(category, settingsLabel, async(player) => {
const sortedSettings =
Array.from(settings).sort((lhs, rhs) => lhs.name.localeCompare(rhs.name));
const innerMenu = new Menu('Choose a setting', ['Setting', 'Value']);
for (const setting of sortedSettings) {
let valueLabel = 'null';
switch (setting.type) {
case Setting.TYPE_BOOLEAN:
valueLabel = setting.value ? 'enabled' : 'disabled';
if (setting.value != setting.defaultValue) {
valueLabel = '{FFFF00}' + valueLabel + '{FFFFFF} (default: ' +
(setting.defaultValue ? 'enabled' : 'disabled') + ')';
}
break;
case Setting.TYPE_NUMBER:
valueLabel = '' + setting.value;
if (setting.value != setting.defaultValue) {
valueLabel = '{FFFF00}' + valueLabel + '{FFFFFF} (default: ' +
setting.defaultValue + ')';
}
break;
default:
throw new Error('Invalid setting type for ' + setting.name);
}
// Add a menu item for the particular setting, that will in turn defer to a
// function that allows the particular setting to be changed.
innerMenu.addItem(setting.name, valueLabel, async(player) => {
switch (setting.type) {
case Setting.TYPE_BOOLEAN:
await this.handleBooleanSettingModification(player, setting);
break;
case Setting.TYPE_NUMBER:
await this.handleNumberSettingModification(player, setting);
break;
}
});
}
await innerMenu.displayForPlayer(player);
});
}
await menu.displayForPlayer(player);
}
// Handles the |player| modifying the |setting|, which represents a boolean value.
async handleBooleanSettingModification(player, setting) {
const menu = new Menu(setting.description);
// Create creative labels that describe both the current value, the new value and the
// default value that was configured for the setting in its entirety.
const enableLabel = (setting.value ? '{FFFF00}' : '') + 'Enable' +
(setting.defaultValue ? ' {FFFFFF}(default)' : '');
const disableLabel = (!setting.value ? '{FFFF00}' : '') + 'Disable' +
(!setting.defaultValue ? ' {FFFFFF}(default)' : '');
menu.addItem(enableLabel, async(player) => {
this.settings_().setValue(setting.identifier, true);
// TODO: Distribute an update to administrators.
return await MessageBox.display(player, {
title: 'The setting has been enabled!',
message: Message.format(Message.LVP_SETTING_TOGGLED, setting.identifier, 'enabled')
});
});
menu.addItem(disableLabel, async(player) => {
this.settings_().setValue(setting.identifier, false);
// TODO: Distribute an update to administrators.
return await MessageBox.display(player, {
title: 'The setting has been disabled!',
message: Message.format(Message.LVP_SETTING_TOGGLED, setting.identifier, 'disabled')
});
});
await menu.displayForPlayer(player);
}
// Handles the |player| modifying the |setting|, which represents a numeric value.
async handleNumberSettingModification(player, setting) {
const answer = await Question.ask(player, {
question: 'Update the ' + setting.identifier + ' setting',
message: setting.description + '\nThe default value is {FFA500}' +
setting.defaultValue + '{A9C4E4}',
leftButton: 'Update'
});
if (!answer)
return; // the |player| cancelled the dialog.
if (!answer.isSafeInteger()) {
return await MessageBox.display(player, {
title: 'Invalid value for the ' + setting.identifier + ' setting!',
message: Message.format(Message.LVP_SETTING_INVALID_NUMBER, answer)
});
}
this.settings_().setValue(setting.identifier, answer.toSafeInteger());
// TODO: Distribute an update to administrators.
return await MessageBox.display(player, {
title: 'The setting has been disabled!',
message: Message.format(Message.LVP_SETTING_UPDATED, setting.identifier, answer)
});
}
// Displays some generic information for those typing `/lvp`. Administrators and higher will see
// a list of sub-commands that they're allowed to execute.
onPlaygroundCommand(player) {
@@ -339,7 +483,7 @@ class PlaygroundCommands {
options.push('access');
if (player.isManagement())
options.push('party', 'reload');
options.push('party', 'reload', 'settings');
player.sendMessage(Message.LVP_PLAYGROUND_HEADER);
if (!options.length)
@@ -15,10 +15,11 @@ describe('PlaygroundCommands', (it, beforeEach, afterEach) => {
beforeEach(() => {
const announce = new MockAnnounce();
const settings = server.featureManager.loadFeature('settings');
access = new PlaygroundAccessTracker();
manager = new PlaygroundManager();
commands = new PlaygroundCommands(manager, access, () => announce);
commands = new PlaygroundCommands(manager, access, () => announce, () => settings);
gunther = server.playerManager.getById(0 /* Gunther */);
gunther.identify();
@@ -152,4 +153,60 @@ describe('PlaygroundCommands', (it, beforeEach, afterEach) => {
assert.equal(access.getCommandLevel(COMMAND_NAME), Player.LEVEL_ADMINISTRATOR);
assert.equal(access.getDefaultCommandLevel(COMMAND_NAME), Player.LEVEL_ADMINISTRATOR);
});
it('should be able to change boolean settings', async(assert) => {
const settings = server.featureManager.loadFeature('settings');
const russell = server.playerManager.getById(1 /* Russell */);
russell.identify();
gunther.level = Player.LEVEL_MANAGEMENT;
// Disable the `spawn_vehicle_admin_override` section in the `abuse` section.
gunther.respondToDialog({ listitem: 0 /* Assumed `abuse` */ }).then(
() => gunther.respondToDialog({ listitem: 3 /* Assumed to be the override */ })).then(
() => gunther.respondToDialog({ listitem: 1 /* Disable */ })).then(
() => gunther.respondToDialog({ response: 1 /* Yeah I get it */ }));
assert.isTrue(settings.getValue('abuse/spawn_vehicle_admin_override'));
assert.isTrue(await gunther.issueCommand('/lvp settings'));
assert.isFalse(settings.getValue('abuse/spawn_vehicle_admin_override'));
// Enable the `spawn_vehicle_admin_override` section in the `abuse` section.
gunther.respondToDialog({ listitem: 0 /* Assumed `abuse` */ }).then(
() => gunther.respondToDialog({ listitem: 3 /* Assumed to be the override */ })).then(
() => gunther.respondToDialog({ listitem: 0 /* Disable */ })).then(
() => gunther.respondToDialog({ response: 1 /* Yeah I get it */ }));
assert.isTrue(await gunther.issueCommand('/lvp settings'));
assert.isTrue(settings.getValue('abuse/spawn_vehicle_admin_override'));
});
it('should be able to change numeric settings', async(assert) => {
const settings = server.featureManager.loadFeature('settings');
const russell = server.playerManager.getById(1 /* Russell */);
russell.identify();
gunther.level = Player.LEVEL_MANAGEMENT;
// Disable the `spawn_vehicle_admin_override` section in the `abuse` section.
gunther.respondToDialog({ listitem: 0 /* Assumed `abuse` */ }).then(
() => gunther.respondToDialog({ listitem: 0 /* Assumed to be the damage time */ })).then(
() => gunther.respondToDialog({ response: 1, inputtext: '20' })).then(
() => gunther.respondToDialog({ response: 1 /* Yeah I get it */ }));
assert.equal(settings.getValue('abuse/blocker_damage_issued_time'), 10);
assert.isTrue(await gunther.issueCommand('/lvp settings'));
assert.equal(settings.getValue('abuse/blocker_damage_issued_time'), 20);
// Enable the `spawn_vehicle_admin_override` section in the `abuse` section.
gunther.respondToDialog({ listitem: 0 /* Assumed `abuse` */ }).then(
() => gunther.respondToDialog({ listitem: 0 /* Assumed to be the damage time */ })).then(
() => gunther.respondToDialog({ response: 1, inputtext: '10' })).then(
() => gunther.respondToDialog({ response: 1 /* Yeah I get it */ }));
assert.isTrue(await gunther.issueCommand('/lvp settings'));
assert.equal(settings.getValue('abuse/blocker_damage_issued_time'), 10);
});
});
@@ -22,6 +22,9 @@ class Setting {
// Gets the name of the setting within the category.
get name() { return this.name_; }
// Gets an identifier for the setting that contains both its category and its name.
get identifier() { return this.category_ + '/' + this.name_; }
// Gets the type of the setting. This is one of the Setting.TYPE_ constants.
get type() { return this.type_; }
@@ -36,7 +39,8 @@ class Setting {
get description() { return this.description_; }
}
// Variable types of the settings that are known.
// Variable types of the settings that are known. Update the `/lvp settings` command display when
// adding or removing types from here as well.
Setting.TYPE_BOOLEAN = 0;
Setting.TYPE_NUMBER = 1;
@@ -19,7 +19,7 @@ class Settings extends Feature {
// Import the settings from the |SettingList| in to the local state.
for (const setting of SettingList)
this.settings_.set(setting.category + '/' + setting.name, setting);
this.settings_.set(setting.identifier, setting);
}
// ---------------------------------------------------------------------------------------------
@@ -51,7 +51,7 @@ class Settings extends Feature {
break;
case Setting.TYPE_NUMBER:
if (typeof value !== 'boolean')
if (typeof value !== 'number')
throw new Error('The value for ' + identifier + ' must be a number.');
break;

0 comments on commit c6b779b

Please sign in to comment.