Хорошей DB должно быть монго
Много-много лет назад, великий гений, коим я, несомненно, являюсь, осознавал неуместность использования реляционных баз данных в веб-программировании и регулярно травил пхп-программистов, которые любили писать, например, логи в базы.
И действительно, даже при разработке «типа CMS» для того, чтобы по адресу /about выводился какой-нибудь текст, в 99% случаев достаточно сделать файл about.txt и пихать все туда, если надо запихать больше одного значения («текст и заголовок») — то serialize и вперед (нет, не xml и прочее гавно).
Да и вообще, CMS никому не нужны.
У меня был движок блога, «написанный на файлах» и была даже специальная кнопочка, на которой значилось «no sql». У Болка движок блога, кстати, до сих пор на файлах работает, а ведь уже 21 век на дворе.
С тех пор прошло много времени, остальное отсталое человечество дозрело и движение nosql действительно завелось и стало трендом, похуже mysql.
Я же совершенно случайно и безотносительно ко всяким трендам попробовал mongodb и полюбил.
Пользоваться mongodb надо не из-за производительности, масштабируемости, nosql (забудьте все, что я говорил выше), а только хотя бы потому, что после ее использования внутри остается теплое приятное чувство, что Сделал Всё Правильно.
В mongo можно пихать «документы», при этом документ — это массив. Поднимите руки, что любит массивы так же, как люблю их я? Ага, молодцы, возьмите с полки пирожки.
Что самое смешное — на предыдущем проекте я написал простенькую «оболочку» для mysql, которая позволяет работать с «документом», как с «массивом» (ну, чисто формально оно и сейчас позволяет, после запроса возвращается же массив? А теперь попробуйте изменить в нем одно значение и запихать его обратно, ага).
Документы не обязаны иметь строгую структуру, это называется «schema-less». Не, ну я любил заниматься анальными извращениями и решать, где для столбика в mysql хватит tinyint, а где и вовсе bit(4), но всему есть предел, к тому же после второго раза это уже не так интересно.
Тем те менее, несмотря на то, что нет «обязательных» полей, строить по ним индексы мы все равно можем. А потом искать по ним (впрочем, искать можно и вовсе без индексов, причем иногда более оптимально, чем с ними — когда требуется перебор всей таблицы, например).
Таким образом, «коллекция» в mongo представляет собой просто набор массивов, куда можно свободно писать, свободно модифицировать и свободно делать любые выборки по любому количеству полей, не хуже, чем в mysql.
Кстати, запросы для выборок — тоже массивы. Очень удобно генерировать их автоматически, не надо подставлять «SELECT .... FROM» в нужных местах, просто создал массив — и вперед. То есть, если документ-массив целиком же кинуть в выборку в качестве запроса, то он найдет и вернет самого себя (что логично), если часть документа, напимер, массив user => acerbial, то оно вернет все документы, где user => acerbial.
«Но без join-ов».
В этом — прелесть номер два. Так как пихать можно любые массивы (это называется «Document Store»), половина join-ов отпадает естественным образом.
Например, заметка и комментарии к ней — это один документ, а не 1+N запись в базе данных (где N — количество комментариев).
Учитывая, что максимальный размер документа — 4 мегабайта, и ты не обязан работать с ним целиком, не только нет причин не хранить комментарии отдельно, но это является единственным логичным и правильным способом.
Сразу решается «проблема» удаления текста и удаления комментариев к нему.
Очень просто решаются задачи, типа «закладки пользователя» — они принадлежат, натурально, пользователю.
Ну давайте уже признаем, что в вебе хранятся и выводятся документы — сразу станет легче жить.
Многие мелочи заботливо сделаны «для веба», да и просто — заботливо сделаны. У каждого документа есть автоматически создаваемый уникальный id («аналог» int autoincrement в mysql), о котором не надо заботиться — он просто есть и работает. Более того, когда ты запишешь новый документ в коллекцию, mongo вернет этот id сам, mysql же придется об этом просить отдельно.
Есть capped collection — коллекция, которая обрезает себя сама («хранить 100 последних документов»), идеально для ведения логоподобной ерунды. Есть upsert — «если документ не существует, то создать», это позволяет писать один и тот же код для создания и редактирования. (Что тоже меня всегда бесило в MYSQL — там update и insert это две разные команды).
Можно не только указать, какие поля возвращать («как в mysql»), но и обратное — указать, какие не возвращать.
В результате всех этих мелочей код у меня выходит раза в два меньше (и раза в два медленней, смакую удовольствие) и пока что нет никакой необходимости создавать обертки вокруг mongodb, все стандартные классы делают ровно то, что нужно.
Инъекций, как легко догадаться, тоже не существует в принципе, как можно сделать инъекцию в массив?
Mongo просто очень приятный и покрывает все потребности «домашних веб-движков блогов» лучше, чем это делает mysql.
Скорее всего, он более подходит и для «серьезных, масштабируемых проектов», но в эти дебри мне углубляться не хочется, потому что там все сводится к аргументу «99.99% стартапов никогда не умрут от излишней посещаемости, поэтому не выебывайтесь, и делайте на mysql+php».
Проблема в том, что на mysql просто физически неприятно после того, как попробовал mongo.
Главный минус — Монго пока что мало где стоит, и уж явно не стоит на хостингах за 5 баксов.
Собственно, поэтому и я агитирую — ставьте, пробуйте, требуйте в магазинах города. Так победим.
Остальные «минусы» Монго вытекают из плюсов — ну, знаете, как с девушками: «страшная, но ебливая», и являются не минусами, а «архитектурными решениями».
Например, по умолчанию Монго пишет на диск когда захочет (Mongo writes when it pleases, ага). За счет этого достигается феноменальная скорость работы (проще просто сказать «ага, записал» на очередной запрос, а записать как-нибудь потом) и феноменальное умение проебывать данные за последнюю минуту и портить всю базу, если отрубилось питание (поправимо с помощью --repair, но осадочек остается).
С одной стороны, это все поправимо, никто не запрещает делать запросы с опцией принудительной записи, с другой — в этом и прелесть, за супермегапроизводительность надо чем-то платить.
(Рекомендуемая книга — MongoDB: The Definitive Guide, хотя для начала мануала на сайте хватает «за глаза»).
Civ5.resources
Меня тут просили разобрать Civ5 «по ресурсам», но конкретно с ней надо делать по-другому, повторюсь: нарисовать табличку по каждой фиче, вида «было — стало — почему».
Мне «Цивилизация» никогда не нравилась с точки зрения ресурсов (потому что там одновременно все примитивно и запутанно), как место для исследований (exploration) и как игра-медитация — да, нравилось.
С ресурсами же все просто: есть время и пространство, как два архетипических ресурса, вместе они образуют вселенную.
Ходы — это время, все остальные ресурсы легко пересчитываются на ходы, их прирост — это функция от времени. Пространство — это механики, начиная с «инвентарного тетриса» в городах, где тебе надо выбрать оптимальные клетки с ресурсами и заканчивая картой.
(«Инвентарным тетрисом» я называю механику, подобную инвентарю в LoL, где у тебя всего 6 клеток и в них надо уместить оптимальные предметы. На больших инвентарях механика не работает, а все остальные инвентарные механики (типа веса предметов) ужасные и поэтому ненужны).
В Civ5 всего-то стали лучше использовать ресурс «пространство» на карте, сделав юнитам объем и непроходимость.
Остальные базовые ресурсы (технология, производство, деньги, культура) являются производным от времени, конкурируют друг с другом (ты увеличиваешь производство, но у уменьшаешь технологию) и обеспечивают разные способы победы. Есть еще второстепенные ресурсы, которые ограничены пространством карты и которые хорошо бы иметь (это, собственно, то, что называется «ресурсами» в самой игре типа лошадей или нефти).
Проблема была в том, что в ресурсы (было) напихано много ограничителей, которые пользователь обязан все знать и помнить, чтобы эффективно играть.
Ну, например, деньги.
Они ограничивали размеры империи и ее эффективность через налоги на содержание (войск), и если ты играл «неправильно», деньги уходили в минус. Это первая механика, не самая приятная и понятная.
Вторая механика — это возможность за деньги купить время (то есть поторопить производство), которая появлялась неизвестно когда (что было огромным минусом) и не имела никакого отношения к предыдущей механике.
В Civ5 из денег сделали более «правильный» ресурс: то есть, натурально, деньги.
Механику трат сделали официальной, и теперь торопить производство можно с первых ходов, подняли общий уровень количества денег. Если предполагается, что деньги теперь тратятся на покупку часто, то их и должно быть больше. Так как их больше, в случае неэффективной игры они не уходят в минус, как раньше, а просто покупки делаются реже. Точка равновесия денег не находится больше на нуле. Если вы играете эффективно, то у вам дают +1000 денег в ход, если не эффективно — то «всего» +500.
Деньги всегда прибывают. Это и понятней, и приятней психологически.
Ну и вообще, ограничивать игрока надо по-другому: например, войска можно ограничивать просто тем фактом, что они они теперь имеют объем, а не брать за содержание одного солдатика 1 монетку.
Первое великое достижение Civ5: они определились с главными ресурсами и сделали красивую панель наверху:
Теперь игроку понятно, что в игре есть 5 главных ресурсов и можно одним взглядом оценить эффективность их добычи. Разобрать эти ресурсы вы можете теперь сами. Можете же?
Исследования, деньги и культура — это валюты. Механики у них чисто валютные: заработал — потратил.
Механика happines теперь стала глобальной и распространяется на всю империю, что правильно, индивидуально нянчится с каждым городом не нужно. Это даже не ресурс, а, скорее, индикатор.
Эта механика меряет общую эффективность, у нее есть плюс (happines) и минус (unhappines). В эту механику можно складывать все бонусы и штрафы, вплоть до того, что убрать расходы из денег вообще, а штрафовать все «несчастьем».
Механика «Golden Age» как и happines связана с эффективностью, но не меряет ее, а просто поощряет регулярными бонусами, регулярность которых и зависит от эффективности.
Вот и разобрали.
Итого: в Civ5 есть три валюты, измеритель эффективности, время (ходы) и пространство (пространство вокруг городов + правило «один солдат на одной клетке» + ресурсы на карте).
Все остальные мини-ресурсы — производные, участвуют в мини-играх и самодостаточны в них. Ну, например, количество жителей — это функция от времени, прирост его можно увеличить с помощью механики «инвентарный тетрис» в городе, после чего это количество жителей используется все в той же механике «инвентарный тетрис».
При таком подходе в игру можно запихать много других мини-игр — ну, например, какой-нибудь «шпионаж» — без заметного ущерба для «баланса».
Это второе великое достижение Civ5 — осознание того факта, что микроменеджемент должен быть опциональным: мы определяем, что в игре главное, после чего принимаем решение «результат мини-игры не может влиять „на главное“ больше, чем на 5%» и пихаем все в мини-игры.
(Попробуйте сыграть в Civ5 сначала с микроменеджементом городов, а потом без него и сравните).
Ну то есть все «обкажуаливание» Civ5 состоит в том, что там улучшили интерфейс, но сделали недостаточное количество мини-игр для задротов. Когда эти мини-игры добавят в аддонах — все будет хорошо.
Ресурсный подход
Давно хотел описать простую методику «для геймдизайнеров», но быстро понял, что это будет, как «рисуем круги — а потом рисуем остальную ебаную сову».
Игра — это конфликт, лучший конфликт можно устроить на пустом месте просто с помощью борьбы за ресурсы, причем не обязательно против другого игрока, достаточно компьютера.
Ресурсом, помимо золота-брильянтов, может быть любая сущность, которой надо управлять, грубо говоря — «которой не хватает», от банального здоровья, маны, места в инвентаре до кулдаунов, размера войск, патронов, изношенности предметов, ходов, времени, и так далее.
(Да, «у нас в игре 100 видов оружия» — это не ресурс, а маркетинговый питч. Оружие при этом может быть «транспортом» или интерфейсом для других ресурсов: износ, специализация, скорость атаки, характеристики персонажа и прочее.
Ну, например, предметы в LoL-е являются «переносчиком» характеристик, плюс завязаны на ресурс «деньги» и ресурс «место в инвентаре». Собственно, они даже традиционными предметами не являются, если обозвать их как-то по-другому, ничего не изменится).
Ресурсы бывают двух типов: «игровые» и «системные».
Игровые — это то, чем люди, собственно, играются, системные — то, без чего игра развалится. Например, «глобальный кулдаун» в WoW: после применения каждого спелла («заклинания», если по-русски) вы не можете применять любой другой в течение ~1 секунды. Это — вещь сугубо системная, которая не дает применять спеллы один за другим с нулевой задержкой.
Так как глобальный кулдаун довольно короткий, он несет в основном чисто техническую роль (борьба с лагами, например), к тому же спеллов есть свои, уже «игровые», ограничения, включая собственные кулдауны и время применения.
Опять-таки, на примере глобального кулдауна видно, что любой системный ресурс рано или поздно переползает в игровые, где пугает игроков своей инородностью: появляются способности, типа «снижает глобальный кулдаун с 1.5 до 1 секунды», некоторые спеллы, которые подразумевают применение сразу после них других спеллов (что-то типа «после применения X, ваш следующий спелл получает Y»), появляется «отвязка» от глобального кулдауна, исключения, и прочее.
Я верю в то, что системные ресурсы надо прятать как можно глубже и не давать им лезть в игровую систему до последнего. В конечном итоге способности, типа «снижают глобальный кулдаун на 0.5 секунд» дают просто прирост повреждений за единицу времени, так что игроку лучше дать тот же самый прирост как-то по-другому, более понятным способом.
Первый шаг — выписывание на бумажке всех ресурсов.
Самое сложное поначалу — понять, что является ресурсом. Я вообще некоторые концепты начинал просто с перечисления ресурсов (и ими же заканчивал, хаха). Например, мой концепт космического симулятора состоит из одних ресурсов и связанных с ними механик, и симулятор отличается от тысячи других именно этим, а не графикой кораблей.
Я хотел уйти от «дрочки на таблицы» и сделать единицу ресурсов, натурально, единицей (т.е. для постройки корабля нужна 1 штука железа и 1 штука урана, а не 1000000), при этом для добычи каждой единицы надо попотеть, делая какие-то смысловые усилия, соотвественно, все ресурсы помнишь наизусть. Механики, типа «постройка этого увеличивает добычу этого на 0.01%» теперь просто не имеют смысла.
А потом вышли седьмые «Сеттлеры» именно с таким же «поштучным» подходом с ресурсам, и сразу стало можно приводить их в пример.
Второй шаг — комбинации ресурс+механика.
После того, как ресурсы выписаны, каждому надо написать список механик, которые работают с этим ресурсом, начиная с атомарных и примитивных.
Для тех, кто не владеет интуицией или памятью, можно вообще составить таблицу, в которой по горизонтали — ресурсы, а по вертикали — известные человечеству механики и поискать смешные пересечения.
Ну, например, ресурс — «жизнь».
Механика «убавление» — это повреждения, «прибавление» — это лечение, «обмен» — это, например, оплата заклинаний здоровьем, а не маной.
Пока все просто, возьмем ресурс «кулдауны».
Механика «убавление» — это, например, предметы в LoL-е, типа «снижает кулдауны на 10%», или способности в WoW-е, типа сброса кулдаунов. «Прибавление» — хороший вариант для дебаффа, типа «вы получили лопатой по голове, поэтому кулдауны на ваши способности увеличены на 20%». «Обмен» — это shared cooldown, когда применение одной способности запускает кулдаун аналогичной/противоположной.
Механики не обязательно писать сразу все, их можно выводить в процессе: очевидно, что у жизни есть «убавление». Значит, можно попробовать поприлеплять «убавление» к другим ресурсам и посмотреть, подходит ли, и что получилось.
Хорошо, если таблица получится компактной и «плотной»: если у нас есть 10 ресурсов и 10 механик, и у каждого ресурса есть пересечение с каждой механикой, то пользователю надо запомнить всего 10+10=20 сущностей, которые дают в результате 10*10=100 комбинаций.
Если же у нас 50 ресурсов и у каждого всего по две механики, то в результате запоминать надо уже 50+2=52 сущности, но дают они все те же 50*2=100 комбинаций.
Иными словами, лучше всего вводить не новые сущности, а как можно больше их комбинаций.
Третий шаг — поиск «неуникальностей».
Ищем комбинации ресурс+механика, которые делают то же самое, что другие комбинации. Неудачные/непонятные комбинации просто удаляем.
Самая распространенная ошибка — переизбыток одинаковых ресурсов, которые делают одно и просто создают иллюзию «стратежности». На эту иллюзию даже ведутся некоторые «любители стратегий», что удивительно.
Менее наглядная ошибка — когда для поставленных целей используются не те ресурсы.
Например, механика haste, которую в WoWе прикрутили к неправильному ресурсу и долго мучились.
Haste «позволяет делать больше действий за тот же период времени». По-русски — «ускорение», ага.
Сначала haste увеличивала скорость автоатаки. Потом внезапно оказалось, что автоатака есть не у всех классов, haste стала ускорять время применения спеллов. Потом оказалось, что есть спеллы с нулевым временем применения, то есть мгновенные, но с отложенным действием, типа «цель получает 10 повреждений каждые 2 секунды в течение 20 секунд». Haste стала стараться снижать эти 2 секунды. Остались просто мгновенные спеллы, типа «цель получает 100 повреждений мгновенно», на них haste не влияла никак. Следующий щаг — прикручивать ее и к global cooldown-у. Ну и так далее.
Проблемы полезли отовсюду: например, есть «вредные» спеллы, сбалансированные длительностью применения. Скажем, fear применяется 1.5 секунды, за которые чисто теоретически враг успевает среагировать и принять меры.
Haste ускоряла это время, скажем, до 0.75 секунд. В результате «чисто математически» за 10 минут можно применить в 2 раза больше fear-ов, «чего и добивались», но значение-то имеет время применения одного fear-а, на которое теперь нельзя было среагировать, потому что 0.75 секунд — это мало.
Спустя пять лет геймдизайнеры WoW-а, наконец-то, осознали, что haste просто позволяет делать больше действий за тот же период времени и начали имитировать желаемый конечный эффект более правильными способами.
У меня (в те далекие времена, когда я еще разрабатывал ММО) было два варианта, как сделать правильный haste. Первый вариант — это увеличивать регенерацию «маны» в зависимости от haste. Логика простая: больше маны за период времени — больше вещей за период времени можно сделать. Так это ж натуральный haste!
Близзард в результате сделали примерно так же.
Второй вариант уже описан выше. Это — правильно! — снижение кулдаунов. Чем короче кулдаун, тем чаще спелл можно применить. Так это ж натуральный haste!
Примерно так «дубликаты» и «отлавливаются»: они в конечном итоге делают одно и то же, пусть даже и выглядят по-разному.
Четвертый шаг — поиск «тупиков».
Тупики — это комбинации ресурс/механика, которые «висят» в воздухе и не поддерживаются сопутствующими механиками. В особо запущенных случаях без опоры могут висеть просто ресурсы или просто механики, часто это выглядит, как «мы хотели сделать, но не успели».
Например, игра Stalker, в которой есть поломка предметов, но нет починки, есть ночные миссии, но нет ускорения времени.
В WoWе таким ресурсом оказалась скорость оружия.
Все proc-и (случайные события) зависели от скорости оружия и имели вид, типа «когда вы наносите повреждения оружием, вы имеете 5% шанс нанести 1000 дополнительных повреждений».
В качестве абсурдного примера — в результате побеждало оружие, имеющее 0 собственных повреждений и бесконечную скорость.
С другой стороны, многие специальные удары имели вид, типа «вы наносите 200% повреждений оружия», поэтому медленное оружие имело преимущество.
(Медленное оружие бьет редко, но сильно, в результате средняя скорость не такая уж и большая, но повреждения от одного удара — большие, способностям же, типа «наносите N% повреждения оружия» была не важна скорость).
Таким образом, скорость оружия вступала в противоречие с главной характеристикой — повреждениями от оружия, часто было так, что более слабое оружие было выгодней использовать из-за его скорости.
Это было контринтуитивно по многим причинам, например, из-за невозможности без калькулятора вычислить это преимущество или из-за того, что с ростом уровня у оружия повышаются именно повреждения, но не скорость, и часто были ситуации, когда оружие низкого уровня оказывалось «круче» оружия более высокого уровня — из-за скорости.
И ту и другую проблему починили — proc-и теперь имеют определенный шанс срабатывания в минуту, а способности используют «нормализированное» повреждения от оружия, то есть не силу одного удара, а «силу среднего удара во времени».
Изменились и другие механики, завязанные на скорость оружия (heroic stike, pushback и прочее).
В результате скорость просто перестала иметь значение, с некоторыми небольшими оговорками. Недавно один из геймдизайнеров ВоВа таки сознался, что можно было бы обойтись и без скорости оружия.
Итого:
1. Выписываем ресурсы.
1.5. Помечаем системные, и не даем им в будущем стать игровыми.
2. Выписываем механики.
3. Ищем избыточные сочетания, удаляем.
4. Ищем недостаточно убедительные сочетания, удаляем.
5. Добавляем новое, если вы знаете, что делаете.
Пятый пункт — самый опасный, потому что после него надо все переделывать.
Вообще же, данная «методика» нужна не только для того, чтобы ничего не пропустить, но еще и для того, чтобы не пропустить лишнее, в идеале в конце работы должно получиться меньше «материала», чем в начале.
Похожая, но в разы более простая методика избавления от всяких legacy систем, называется «зачем это?».
Надо пройтись по каждому ресурсу — ну или «фиче» — и спрашивать себя «зачем это?».
Аргументы, типа «так все делают» или «поэтому что это есть в...» не проходят, потому что обычно там прячутся фичи, которые были в проекте X, потому что были в проекте V, куда они попали, потому что были в проекте T и S, оттуда — из проекта R, и так до самого конца, а в проекте A эту фичу впервые написал программист на коленке, оставленный без присмотра менеджером на несколько минут (да в те далекие времена и менеджеров-то и не было).
Именно на отлов таких фич и направлена методика «зачем это?»™.
Даже если ее применять ко всякой привычной ерунде, типа аптечек, жизни, лечению, то можно достичь неожиданных результатов, как, например, убирание здоровья вообще (как в современных шутерах).
Раз уж мы говорим про WoW, в качестве примера выкидывания лишнего можно взять характеристики до 4.0.1 и после и провести простой ретроспективный анализ.
(WoW — как пример упрощения, который ничего не поменял (кроме улучшения восприятия), а не как пример хорошей системы.
Раньше было ~15 характеристик, сейчас стало ~3. Сложность системы от количества характеристик не поменялась, потому что куча характеристик дублировала друг друга (типа сила — сила атаки).