BeamNG.Earth
BeamNG.drive용 1:1 비율 실시간 지구, 실제 고도·위성 이미지·부동 원점·보조 서버에서 LOD 스트리밍
전통 카자흐 게임 asyq atu를 디지털화해 목표 설정·힘 선택·뼈를 링 밖으로 날리는 게임이며, 챌린지·듀얼·일일 도전·대학 리그가 있습니다
A digital take on the traditional Kazakh game asyq atu: aim, pick your power and knock the bones out of the ring. Challenges, duels, a daily challenge and a university league.
게임TypeScript · JavaScriptGitHub @dam1r-dev온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기Asyq League — digital asyq atu (Kazakh traditional game) for students & university tournaments. Narxoz Incubator 2026.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Asyq League — асық ату в браузере Цифровая версия казахской игры на меткость асық ату. Прицелься, выбери силу броска и выбей асыки из кона сақой. Есть обучение, 28 испытаний с нарастающей сложностью и историей игры, дуэль вдвоём на одном телефоне, испытание дня, лига университетов, редактор расстановок со ссылкой для друга и магазин косметики за игровые тиыны (без доната).README 전문 보기 ↗
Цифровая версия казахской игры на меткость асық ату. Прицелься, выбери силу броска и выбей асыки из кона сақой. Есть обучение, 28 испытаний с нарастающей сложностью и историей игры, дуэль вдвоём на одном телефоне, испытание дня, лига университетов, редактор расстановок со ссылкой для друга и магазин косметики за игровые тиыны (без доната).
demo, пароль asyq2026 (на странице входа кнопка «Войти как demo» создаёт отдельный временный аккаунт с 1000 🪙 для каждого проверяющего; общий demo при каждом входе пополняется до 1000 🪙)Работа для технического этапа Narxoz Incubator 2026 (задание «Асық ату»).
Асық ату знакомо родителям по летним дворам, но многие студенты уже не играли в него сами. Asyq League — это игра для студентов и школьников Казахстана, в которую можно сыграть за минуту с телефона, а потом бороться за свой университет в лиге вузов.
Идея продукта: не просто перенести асыки на экран, а дать повод возвращаться каждый день.
| Событие | Очки |
|---|---|
| Асык полностью за меловой линией кона | +1 |
| Выбил несколько за один бросок — за каждый сверх первого ✦ | +1 |
| Сақа остановилась внутри кона (как «айып» во дворе) | −1 |
| Кон очищен — за каждый неиспользованный бросок ✦ | +2 |
Выбитый асык сразу удаляется из состояния раунда, поэтому очки за него невозможно получить дважды. Счёт не опускается ниже нуля.
Отмечены ✦ выше и значком «авторское» в самом приложении:
PUDDLE_DRAG): асык и сақа быстро теряют скорость.
Сильный бросок пролетает воду, слабый застревает, а асык у края кона в луже не выкатывается за линию. Лужа детерминирована
(как и вся физика), поэтому сервер пересчитывает броски с лужами точно так же. Прицельный пунктир учитывает лужу: он
обрывается там, где сақа остановится в воде. Лужи можно ставить в редакторе
(до 4 штук), они сохраняются в ссылках-испытаниях и работают в дуэлях со своим полем./history): что такое асық и четыре его стороны, лента времени от древности до списка ЮНЕСКО,
сақа, как играли во дворе, современность (ЮНЕСКО 2017, Всемирные игры кочевников), карточки «Знаешь ли ты?», символы
уровней и список источников. Факты сверены по открытым источникам, ссылки приведены на странице./rules): схема броска, пять карточек с картинками «за что даются очки», пошаговый
пример подсчёта, объяснение звёзд, отличие очков от звёзд и тиынов, частые вопросы и советы новичку.Мультиплеер без аккаунтов: «Дуэль → Онлайн по ссылке» → «Создать матч» → отправить ссылку /m/КОД другу.
Друг вводит имя, и матч начинается. Каждый играет на своём устройстве, а каждый бросок выглядит у обоих одинаково:
у соперника бросок проигрывается с анимацией.
Как это устроено:
sx, vx, vy). Физика детерминированная, поэтому каждый клиент сам получает
одно и то же поле, очки и очередь, повторяя те же броски через duelThrow.Match, сам проигрывает их и проверяет: чей сейчас ход, честен ли бросок,
не устарел ли запрос (защита от гонок через version). Подделать бросок за соперника или сходить не в свою очередь нельзя.localStorage), поэтому можно закрыть вкладку и вернуться.scripts/online-e2e.mjs: два независимых браузера играют матч, счёт совпадает после каждого броска.decodeChallenge + validLayout).Match.mode, Match.perTurn, Match.rounds), поэтому соперник видит их ещё до входа,
а сервер проверяет каждый бросок по полю текущего раунда.src/game/duel.test.ts и сквозным scripts/duel-modes-e2e.mjs (два браузера: свои раунды, 2 броска за ход,
порядок H H G G H H G G H G …, второй раунд начинает гость).mustache: true/false) читаются как раньше; сервер проверяет новые поля через zod, а платные части ещё и по покупкам.Игру можно добавить на экран телефона: в Chrome «Установить приложение», в Safari «На экран Домой». Для этого есть манифест
(src/app/manifest.ts) и иконки 192/512 px; запускается в полноэкранном режиме без адресной строки.
src/i18n/dict/{ru,kk,en}.ts; TypeScript
проверяет, что у всех трёх языков одинаковый набор ключей. Формы множественного числа учитываются
(«1 бросок / 3 броска / 5 бросков», «1 throw / 5 throws»).Accept-Language).src/game/physics.ts — простой движок для кругов на плоскости:
1/120 с, не зависит от FPS;+ − × ÷ и Math.sqrt. По IEEE 754 они дают бит-в-бит одинаковый результат
в любом браузере и в Node.js. sin/cos/atan2 такой гарантии не дают, поэтому направление броска
задаётся вектором (vx, vy), а не углом.Благодаря детерминированности бросок полностью описывается тремя числами {sx, vx, vy}:
src/lib/verify.ts). Подделать рекорд правкой запроса
или localStorage нельзя.replayRound).Проверено тестами (npm test): одинаковый результат повторного броска, отсутствие двойного счёта,
реплей раунда, валидация ссылок и проходимость каждого уровня и испытаний дня ботом-перебором.
Сложность уровней калибровалась симуляцией «неточного игрока» со случайной ошибкой прицела и силы.
src/game/ чистый TypeScript без React: физика, правила раунда, уровни, дуэль,
испытание дня, ссылки-вызовы, советы, рендер canvas
src/components/ GameCanvas (ввод + анимация), PlayScreen, ResultModal, провайдер аккаунта
src/app/ страницы (App Router) и API-маршруты
src/lib/ Prisma, сессии, проверка попыток, рейтинги, каталог магазина, localStorage, звук
prisma/ схема и миграции
scripts/ применение миграций на Turso, e2e-проверка в браузере
User (с персонажем), Attempt (все сыгранные раунды с бросками), Purchase, CoinTx (журнал тиынов).jose) в httpOnly-cookie.src/avatar/Avatar.tsx). Сначала выбирается пол: у девочки
сразу косички, юбка и ресницы, у мальчика короткая стрижка и шорты (дальше всё можно поменять). Бесплатно настраиваются кожа, глаза,
причёска, цвет волос и усы: внешность человека не продаём. Костюмы кочевников продаются за тиыны:
шапан, камзол, қыз көйлегі, тон, батыр сауыты, а также три вида тақии (ою, киіз, зерлі), бөрік, тымақ, сәукеле с үкі, дулыға и цвета одежды.
Всё можно примерить до покупки. Персонаж виден в профиле, в шапке и в рейтинге.CoinTx с уникальным ключом, поэтому одну и ту же награду нельзя получить дважды:
повторное прохождение уровня на те же звёзды ничего не даёт, повторный запрос награды тоже.За игру несколько дней подряд даётся бонус тиынов: +5 🪙 за каждый день серии, максимум +35 🪙 в день (первый день серии бонуса
не даёт). Серия считается по попыткам, сохранённым на сервере (день по Алматы), и не рвётся, пока не закончился сегодняшний день.
Бонус выдаётся один раз в день (refKey = streak:<user>:<день>), поэтому повторные попытки его не накручивают. Серия видна
в профиле, а в окне результата показывается строка «🔥 Серия N дней: +X 🪙». Логика подсчёта вынесена в чистую функцию
computeStreak и покрыта тестами.
21 достижение с наградой в тиынах (от +10 до +150): первая победа, пороги звёзд (10 / 30 / 60 / все 84), прохождение глав, три звезды в 5
и 15 испытаниях, победа за один бросок, прохождение уровня с лужей, 100 и 500 выбитых асыков, серии 3 и 7 дней, испытание дня в 3 и
10 разных дней, первая покупка, пять уровней без пунктира, победа в онлайн-дуэли. Условия считаются на сервере по уже хранимым данным (попытки, покупки,
серия, дуэли), отдельной таблицы нет: факт получения записывается в журнал тиынов с уникальным refKey = ach:<user>:<id>, поэтому
награда выдаётся ровно один раз, даже при гонке запросов, а у старых игроков достижения засчитываются задним числом.
Новые достижения проверяются после попытки, покупки и конца дуэли и показываются в окне результата; полный список с прогрессом
есть в профиле (GET /api/achievements). Список и пороги покрыты тестами (src/lib/achievements.test.ts).
В окне результата и в профиле есть кнопка «Картинка для шаринга»: в браузере рисуется PNG 1080×1350 (формат 4:5 для Telegram и Instagram) с расстановкой испытания (с лужами и камнями), звёздами, очками и ссылкой на игру. На телефоне открывается системное окно «Поделиться» с файлом, на компьютере картинка скачивается. Рисование идёт в canvas тем же шрифтом, что и на сайте, поэтому кириллица и казахские буквы отображаются правильно, а сервер для этого не нужен.
Кнопка «Прицел» в панели управления переключает три уровня помощи (настройка хранится в браузере и не влияет на очки и звёзды, которые сервер всё равно пересчитывает по броскам): лёгкий — пунктир до первого удара (с учётом луж), средний — только короткий хвост из пяти штрихов у сақа, сложный — без пунктира (остаётся резинка и процент силы).
Бонус за сложность: за выигранный уровень на среднем прицеле +5 🪙, на сложном +10 🪙 (сложный засчитывает и средний, итого
не больше 15 🪙 за уровень). Каждая ступень на каждом уровне оплачивается один раз (refKey = aim:<user>:<уровень>:<ступень>).
Сложность называет клиент и сервер проверить её не может, поэтому награда небольшая и ограничена числом уровней; очки, звёзды и рейтинг
от сложности не зависят. Достижение «Орлиный глаз» даётся за пять уровней на сложном прицеле.
В дуэли на одном устройстве есть кнопки «Закончить» (побеждает тот, у кого больше очков, при равенстве ничья) и «Сдаться» (проигрывает тот, чей сейчас ход). Статус хода и номер раунда показываются в полосе над полем, а не поверх него.
В игре нет реальных денег и платных пакетов: тиыны зарабатываются только игрой — звёзды в испытаниях, участие и места в турнире дня, приветственный бонус. На них покупаются только косметика (костюмы, головные уборы, цвета, скины сақа, площадки) и набор «Дала аңыздары». Физика одинакова для всех скинов, а ничего из магазина не влияет на очки и рейтинг. Премиальный костюм «Алтын адам» требует ещё и 56 звёзд из 84 возможных в испытаниях: купить его только накоплением тиынов нельзя. Реального платёжного провайдера, пополнения и тестовой карты в проекте нет.
Проект проверен на базовые классы веб-атак (OWASP / PortSwigger Academy):
| Класс атаки | Как закрыто |
|---|---|
| Path traversal | Нет маршрутов, читающих файлы по пути от пользователя; единственный динамический параметр (код матча) проверяется регулярным выражением |
| Незащищённая админка / роль из запроса | Админки и ролей нет. Личность берётся только из подписанного JWT (HS256, httpOnly, sameSite=lax, secure), а не из параметров запроса |
| IDOR | Ни один API не принимает userId из запроса — всё выбирается по сессии. Матчи защищены секретом места (18 случайных байт), в ответах нет токенов и хэшей паролей; в таблице лидеров чужие id не отдаются |
| Перебор логинов | Вход выполняет bcrypt.compare всегда (даже для несуществующего логина) и отвечает одинаково: время и текст не выдают, есть ли такой пользователь |
| Перебор паролей / спам | Rate limit в базе (по IP и по логину) на вход, регистрацию, demo, пополнение, покупки и матчи; пароль от 8 символов, частые пароли запрещены |
| 2FA, SSRF, загрузка файлов, OS-инъекции | Не применимо: этих функций нет, сервер не делает запросов на адреса от пользователя, не запускает команды и не принимает файлы |
| SQL-инъекции | Только Prisma с параметризованными запросами, сырого SQL нет; весь ввод проходит валидацию zod |
| CSRF | sameSite=lax + проверка Origin/Sec-Fetch-Site для всех изменяющих запросов (src/proxy.ts) |
| Кликджекинг, XSS, утечки | CSP, X-Frame-Options: DENY, nosniff, HSTS, Referrer-Policy, Permissions-Policy; React экранирует весь пользовательский текст |
| Кража токена | Выход из аккаунта отзывает все выданные токены (sessionVersion) |
| Гонки в экономике | Покупка проверяет баланс и списывает его в одной транзакции; награды защищены уникальным refKey, дозачисление demo идемпотентно |
| Нагрузка на CPU (DoS) | Каждая попытка/бросок перепроигрывается на сервере, поэтому у attempts, sync и match/throw свои лимиты |
| Кэширование чужих данных | Все ответы /api/* идут с Cache-Control: private, no-store |
| Бесконечные тиыны | Пополнения за деньги нет: тиыны выдаёт только сервер за игру, награды защищены уникальным |
Сообщить об уязвимости: GitHub Security Advisories (см. также /.well-known/security.txt).
Общий логин demo / asyq2026 публичен намеренно, в нём нет личных данных.
npm install # также выполнит prisma generate
cp .env.example .env # DATABASE_URL="file:./dev.db", AUTH_SECRET=любая длинная строка
npx prisma migrate dev # создать локальную SQLite-базу
npm run dev # http://localhost:3000
npm test # тесты физики, правил и баланса уровней
libsql://…) и токен.DATABASE_URL, TURSO_AUTH_TOKEN, AUTH_SECRET.npm run build сначала запускает scripts/db-apply.mjs: он накатывает миграции на Turso
(prisma migrate deploy не умеет работать с libsql://) и создаёт тестовый аккаунт demo.next/font)@prisma/adapter-libsql, Turso (libSQL)jose (JWT), bcryptjs (пароли), zod (валидация)create-next-app. Готовых игровых шаблонов и физических движков не используется:
физика, рендер, уровни и звук написаны с нуля для этого проекта.refKey| Cookie и секреты | __Host- префикс, срок сессии 7 дней, сравнение токенов матча за постоянное время, предупреждение в логах, если AUTH_SECRET короче 32 символов |
| Журналы | Структурные события (auth_fail, rate_limited, server_error) без паролей и токенов; клиент видит только код ошибки |
| Зависимости | Dependabot и GitHub Actions (npm audit --omit=dev --audit-level=critical, CodeQL). npm audit без флага показывает dev-зависимость prisma CLI (mysql2/deepmerge-ts): в рантайме она не используется |