JS Консольные команды для сервера Rage MP (console commands)

youtube tumbnail.png
По умолчанию в консоль сервера нельзя ничего ввести. И сам api Rage Multiplayer не дает возможности объявлять свои консольные команды.
Но поскольку сервер использует node js, то мы можем воспользоваться некоторыми его встроенными библиотеками чтобы реализовать это.


Так как команды консольные, то весь код будет серверный. Вначале мы подключим модуль readline.
JavaScript:
const readline = require('readline');

Далее инициализируем интерфейс для работы с консолью.
JavaScript:
const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

Теперь мы можем построчно читать текст, которые вводится в консоль сервера.
JavaScript:
rl.on('line', (input) => {
  console.log(`Введен текст: ${input}`);
});
cl1.JPG

Отлично! Это то, что мне нужно. Теперь нужно реализовать обработку введенных команд. Для своего удобства я сделаю так, чтобы команды начинались со слеша. И при обработки введенного текста мы могли точно определить что пользователь ввел какую-то команду.
JavaScript:
rl.on('line', (input) => {

    if(input.charAt(0) == '/'){
        console.log(`Введена команда: ${input}`);
    }
   
});
cl2.JPG

Пришло время добавить нашу первую команду. Чтобы код команд был более читаемым и не завязанным на обработчике line - я буду объявлять каждую команду отдельным событием. Но название события будет иметь определенный формат - "console:Команда". И в обработчике line я зная название команды просто буду вызывать этот event.
JavaScript:
rl.on('line', (input) => {

    if(input.charAt(0) == '/'){
        const commandName = input.substr(1); // Получаем название команды без слеша
        mp.events.call(`console:${commandName}`);
    }
});

Например, сделаем простенькую команду /help
JavaScript:
mp.events.add("console:help", () => {
    console.log('Привет - это команда /help');
});
cl3.JPG

Теперь сделаем что-нибудь более полезное. Например, команду которая выведет в консоль список игроков онлайн. Тоже все просто.

JavaScript:
mp.events.add("console:players", () => {
    let count = 0;
    console.log('Игроки онлайн:');
    mp.players.forEach((player) => {
        console.log(`${player.name} (ID: ${player.id})`);
        count++;
    });
    console.log(`Всего игроков: ${count}`);
});
cl4.JPG

Дальше логично добавить какие-то админ команды. Например, круто было бы иметь возможность не заходя на сервер кикнуть какого-то игрока прямо из консоли. Но здесь нам нужно будет передать в команду ID игрока.
Пока что наш обработчик не умеет в аргументы. Давайте немного улучшим его :) Вначале мы будем разбивать наш input в массив по пробелам. Первый элемент массива будет нашей командой, а остальные уже аргументы. Массив аргументов мы просто передадим в обработчик самой команды и там где они нужны - они будут обрабатываться уже на уровне самой команды.

JavaScript:
rl.on('line', (input) => {

    if(input.charAt(0) == '/'){
        const data = input.substr(1).split(' '); // убираем слеш и преобразовываем строку в массив разделяя по пробелам
        const commandName = data[0];
        const args = data.slice(1); // из data убираем первый элемент - название команды и получаем массив только аргументов args
        mp.events.call(`console:${commandName}`, args); // передаем args в обработчик команды
    }
});

Теперь можно легко реализовать команду /kick playerID
JavaScript:
mp.events.add("console:kick", (args) => {
    const playerId = args[0]; // id игрока у нас должен быть в первом элементе массива args
    if(!playerId || isNaN(playerId)) return console.log("Неверно указан Id игрока. Команда: /kick playerID");
   
    const player = mp.players.at(playerId);
    if(!player) return console.log(`Игрок с id ${playerId} оффлайн`); // Проверяем чтобы игрок был онлайн

    player.kick();
});

