Бессонный кодер
4.57K subscribers
335 photos
61 videos
5 files
134 links
Канал разработчика и поисковика ДПСО LizaAlert

Связь: @sleeplessfeedbackbot или Direct канала (они бесплатные)
По рекламе: @dpp_ads
Купить рекламу: https://telega.in/c/sleeplesscode
Download Telegram
Свердловский депутат требует вернуть опасную детскую игру

Челябинский депутат Госдумы Лантратова требует запретить видеоигры

Вражеские бирюльки. РКН запретит культовые компьютерные игры для 87 млн

Депутаты Госдумы России предложили ограничить доступ к видеоиграм по «китайскому сценарию»

Депутат Гусев предложил ввести самозапрет на компьютерные игры: «Для многих это уже форма зависимости»


Мы уже привыкли к жёсткой стигматизации игры в СМИ как источника всего плохого и вообще чуть ли не абсолютного зла. И ведь причём это далеко не только наша и далеко не новая тенденция.

Многие политики, журналисты, да и обычные люди старших поколений привыкли обсуждать игры в двух полярностях: полезное и вредное, будто это какой-то прибор.
Но давайте честно — игры уже давно не просто безделушки. Это социальное, экономическое и даже политическое явление. Вещь, которая влияет на жизни миллионов людей.

А когда что-то влияет на мышление, поведение и отношения — она становится предметом этики.
А в ней нам надо ставить вопрос не "нравятся ли вам игры?", а "какими они нас делают?"

Причём это вопрос не только про агрессию или зависимость. Это вопрос про эмпатию, социализацию, моральный выбор, ощущение ответственности, отношение к насилию, смерти, власти, труду, даже к самому себе.

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

или
игры вредны

Хотя всё сильно сложнее.

Как думаете, наше политическое и социологическое развитие уже дошло до такого разговора — или пока нет?
Давайте обсудим это в рамках рубрики об этике в играх.
27🔥4👀4❤‍🔥1🤯1
Давайте немного о коде и решениях 😄

Имперский Стражник — большая система с кучей разных источников сообщений. Сообщение может уйти из одного из 200 классов ядра или ещё большего зоопарка внешних модулей, но есть одна важная проблема: отправку сообщений надо контролировать.

Надо следить за лимитами, чтобы не передалбывать Bot API зазря. Надо следить за ответами, чтобы, если мы вышли за лимит, подождать и повторно отправить запрос. Надо вообще убеждаться в доставке, синхронизировать состояние и ответы.

Короче, следить надо за всем. Так что давайте я расскажу, что мы сделали, чтобы всё это работало и жило с учётом нашего стека.

Начнём с учёта отправки, лимитов и структуризации. Я решил, что за отправку сообщений будет отвечать синглтон-сервис, на который все остальные сервисы будут скидывать задачи. Но как это всё соединить, учитывая необходимость быстрого масштабирования и подключения новых модулей?

Тут на помощь пришёл Bull (не реклама, если что, просто делюсь либой, на которой делал решение). Давайте посмотрим, как выглядит воркер отправки:


new Worker(process.env.DEBUG ? "guard_dev_send" : "guard_send", async (job) => { // тут я переключаю очередь в зависимости от режима, в котором запущен сервис, чтобы не задевать прод во время тестирования
let telegram = job.data.target === "media" ? mediaBot.telegram : bot.telegram; // в данный момент бот, через который будет идти отправка, выбирается немного костыльно, но мы в работе :D
try {
return await telegram.sendMessage(job.data.id, job.data.text, job.data.extra); // отправляем сообщение
} catch (e: any) {
if (e.code === 429) {
await job.moveToDelayed((Date.now() / 1000) + 30000); // словили рейтлимит, ждём 30 секунд и пробуем опять
} else {
e._sourceStack = job.data.stack; // подпихиваем стек до очереди в ошибку
e._stack = e.stack; // стек внутри очереди
e._message = e.message; // и причину
return e;
}
}
}, {
connection: {
host: process.env.REDIS_HOST,
port: Number.parseInt(process.env.REDIS_PORT ?? "6379")
},
limiter: { // режем скорость отправки
max: 2,
duration: 1000
},
concurrency: 1, // и ограничиваем параллельность
}).on("failed", (job, err) => {
Queues.logger.error({
job,
err
}, "Failed to send message"); // если что-то пошло не так, логируем
});


Небольшой такой код, но он позволяет нам чётко контролировать очередь и лимиты. А дальше посмотрим на отправку и приём)

В комментариях разрешается открыть портал в холивар))
21😁5🔥2🎉1
Давайте опять поговорим про очереди в Стражнике. Мы уже увидели сниппет кода, отвечающий за обработку, но как мы реализовали отправку? Вот тут всё начинает становиться сильно интереснее.

Основная сложность, с которой мы столкнулись, — необходимость рефакторить весь код. Хотелось избежать редактирования вообще всех мест, где идёт отправка сообщений. Поэтому мы вспомнили очень важную вещь, которая есть в JS: почти всё можно перезаписать по своему желанию. Этим мы и воспользовались, перезаписав метод отправки сообщения и подменив логику отправки в Telegram логикой отправки в очередь.

Получился вот такой прикольчик:


ctx.reply = async function (text: string, options?: ExtraSendMessage): Promise<Message> {
return Queues.getInstance().sendMessage("replyQueued", this.chat.id, text, options);
}


Тут мы перезаписали оригинальный метод ответа на сообщение своим собственным. Собственно, вуаля — половина работы отменена)

Дальше задача номер два, которая стала сильно интереснее: отправка сообщений разделяется на два типа — там, где нам нужен ответ, и там, где нам наплевать на ответ.

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

