Open-source веб-студия для создания игр Рублокса, двойная лицензия AGPL-3.0 + Коммерческая. Главное: - Vite 5 + React 18 + Babylon 7.54.3 + Monaco Editor + Colyseus 0.16 - Самодостаточный движок ~28к строк (66 файлов): BlockManager, TerrainVoxelBuilder, ModelManager, DecoManager, PlayerController, ScriptSandboxWorker, MultiplayerSync, 30+ GD-гейммодов - Главный редактор KubikonEditor (~37к строк) + панели, ScriptEditor (Monaco) - Витрина игр (KubikonFeed, KubikonStudio, KubikonDocs, KubikonLearn) - Geometry Dash sub-app (GdMenu, GdShop, GdRules, GdCoverArt) - 10 admin-preview каталогов для дизайнеров (скины, музыка, порталы и т.д.) - Конфигурируемый бэкенд через VITE_API_BASE — работает со staging (dev-api.rublox.pro) без настройки - Standalone-режим (VITE_STANDALONE=true) — открыть пустой редактор без бэка - Полная документация (на русском): README, ARCHITECTURE, CONTRIBUTING, SECURITY, CHANGELOG - ESLint + Prettier + EditorConfig - Legal: LICENSE (AGPL-3.0), LICENSE-COMMERCIAL.md, CLA.md, COPYRIGHT.md - Issue templates: bug_report, feature_request, security_disclosure Перед публикацией: - Все импорты из minecraftia заменены на локальные - Все хардкоды URL (minecraftia-school.ru) и внутренних IP убраны → env - Admin-эндпоинты Kubikon3DService вырезаны (остаются в приватном репо) - AdminKubikonModeration не публикуется (модерация — в team.rublox.pro) - 93 МБ ассетов public/kubikon-assets вынесены в .gitignore (раздаются через release artifact)
132 lines
5.6 KiB
JavaScript
132 lines
5.6 KiB
JavaScript
/**
|
||
* Dev-log клиент — шлёт логи на локальный HTTP-сервер на localhost:8765.
|
||
*
|
||
* Полезно когда нужно собирать console.* логи из браузера в файл для
|
||
* последующего просмотра (например при отладке вместе с AI-ассистентом).
|
||
*
|
||
* Активен только на localhost. На любом другом хосте запросы тихо
|
||
* игнорируются (если devlog-сервер не запущен).
|
||
*
|
||
* Использование:
|
||
* import { devlog, attachConsoleHook } from './devlog';
|
||
* devlog('info', 'PerfReport', { fps: 30, triangles: 5000000 });
|
||
* attachConsoleHook(); // перехват console.log/warn/error
|
||
*/
|
||
|
||
const DEVLOG_URL = 'http://localhost:8765/log';
|
||
const RESET_URL = 'http://localhost:8765/reset';
|
||
|
||
// Активируем только в development (localhost)
|
||
const isDev = typeof window !== 'undefined'
|
||
&& (window.location.hostname === 'localhost'
|
||
|| window.location.hostname === '127.0.0.1');
|
||
|
||
let devlogEnabled = isDev;
|
||
let pendingTimer = null;
|
||
const pendingQueue = [];
|
||
|
||
/**
|
||
* Отправить одну запись в devlog. Не блокирует — fire-and-forget.
|
||
* Запросы батчатся (флашатся через 200мс), чтобы не спамить сервер.
|
||
*/
|
||
export function devlog(level, msg, data) {
|
||
if (!devlogEnabled) return;
|
||
pendingQueue.push({ level, msg, data });
|
||
if (!pendingTimer) {
|
||
pendingTimer = setTimeout(flushQueue, 200);
|
||
}
|
||
}
|
||
|
||
function flushQueue() {
|
||
pendingTimer = null;
|
||
if (!devlogEnabled) { pendingQueue.length = 0; return; }
|
||
if (pendingQueue.length === 0) return;
|
||
const batch = pendingQueue.splice(0, pendingQueue.length);
|
||
// ВАЖНО: шлём ОДИН POST на весь батч, а не N штук. Раньше цикл
|
||
// запускал все fetch сразу — а devlogEnabled=false из .catch()
|
||
// выставлялся асинхронно уже после вылета всех 50 запросов, и
|
||
// консоль засорялась десятками ERR_CONNECTION_REFUSED. Теперь —
|
||
// один запрос: упал → один лог в консоли → devlog навсегда выключен.
|
||
try {
|
||
fetch(DEVLOG_URL, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
// Сервер принимает либо одиночную запись, либо массив.
|
||
body: JSON.stringify(batch.length === 1 ? batch[0] : batch),
|
||
// keepalive чтобы запрос не отменился при уходе со страницы
|
||
keepalive: true,
|
||
}).catch(() => {
|
||
// Сервер не запущен — глушим devlog навсегда (один раз).
|
||
devlogEnabled = false;
|
||
pendingQueue.length = 0;
|
||
});
|
||
} catch (e) {
|
||
devlogEnabled = false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Сбросить лог-файл (очистить). Вызывать перед новым тестом.
|
||
*/
|
||
export function devlogReset() {
|
||
if (!devlogEnabled) return;
|
||
try {
|
||
fetch(RESET_URL, { method: 'POST', keepalive: true })
|
||
.catch(() => { devlogEnabled = false; });
|
||
} catch (e) { /* ignore */ }
|
||
}
|
||
|
||
/**
|
||
* Подключить перехват console.log/warn/error/info — все логи будут
|
||
* автоматически попадать в devlog.txt. Безопасно вызывать многократно
|
||
* (повторный вызов — no-op).
|
||
*/
|
||
let consoleHooked = false;
|
||
export function attachConsoleHook() {
|
||
if (!devlogEnabled || consoleHooked) return;
|
||
consoleHooked = true;
|
||
const original = {
|
||
log: console.log,
|
||
warn: console.warn,
|
||
error: console.error,
|
||
info: console.info,
|
||
};
|
||
const formatArg = (arg) => {
|
||
if (arg === null || arg === undefined) return String(arg);
|
||
if (typeof arg === 'string') return arg;
|
||
if (typeof arg === 'number' || typeof arg === 'boolean') return String(arg);
|
||
if (arg instanceof Error) return `${arg.name}: ${arg.message}\n${arg.stack || ''}`;
|
||
try {
|
||
return JSON.stringify(arg, (key, value) => {
|
||
// Защита от циклических ссылок и больших объектов Babylon
|
||
if (typeof value === 'object' && value !== null) {
|
||
if (value.constructor?.name === 'Scene') return '[Babylon.Scene]';
|
||
if (value.constructor?.name === 'Mesh') return `[Mesh ${value.name}]`;
|
||
if (value.constructor?.name === 'Engine') return '[Babylon.Engine]';
|
||
}
|
||
return value;
|
||
}, 0);
|
||
} catch (e) {
|
||
return String(arg);
|
||
}
|
||
};
|
||
const hook = (level, originalFn) => (...args) => {
|
||
try {
|
||
const formatted = args.map(formatArg).join(' ');
|
||
devlog(level, formatted);
|
||
} catch (e) { /* ignore */ }
|
||
try { originalFn.apply(console, args); } catch (e) {}
|
||
};
|
||
console.log = hook('log', original.log);
|
||
console.warn = hook('warn', original.warn);
|
||
console.error = hook('error', original.error);
|
||
console.info = hook('info', original.info);
|
||
}
|
||
|
||
// Глобальные хелперы для ручного дёрганья из консоли DevTools
|
||
if (typeof window !== 'undefined' && isDev) {
|
||
window.__devlog = devlog;
|
||
window.__devlogReset = devlogReset;
|
||
window.__devlogAttach = attachConsoleHook;
|
||
}
|