Еще крутая возможность при работе с readline - это возможность делать простые диалоги и получать ответы. Например, я хочу сделать команду /shutdown. Но чтобы при ее вводе у меня в консоли спрашивало подтверждение, чтобы я случайно не отключил сервер.
Для этого у объекта rl есть метод question. Первый аргумент - это текст, который выведет в консоль. Второй - функция-обработчик, куда будет передан ответ пользователя.
JavaScript:
mp.events.add("console:shutdown", () => {
    rl.question("Вы уверены что хотите выключить сервер? (Да/Нет)", (answer) => {
        if(answer == 'Да'){
            console.log(`Выключаем сервер...`);
            rl.close();
            process.exit(0);
        }
    });
});
cl5.JPG
В данном случае мы обрабатываем только ответ 'Да', т. к. при любом другом вводе мы не будем выключать сервер.

Как видите консольные команды позволяют нам сделать много интересных вещей. В rage:mp мы легко можем пользоваться ими, например так, как я показал в этом уроке. Обработчик можно и дальше улучшать, но многие базовые вещи он позволит решить.

Полезные ссылки:
 

geneff

Middle Developer
Скриптер
JavaScript:
rl.on('line', (input) => {
    if(input.charAt(0) == '/'){
        const [cmdName, playerId] = input.slice(1).split(' ');
        mp.events.call(`console:${cmdName}`, playerId); // передаем args в обработчик команды
    }
});

mp.events.add("console:kick", (playerId) => {
    if(!playerId || isNaN(playerId)) return console.log("Неверно указан Id игрока. Команда: /kick playerID");
  
    const player = mp.players.at(playerId);
    if(!player) return console.log(`Игрок с id ${playerId} оффлайн`); // Проверяем чтобы игрок был онлайн

    player.kick();
});

:unsure:
 

Lev Angel

Developer
Команда форума
Скриптер
Не, так не пойдет :)
Идея в том, чтобы rl.on('line') был универсальным для всех команд. При добавлении новой команды мы его вообще не трогаем. А могут быть команды где передается не только playerId или что-то другое
 

geneff

Middle Developer
Скриптер
Не, так не пойдет :)
Идея в том, чтобы rl.on('line') был универсальным для всех команд. При добавлении новой команды мы его вообще не трогаем. А могут быть команды где передается не только playerId или что-то другое
JavaScript:
rl.on('line', (input) => {
    if(input.charAt(0) == '/'){
        const [cmdName, ...args] = input.slice(1).split(' ');
        mp.events.call(`console:${cmdName}`, ...args); // передаем args в обработчик команды
    }
});

:unsure: :unsure: :unsure: :unsure: :unsure:
 

Lev Angel

Developer
Команда форума
Скриптер
JavaScript:
rl.on('line', (input) => {
    if(input.charAt(0) == '/'){
        const [cmdName, ...args] = input.slice(1).split(' ');
        mp.events.call(`console:${cmdName}`, ...args); // передаем args в обработчик команды
    }
});

:unsure: :unsure: :unsure: :unsure: :unsure:
😁

Может быть ты еще сможешь добавить кое что?))
Например, команды выглядит так
/kick playerId reason
/kick 0 "Чит на деньги"

Короче чтобы если идет какой-то текст в кавычках, то его воспринимало как один аргумент. Я чет кроме хитрых регулярок ничего не придумал и забил на это.
 

geneff

Middle Developer
Скриптер
Можно сделать типо как в addPlayerCommand, передавать fulltext
JavaScript:
rl.on('line', (input) => {

    if(input.charAt(0) == '/'){
        const [cmdName, ...args] = input.slice(1).split(' ');
        mp.events.call(`console:${cmdName}`, args, ...args); // передаем args в обработчик команды
    }
});

mp.events.add("console:kick", (fulltext, playerId) => {
    if(!playerId || isNaN(playerId)) return console.log("Неверно указан Id игрока. Команда: /kick playerID");
 
    const player = mp.players.at(playerId);
    if(!player) return console.log(`Игрок с id ${playerId} оффлайн`); // Проверяем чтобы игрок был онлайн

    const reason = fulltext.slice(1).join(' ');
    console.log(`Вы успешно кикнули игрока ${player.name}. Причина: ${reason}.`);
    player.kick();
});
 
Яндекс.Метрика
Верх