Что могло пойти не так? Да, как оказалось, много чего. У нас утекли листенеры. Их стало так много, что Node.js перестал справляться с их контролем.

Встал вопрос: как сделать решение, которое не перегрузит среду, но сможет дожидаться ответа по отправке сотен сообщений и делать это асинхронно?

И тут я придумал прикольное решение: создаём Map, который будет хранить ID задачи на отправку и коллбэк.


private _promisedMessages = new Map<string, { resolve: (message: Message) => void, reject: (error: Error) => void }>();


В отправке мы создаём задачу и промис, записывая в мапу ID задачи и resolve этого промиса в качестве коллбэка.


async sendMessage(target: TargetBot, name: string, id: number, text: string, extra: ExtraSendMessage = {}, priority = 10) {
let error = new Error("Stack trace"); // получаем стектрейс до этого момента
let job = await this._sendQueue.add(name, {id, text, extra, target, stack: error.stack}, {priority});
return new Promise<Message>((resolve, reject) => {
this._promisedMessages.set(job.id!, {resolve, reject});
});
}


И вместо сотен листенеров делаем один-единственный глобальный, который будет работать до самого завершения процесса:


new QueueEvents(process.env.DEBUG ? "guard_dev_send" : "guard_send", {
connection: {
host: process.env.REDIS_HOST,
port: Number.parseInt(process.env.REDIS_PORT ?? "6379"),
},
})
.on("completed", async ({jobId, returnvalue}) => {
if (this._promisedMessages.has(jobId)) { // проверяем, что это сообщение ожидается на этом воркере
let {resolve, reject} = this._promisedMessages.get(jobId)!; // получаем коллбэки этого сообщения
this._promisedMessages.delete(jobId); // удаляем его из мапы
resolve(returnvalue); // отдаём результат промису
}
});


И тем самым получается, что в команде код выглядит как самый обычный вызов:


if (!peer.isUserKnown()) {
await ctx.reply(await ctx.lang.t("commands.general.userNotFound"));
return;
}


А на самом деле за пару миллисекунд запрос обходит две очереди и несколько сервисов, выполняя работу чётенько и безопасненько)

С первого взгляда выглядит как быстрое и логичное решение, но мы к этому шли месяц-полтора.

Вот как-то так.
❤‍🔥245🔥4
Я живой (вроде), так что попробуем оживится ещё и в ваших глазах.
Встречаемся в 22:30 (МСК) в уже знакомом нам месте.

https://www.twitch.tv/sleepless_code
19❤‍🔥1
Бессонный кодер
Свердловский депутат требует вернуть опасную детскую игру Челябинский депутат Госдумы Лантратова требует запретить видеоигры Вражеские бирюльки. РКН запретит культовые компьютерные игры для 87 млн Депутаты Госдумы России предложили ограничить доступ к видеоиграм…
Игры всегда надо рассматривать с нескольких сторон. Всегда можно уйти в две крайности:
Первая — игры это зло, которое надо запретить.
Вторая — игры идеальны и ни в чём не виноваты.

Как говорили великие, нет ничего абсолютно чёрного и абсолютно белого. Это было бы слишком примитивно. Проблема в том, что реальность намного сложнее. (Если, конечно, не вспоминать The Horses.)

Игры могут делать совершенно разные вещи. Они могут:
- Помогать
- Развивать
- Объединять
- Давать убежище

Но при этом они могут:
- Вызывать зависимость (в частности, азартные)
- Усиливать агрессию
- Портить здоровье (особенно режим сна)

Из-за этого любая дискуссия легко превращается в банальный обмен простыми характеристиками. Что, кстати, очень напоминает комментарии в интернете.

Мне кажется, игры (хотя, возможно, не все — тут это уже моя субъективная точка зрения) не могут быть просто хорошими или плохими. Всё зависит от того, какие они, для кого они и что именно делают.
И это даже не вопрос качества уровня Concord. Это вопрос морально-этического осмысления. Потому что одна и та же игра для одного человека может стать способом социализации и поиска друзей, а для другого — способом убежать от проблем.

Именно поэтому мне кажется, что вопрос должен звучать не «хорошие игры или плохие», а «какое влияние оказывают конкретные игры и почему».

Давайте продолжим рассуждать вместе.
Как вы думаете, какие игры для кого и для чего существуют?
🔥301👍1
Игрок тоже несёт ответственность, это важно помнить, когда мы рассматриваем влияние игр.
И что самое главное, вы в комментариях под прошлым постом упомянули и предсказали этот пост)

Давайте вспомним любое массовое событие где подросток что-либо сделал, кого винят в первую и основную очередь — игры. Но при этом СМИ и государство никогда не говорят об самом человеке и его окружении, а ведь всё начинается с ответственности самого игрока.

Смысл игры создаётся не только разработчиком, но и людьми, которые играют в неё. Относительно этого феномена мне очень нравится дилемма Arc Riders.
Мы привыкли что в экстакшен шутере игроки достаточно агрессивны, но в Arc Riders появился необычный феномен, где игроки намеренно перестали, и даже наоборот, стали порицать убийства, буквально превратив PVP игру в PVE. (звукорежиссёр про это рассказывал очень хорошо)

У нас буквально рождается несколько типов поведения где игрок может:
- помогать команде и врагам
- токсичить в войсчате
- отстреливать всех встречающихся
- читерить
- помогать игрокам
- играть в PvP
- играть в PvE

Это одна и та же игра, но именно разность людей определяет поведение.
Игрок — не зритель игрового мира. Игрок — автор видеоигровой среды.
21🔥2