@needle-tools/engine
Version:
Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in.
317 lines (309 loc) • 10.8 kB
JavaScript
import { ContextRegistry } from "../engine_context_registry.js";
import { isLocalNetwork } from "../engine_networking_utils.js";
import { getParam } from "../engine_utils.js";
const debug = getParam("debugdebug");
let hide = false;
if (getParam("noerrors") || getParam("nooverlaymessages"))
hide = true;
const globalErrorContainerKey = "needle_engine_global_error_container";
export var LogType;
(function (LogType) {
LogType[LogType["Log"] = 0] = "Log";
LogType[LogType["Warn"] = 1] = "Warn";
LogType[LogType["Error"] = 2] = "Error";
})(LogType || (LogType = {}));
export function getErrorCount() {
return _errorCount;
}
const _errorListeners = new Array();
/** Register callback when a new error happens */
export function onError(cb) { _errorListeners.push(cb); }
/** Unregister error callback */
export function offError(cb) { _errorListeners.splice(_errorListeners.indexOf(cb), 1); }
let isInvokingErrorListeners = false;
function invokeErrorListeners(...args) {
if (isInvokingErrorListeners)
return; // prevent infinite loop
isInvokingErrorListeners = true;
try {
for (let i = 0; i < _errorListeners.length; i++) {
_errorListeners[i](...args);
}
}
catch (e) {
console.error(e);
}
isInvokingErrorListeners = false;
}
const originalConsoleError = console.error;
const patchedConsoleError = function (...args) {
originalConsoleError.apply(console, args);
onParseError(args);
addLog(LogType.Error, args, null, null);
onReceivedError(...args);
};
/** Set false to prevent overlay messages from being shown */
export function setAllowBalloonMessages(allow) {
hide = !allow;
if (allow)
console.error = patchedConsoleError;
else
console.error = originalConsoleError;
}
/**
* @deprecated Use {@link setAllowBalloonMessages} instead
*/
export function setAllowOverlayMessages(allow) {
return setAllowBalloonMessages(allow);
}
export function makeErrorsVisibleForDevelopment() {
if (hide)
return;
if (debug)
console.warn("Patch console", window.location.hostname);
console.error = patchedConsoleError;
window.addEventListener("error", (event) => {
if (!event)
return;
const message = event.error;
if (message === undefined) {
if (isLocalNetwork())
console.warn("Received unknown error", event, event.target);
return;
}
addLog(LogType.Error, message, event.filename, event.lineno);
onReceivedError(event);
}, true);
window.addEventListener("unhandledrejection", (event) => {
if (hide)
return;
if (!event)
return;
if (event.reason)
addLog(LogType.Error, event.reason.message, event.reason.stack);
else
addLog(LogType.Error, "unhandled rejection");
onReceivedError(event);
});
}
let _errorCount = 0;
function onReceivedError(...args) {
_errorCount += 1;
invokeErrorListeners(...args);
}
function onParseError(args) {
if (Array.isArray(args)) {
for (let i = 0; i < args.length; i++) {
const arg = args[i];
if (typeof arg === "string" && arg.startsWith("THREE.PropertyBinding: Trying to update node for track:")) {
args[i] = "Some animated objects couldn't be found: see console for details";
}
}
}
}
export function addLog(type, message, _file, _line) {
if (hide)
return;
const context = ContextRegistry.Current;
const domElement = context?.domElement ?? document.querySelector("needle-engine");
if (!domElement)
return;
if (Array.isArray(message)) {
let newMessage = "";
for (let i = 0; i < message.length; i++) {
let msg = message[i];
if (msg instanceof Error) {
msg = msg.message;
}
if (typeof msg === "object")
continue;
if (i > 0)
newMessage += " ";
newMessage += msg;
}
message = newMessage;
}
if (!message || message.length <= 0)
return;
showMessage(type, domElement, message);
}
// function getLocation(err: Error): string {
// const location = err.stack;
// console.log(location);
// if (location) {
// locationRegex.exec(location);
// const match = locationRegex.exec(location);
// return match ? match[1] : "";
// }
// return "";
// }
/**
*
*/
const currentMessages = new Map();
function showMessage(type, element, msg) {
if (msg === null || msg === undefined)
return;
const container = getLogsContainer(element);
if (container.childElementCount >= 20) {
const last = container.lastElementChild;
returnMessageContainer(last);
}
// truncate long messages before they go into the cache/set
if (msg.length > 400)
msg = msg.substring(0, 400) + "...";
const key = msg;
if (currentMessages.has(key)) {
return;
}
const msgcontainer = getMessageContainer(type, msg);
container.prepend(msgcontainer);
const removeFunction = () => {
currentMessages.delete(key);
returnMessageContainer(msgcontainer);
};
currentMessages.set(key, removeFunction);
setTimeout(removeFunction, 10_000);
}
/**
* Clear all overlay messages from the screen
*/
export function clearMessages() {
if (debug)
console.log("Clearing messages");
for (const cur of currentMessages.values()) {
cur?.call(cur);
}
currentMessages.clear();
}
const logsContainerStyles = `
@import url('https://fonts.googleapis.com/css2?family=Roboto+Flex:opsz,wght@8..144,100..1000&display=swap');
div[data-needle_engine_debug_overlay] {
font-family: 'Roboto Flex', sans-serif;
font-weight: 400;
font-size: 16px;
}
div[data-needle_engine_debug_overlay] strong {
font-weight: 700;
}
div[data-needle_engine_debug_overlay] a {
color: white;
text-decoration: none;
border-bottom: 1px solid rgba(255, 255, 255, 0.3);
}
div[data-needle_engine_debug_overlay] a:hover {
text-decoration: none;
border: none;
}
div[data-needle_engine_debug_overlay] .log strong {
color: rgba(200,200,200,.9);
}
div[data-needle_engine_debug_overlay] .warn strong {
color: rgba(255,255,230, 1);
}
div[data-needle_engine_debug_overlay] .error strong {
color: rgba(255,100,120, 1);
}
`;
function getLogsContainer(domElement) {
if (!globalThis[globalErrorContainerKey]) {
globalThis[globalErrorContainerKey] = new Map();
}
const errorsMap = globalThis[globalErrorContainerKey];
if (errorsMap.has(domElement)) {
return errorsMap.get(domElement);
}
else {
const container = document.createElement("div");
errorsMap.set(domElement, container);
container.setAttribute("data-needle_engine_debug_overlay", "");
container.classList.add("debug-container");
container.style.cssText = `
position: absolute;
top: 0;
right: 5px;
padding-top: 0px;
max-width: 70%;
max-height: calc(100% - 5px);
z-index: 9999999999;
pointer-events: scroll;
display: flex;
align-items: end;
flex-direction: column;
color: white;
overflow: auto;
word-break: break-word;
`;
if (domElement.shadowRoot)
domElement.shadowRoot.appendChild(container);
else
domElement.appendChild(container);
const style = document.createElement("style");
style.innerHTML = logsContainerStyles;
container.appendChild(style);
return container;
}
}
const typeSymbol = Symbol("logtype");
const containerCache = new Map();
function returnMessageContainer(container) {
container.remove();
const type = container[typeSymbol];
const containers = containerCache.get(type) ?? [];
containers.push(container);
containerCache.set(type, containers);
}
function getMessageContainer(type, msg) {
if (containerCache.has(type)) {
const containers = containerCache.get(type);
if (containers.length > 0) {
const container = containers.pop();
container.innerHTML = msg;
return container;
}
}
const element = document.createElement("div");
element.setAttribute("data-id", "__needle_engine_debug_overlay");
element.style.marginRight = "5px";
element.style.padding = ".5em";
element.style.backgroundColor = "rgba(0,0,0,.9)";
element.style.marginTop = "5px";
element.style.marginBottom = "3px";
element.style.borderRadius = "8px";
element.style.pointerEvents = "all";
element.style.userSelect = "text";
element.style.maxWidth = "250px";
element.style.whiteSpace = "pre-wrap";
element.style["backdrop-filter"] = "blur(10px)";
element.style["-webkit-backdrop-filter"] = "blur(10px)";
element.style.backgroundColor = "rgba(20,20,20,.8)";
element.style.boxShadow = "inset 0 0 80px rgba(0,0,0,.2), 0 0 5px rgba(0,0,0,.2)";
element.style.border = "1px solid rgba(160,160,160,.2)";
element[typeSymbol] = type;
switch (type) {
case LogType.Log:
element.classList.add("log");
element.style.color = "rgba(200,200,200,.7)";
element.style.backgroundColor = "rgba(40,40,40,.7)";
// element.style.backgroundColor = "rgba(200,200,200,.5)";
break;
case LogType.Warn:
element.classList.add("warn");
element.style.color = "rgb(255, 255, 150)";
element.style.backgroundColor = "rgba(50,50,20,.8)";
// element.style.backgroundColor = "rgba(245,245,50,.5)";
break;
case LogType.Error:
element.classList.add("error");
element.style.color = "rgb(255, 50, 50";
element.style.backgroundColor = "rgba(50,20,20,.8)";
// element.style.backgroundColor = "rgba(255,50,50,.5)";
break;
}
element.title = "Open the browser console (F12) for more information";
// msg = msg.replace(/[\n\r]/g, "<br/>");
// console.log(msg);
element.innerHTML = msg;
return element;
}
//# sourceMappingURL=debug_overlay.js.map