Permalink
Browse files

Report illegal vehicle entry to administrators

  • Loading branch information...
RussellLVP committed Oct 20, 2016
1 parent 3cb3438 commit c5e8fe2e339f3f78745c0e354831a9ada48b81eb
View
@@ -1,6 +1,8 @@
{
"TEST_MESSAGE": "Hello, world!",
"ABUSE_ANNOUNCE_DETECTED": "%s (Id: %d) has just abused %s (%s time).",
"ANNOUNCE_ADMINISTRATORS": "{FFFF00}Admin notice{FFFFFF}: %s",
"ANNOUNCE_ALL": "{80CBC4}*** %s",
"ANNOUNCE_MINIGAME": "{CCD782}Sign up for the {838F31}%s{CCD782}! Type {838F31}%s{CCD782} to join.",
@@ -4,6 +4,7 @@
const AbuseConstants = require('features/abuse/abuse_constants.js');
const AbuseMitigator = require('features/abuse/abuse_mitigator.js');
const AbuseMonitor = require('features/abuse/abuse_monitor.js');
const AbuseNatives = require('features/abuse/abuse_natives.js');
const DamageManager = require('features/abuse/damage_manager.js');
const Feature = require('components/feature_manager/feature.js');
@@ -17,10 +18,15 @@ class Abuse extends Feature {
constructor() {
super();
// The announce feature enables abuse to be reported to administrators.
this.announce_ = this.defineDependency('announce');
// The settings for the Abuse system are configurable at runtime.
this.settings_ = this.defineDependency('settings');
this.mitigator_ = new AbuseMitigator();
this.monitor_ = new AbuseMonitor(this.announce_, this.settings_);
this.damageManager_ = new DamageManager(this.mitigator_, this.settings_);
this.natives_ = new AbuseNatives(this);
@@ -112,6 +118,12 @@ class Abuse extends Feature {
this.natives_.dispose();
this.natives_ = null;
this.damageManager_.dispose();
this.damageManager_ = null;
this.monitor_.dispose();
this.monitor_ = null;
this.mitigator_.dispose();
this.mitigator_ = null;
}
@@ -0,0 +1,87 @@
// Copyright 2016 Las Venturas Playground. All rights reserved.
// Use of this source code is governed by the MIT license, a copy of which can
// be found in the LICENSE file.
// The abuse monitor keeps an eye out for players who may abuse something on Las Venturas Playground
// and informs administrators of the event when something has been detected.
//
// The abuse monitor is able to detect the following kinds of abuse:
// 1) Illegal vehicle entry: entering a vehicle that is locked for the player.
//
class AbuseMonitor {
constructor(announce, settings) {
this.announce_ = announce;
this.settings_ = settings;
this.detected_ = new Map([
[ AbuseMonitor.TYPE_ILLEGAL_VEHICLE_ENTRY, new WeakMap() ]
]);
server.playerManager.addObserver(this);
}
// ---------------------------------------------------------------------------------------------
// Returns the maximum number of times to report a particular warning about a particular player.
getReportLimit() { return this.settings_().getValue('abuse/warning_report_limit'); }
// ---------------------------------------------------------------------------------------------
// Reports that the |player| has been detected for abusing |type|. Administrators will receive
// a configurable amount of warnings for the |player, type| tuple as well.
reportAbuse(player, type) {
const incidents = (this.detected_.get(type).get(player) || 0) + 1;
if (incidents <= this.getReportLimit()) {
const incidentDescription = this.getTypeDescription(type);
const incidentOrdinal = incidents.toOrdinalString();
this.announce_().announceToAdministrators(
Message.ABUSE_ANNOUNCE_DETECTED, player.name, player.id, incidentDescription,
incidentOrdinal);
}
// Do keep track of the new number of incidents for the |player|.
this.detected_.get(type).set(player, incidents);
}
// Returns the number of times the |player| has been caught for the various kinds of abuse.
getPlayerStatistics(player) {
// Utility function that returns the number of times |player| has been caught for |type|.
const createEntryForType = type =>
[ this.getTypeDescription(type), this.detected_.get(type).get(player) || 0 ];
return new Map([
createEntryForType(AbuseMonitor.TYPE_ILLEGAL_VEHICLE_ENTRY)
]);
}
// Gets the textual description for the abuse |type|.
getTypeDescription(type) {
switch (type) {
case AbuseMonitor.TYPE_ILLEGAL_VEHICLE_ENTRY:
return 'illegal vehicle entry';
default:
throw new Error('Unknown abuse type given: ' + type);
}
}
// ---------------------------------------------------------------------------------------------
// Called when the |player| enters the |vehicle|. Will report them for abuse when the vehicle
// was locked for them, and entry therefore shouldn't have been possible.
onPlayerEnterVehicle(player, vehicle) {
if (vehicle.isLockedForPlayer(player))
this.reportAbuse(player, AbuseMonitor.TYPE_ILLEGAL_VEHICLE_ENTRY);
}
// ---------------------------------------------------------------------------------------------
dispose() {
server.playerManager.removeObserver(this);
}
}
// The different sorts of abuse that can be detected by the monitor.
AbuseMonitor.TYPE_ILLEGAL_VEHICLE_ENTRY = 0;
exports = AbuseMonitor;
@@ -0,0 +1,101 @@
// Copyright 2016 Las Venturas Playground. All rights reserved.
// Use of this source code is governed by the MIT license, a copy of which can
// be found in the LICENSE file.
const AbuseMonitor = require('features/abuse/abuse_monitor.js');
describe('AbuseMonitor', (it, beforeEach) => {
let monitor = null;
let settings = null;
beforeEach(() => {
monitor = server.featureManager.loadFeature('abuse').monitor_;
settings = server.featureManager.loadFeature('settings');
});
it('should be able to detect and report illegal vehicle entry', assert => {
const gunther = server.playerManager.getById(0 /* Gunther */);
const russell = server.playerManager.getById(1 /* Russell */);
russell.identify();
russell.level = Player.LEVEL_ADMINISTRATOR;
const vehicle = server.vehicleManager.createVehicle({
modelId: 441,
position: new Vector(200, 300, 50)
});
vehicle.lockForPlayer(gunther);
// (1) Make sure that the abuse can be detected.
{
assert.isTrue(vehicle.isLockedForPlayer(gunther));
// Force |gunther| in the |vehicle|. This roughly matches how cheats enter vehicles.
gunther.enterVehicle(vehicle);
// Make sure that |russell| has received a warning about the incident.
assert.equal(russell.messages.length, 1);
assert.isTrue(
russell.messages[0].includes(
Message.format(Message.ABUSE_ANNOUNCE_DETECTED, gunther.name, gunther.id,
'illegal vehicle entry', '1st')));
// Make sure that the incident has been reported in |gunther|'s statistics.
const statistics = monitor.getPlayerStatistics(gunther);
assert.equal(statistics.get('illegal vehicle entry'), 1);
}
// (2) Make sure that the warnings setting will be respected.
{
settings.setValue('abuse/warning_report_limit', 10);
assert.equal(monitor.getReportLimit(), 10);
// Incidents should be reported up to the 10th time.
for (let i = 2; i <= 10; ++i) {
gunther.enterVehicle(vehicle);
assert.equal(russell.messages.length, i);
}
// The next incidents should not be reported to administrators anymore.
assert.equal(russell.messages.length, 10);
gunther.enterVehicle(vehicle);
gunther.enterVehicle(vehicle);
gunther.enterVehicle(vehicle);
gunther.enterVehicle(vehicle);
assert.equal(russell.messages.length, 10);
const statistics = monitor.getPlayerStatistics(gunther);
assert.equal(statistics.get('illegal vehicle entry'), 14);
}
});
it('should gather and have names for all sorts of abuse', assert => {
const gunther = server.playerManager.getById(0 /* Gunther */);
const types = new Set();
for (const name of Object.getOwnPropertyNames(AbuseMonitor)) {
if (name.startsWith('TYPE_'))
types.add(AbuseMonitor[name]);
}
assert.isAbove(types.size, 0);
// (1) Verify that all types have a description.
{
for (const type of types)
assert.equal(typeof monitor.getTypeDescription(type), 'string');
}
// (2) Verify that all types are included in the statistics.
{
const statistics = monitor.getPlayerStatistics(gunther);
for (const type of types)
assert.isTrue(statistics.has(monitor.getTypeDescription(type)));
}
});
});
@@ -15,4 +15,6 @@ exports = [
new Setting('abuse', 'teleportation_admin_override', Setting.TYPE_BOOLEAN, true, 'Should administrators override teleportation restrictions?'),
new Setting('abuse', 'teleportation_throttle_time', Setting.TYPE_NUMBER, 180, 'Minimum number of seconds between teleporting twice.'),
new Setting('abuse', 'warning_report_limit', Setting.TYPE_NUMBER, 3, 'Number of types to report a specific abuse type for a player.'),
];
View
@@ -8,6 +8,7 @@ const TestRunner = require('base/test/test_runner.js');
// Import global objects.
require('base/color.js');
require('base/message.js');
require('base/number_util.js');
require('base/string_util.js');
require('base/time.js');
require('base/vector.js');
@@ -21,6 +21,8 @@ const MockTextLabel = require('entities/test/mock_text_label.js');
const MockVehicle = require('entities/test/mock_vehicle.js');
const Abuse = require('features/abuse/abuse.js');
const Communication = require('features/communication/communication.js');
const MockAnnounce = require('features/announce/test/mock_announce.js');
const Settings = require('features/settings/settings.js');
const Streamer = require('features/streamer/streamer.js');
@@ -46,6 +48,8 @@ class MockServer {
// Register features whose production versions are suitable for testing.
this.featureManager_.registerFeaturesForTests({
abuse: Abuse,
announce: MockAnnounce, // TODO: Move functionality to |communication|. See #309.
communication: Communication,
settings: Settings,
streamer: Streamer
});

0 comments on commit c5e8fe2

Please sign in to comment.