Game logic, physics hooks and update extensions for blackhack
이 스크립트는 직접 설치하는 용도가 아닙니다. 다른 스크립트에서 메타 지시문 // @require https://update.greasyfork.org/scripts/573606/1802158/blackhack-logic.js을(를) 사용하여 포함하는 라이브러리입니다.
// ==UserScript==
// @name blackhack-logic
// @namespace brofist.io 1st-cheat (FOR ALL MODES)
// @version 1.1
// @description Game logic, physics hooks and update extensions for blackhack
// @author CiNoP
// @license GPL-3.0-only
// ==/UserScript==
(function () {
'use strict';
const BH = window.BH = window.BH || {};
BH.logicVer = 1.1;
// ─── GLOBAL BIND INTERCEPTOR (OCP Fix) ───
const _bind = Function.prototype.bind;
Function.prototype.bind = function (ctx, ...args) {
const b = _bind.apply(this, [ctx, ...args]);
if (!BH.Hooks.allFound) {
try {
const s = this.toString();
let allFound = true;
for (const h of BH.Hooks.registry) {
if (!h.orig && h.match(s)) { h.orig = this; b.__hk_id = h.id; }
if (!h.orig) allFound = false;
}
BH.Hooks.allFound = allFound;
} catch (_) {}
} else {
for (const h of BH.Hooks.registry) {
if (this === h.orig) { b.__hk_id = h.id; break; }
}
}
if (BH.isTwoPlayer && window.hack && !window.hack.mode && ctx?.playerData && ctx?.twoPlayerController) {
Object.assign(window.hack, {
mode: ctx, modeController: ctx.twoPlayerController,
gp: ctx.gp, networkHandler: ctx.networkHandler, client: ctx.client
});
}
return b;
};
// ─── ISOLATED DOMAIN MODULES (SRP & ISP Fixes) ───
BH.Blacklist = {
process: (bl, otherPlayers) => {
for (const n of bl.names) {
for (const i of BH.utils.getIdxByName(otherPlayers, n)) {
if (!bl.data.some(e => e?.[0] === i && e?.[1] === n)) bl.data.push([i, n]);
}
}
if (bl.data.length) bl.data = bl.data.filter(e => e?.[1] && bl.names.includes(e[1]));
},
isIdxBL: (bl, i) => bl.data.some(e => e?.[0] === i),
enforce: (bl, otherPlayers, msgs) => {
if (!bl.data.length) return;
for (const [i] of bl.data) BH.utils.setPlayerVisible(otherPlayers[i], false);
if (msgs) {
const s = new Set(bl.data.map(e => e[0]));
for (let i = 0; i < msgs.length; i++) msgs[i] = msgs[i].filter(m => !s.has(m[0]));
}
}
};
BH.TP = {
update: (v, otherPlayers, lp) => {
if (!v.tpToPlayerEnabled || v.tpTargetName === "Никто") return;
let target = otherPlayers[v.tpTargetIndex];
if (!target || target.myName !== v.tpTargetName) {
const foundIndex = otherPlayers.findIndex(p => p && p.myName === v.tpTargetName);
if (foundIndex !== -1) {
v.tpTargetIndex = foundIndex;
target = otherPlayers[foundIndex];
}
}
if (target?.gpData && target.reset <= 10 && lp) {
lp.setX(target.gpData.getX());
lp.setY(target.gpData.getY());
if (lp.p) { lp.p.velocity[0] = 0; lp.p.velocity[1] = 0; }
}
}
};
BH.Xray = {
clear: (xrayGates) => {
xrayGates.forEach(item => {
if (item.gfx?.parent) item.gfx.parent.removeChild(item.gfx);
if (typeof item.gfx?.destroy === 'function') item.gfx.destroy();
if (item.text?.parent) item.text.parent.removeChild(item.text);
if (typeof item.text?.destroy === 'function') item.text.destroy();
});
xrayGates.length = 0;
},
update: (layoutMode, xrayGates, gp) => {
if (!layoutMode) {
if (xrayGates.length > 0) BH.Xray.clear(xrayGates);
return;
}
if (xrayGates.length === 0 && gp?.list) {
const pixi = window.pixi || window.PIXI;
if (pixi?.Graphics) {
gp.list.forEach(obj => {
if (!obj?.id?.toLowerCase().startsWith('gate')) return;
const action = obj.id.split(':').pop();
obj.shapes.forEach((shape, idx) => {
if (shape?.make === 3 || !shape?.g?.parent) return;
const gfx = new pixi.Graphics();
const color = action === "1" ? 0xFF00FF : 0x00FFFF;
let baseW = 50, baseH = 50, baseR = 25;
if (shape.p) {
if (shape.p.width) baseW = shape.p.width * 100;
if (shape.p.height) baseH = shape.p.height * 100;
if (shape.p.radius) baseR = shape.p.radius * 100;
} else if (shape.g) {
const sx = Math.abs(shape.g.scale?.x || 1), sy = Math.abs(shape.g.scale?.y || 1);
if (shape.g.width) baseW = shape.g.width / sx;
if (shape.g.height) baseH = shape.g.height / sy;
baseR = baseW / 2;
}
const type = typeof shape.getType === 'function' ? shape.getType() : shape.type;
if (type === 2) gfx.circle(0, 0, baseR);
else if (type === 5) {
const triH = baseW * Math.sqrt(3) / 2, rOff = triH / 6;
gfx.moveTo(-baseW / 2, triH / 2 - rOff).lineTo(baseW / 2, triH / 2 - rOff).lineTo(0, -triH / 2 - rOff).closePath();
} else gfx.rect(-baseW / 2, -baseH / 2, baseW, baseH);
gfx.fill({ color, alpha: 0.15 }).stroke({ width: 3, color, alpha: 1 });
if (gp.gWorld?.mid) gp.gWorld.mid.addChild(gfx);
let textObj = null;
if (idx === 0) {
textObj = new pixi.Text(obj.id, { fontFamily: 'monospace', fontSize: 14, fill: color, stroke: 0x000000, strokeThickness: 3, fontWeight: 'bold' });
textObj.anchor.set(0.5, 1);
gp.gWorld.mid.addChild(textObj);
}
xrayGates.push({ gfx, src: shape, text: textObj, baseH, baseR, type });
});
});
}
}
xrayGates.forEach(item => {
const { src, gfx, text, baseH, baseR, type } = item;
if (!src?.g?.parent) { gfx.visible = false; if (text) text.visible = false; return; }
gfx.visible = true; gfx.scale.set(1);
if (typeof src.getGlobalPosition === 'function') {
const pos = src.getGlobalPosition();
gfx.position.set(pos.x * 100, pos.y * -100);
gfx.rotation = -pos.angle;
} else {
gfx.position.set(src.g.x, src.g.y);
gfx.rotation = src.g.rotation || 0;
}
const hasCollision = typeof src.getCollision === 'function' ? src.getCollision() : src.collision;
gfx.alpha = hasCollision ? 0.15 : 1.0;
if (text) text.alpha = hasCollision ? 0.5 : 1.0;
if (text) {
text.visible = true;
text.position.set(gfx.x, gfx.y - (type === 2 ? baseR : baseH / 2) - 5);
text.scale.set(1);
}
});
}
};
// ─── DECLARATIVE HOOKS REGISTRY (OCP Fix) ───
BH.Hooks = {
allFound: false,
registry:[
{
id: 'processOthers',
match: s => s.includes('othersPlayerNetworkData') && s.includes('oldPlayersIndex'),
orig: null, injected: false,
createCustom: (hack, is2pa) => function () {
const mode = hack.mode, gp = hack.gp, nh = hack.networkHandler;
BH.Blacklist.process(hack.vars.blacklisted, mode.otherPlayers);
if (!mode.othersPlayerNetworkData.length) return;
let cur = [];
for (let x = 0; x < mode.othersPlayerNetworkData.length; x++) {
for (let y = 0; y < mode.othersPlayerNetworkData[x].length; y++) {
const d = mode.othersPlayerNetworkData[x][y], idx = d[0], xV = d[1], yV = d[2], xA = d[3], yA = d[4];
if (BH.Blacklist.isIdxBL(hack.vars.blacklisted, idx)) continue;
if (mode.otherPlayers[idx] == null) {
mode.oldPlayersIndex.push(idx);
const p = { myName: "", mySkin: 0, reset: Infinity, gpData: is2pa ? mode.basePlayer.createPlayer(mode.playerData) : mode.createPlayer() };
p.gpData.g.myIndex = idx; mode.otherPlayers[idx] = p;
if (nh?.gsSocket) nh.gsSocket.emit("rBio", idx);
gp.gWorld.removeChild(p.gpData.g); gp.gWorld.mid.addChild(p.gpData.g);
}
const op = mode.otherPlayers[idx];
if (10 < op.reset) { op.gpData.setX(xA); op.gpData.setY(yA); op.reset = 0; }
op.reset++;
op.gpData.p.velocity[0] = xV;
op.gpData.p.velocity[1] = yV;
cur.push(idx);
}
}
const r = BH.utils.diff(mode.oldPlayersIndex, cur);
for (const i of r) {
const p2 = mode.otherPlayers[i];
if (p2) {
gp.gWorld.mid.removeChild(p2.gpData.g); gp.pWorld.removeBody(p2.gpData.p);
gp.list[gp.list.indexOf(p2.gpData)] = null; gp.deleteCounter++; mode.otherPlayers[i] = null;
}
}
mode.oldPlayersIndex = cur; mode.othersPlayerNetworkData =[];
}
},
{
id: 'movement',
match: s => s.includes('moveLeft') && (s.includes('raycast') || s.includes('velocity[1]')),
orig: null, injected: false,
createCustom: (hack, is2pa) => function () {
const ctrl = hack.modeController, p = hack.getLP();
const v = hack.vars, mult = v.mult.value, jh = v.jumpHeight, gs = v.gravNoclipGravScale;
if (!p || !ctrl) return;
const getEV = () => {
const binds = hack.keyBinds, first = v.arrowFirstPressed;
if (binds.ARROW_UP && binds.ARROW_DOWN) return first === 'up' ? 'down' : (first === 'down' ? 'up' : null);
return binds.ARROW_UP ? 'up' : (binds.ARROW_DOWN ? 'down' : null);
};
if (v.noclip) {
p.p.velocity[0] = ctrl.moveRight ? 3 * mult : (ctrl.moveLeft ? -3 * mult : 0);
p.p.velocity[1] = ctrl.moveUp ? 3 * mult : (ctrl.moveDown ? -3 * mult : 0);
return;
}
if (v.gravNoclip) {
p.p.velocity[0] = ctrl.moveRight ? 3 * mult : (ctrl.moveLeft ? -3 * mult : 0);
const ev = getEV();
if (ev === 'up') { p.p.gravityScale = -gs; p.setAngle(180); }
else if (ev === 'down') { p.p.gravityScale = gs; p.setAngle(0); }
else p.p.gravityScale = 0;
const gd = p.p.gravityScale;
let sj = false, jd = 0, rsY, reY;
const ex = p.getX(), ty = p.getY(), hH = 50, rL = 50, n = ex - 15, r = 30 / 11;
if (gd > 0 && ctrl.moveUp) { sj = true; jd = jh; rsY = ty + (hH - 1); reY = rsY + rL; }
else if (gd < 0 && ctrl.moveDown) { sj = true; jd = -jh; rsY = ty - (hH - 1); reY = rsY - rL; }
if (sj) {
const ph = is2pa ? ctrl.physics : hack.gp;
for (let i = 0; i < 12; i++) {
const ox = n + i * r;
p.ray.from =[ph.xAxis(ox, 0), ph.yAxis(rsY, 0)]; p.ray.to =[ph.xAxis(ox, 0), ph.yAxis(reY, 0)];
p.ray.update(); p.ray.result.reset(); p.ray.hitPoint = [Infinity, Infinity];
if (hack.gp.pWorld.raycast(p.ray.result, p.ray) &&
p.ray.result.shape.ref.getCollision() &&
p.ray.result.getHitDistance(p.ray) < 0.15) {
p.p.velocity[1] = jd; break;
}
}
}
return;
}
if (ctrl.moveRight) p.p.velocity[0] = 3;
if (ctrl.moveLeft) p.p.velocity[0] = -3;
if (!ctrl.moveUp) return;
const ex = p.getX(), ty = p.getY(), n = ex - 15, r = 30 / 11, ph = is2pa ? ctrl.physics : hack.gp;
for (let i = 0; i < 12; i++) {
const ox = n + i * r, oy1 = ty + 49, oy2 = oy1 + 50;
p.ray.from =[ph.xAxis(ox, 0), ph.yAxis(oy1, 0)]; p.ray.to =[ph.xAxis(ox, 0), ph.yAxis(oy2, 0)];
p.ray.update(); p.ray.result.reset(); p.ray.hitPoint = [Infinity, Infinity];
if (hack.gp.pWorld.raycast(p.ray.result, p.ray) &&
p.ray.result.shape.ref.getCollision() &&
p.ray.result.getHitDistance(p.ray) < 0.05) {
p.p.velocity[1] = jh; break;
}
}
}
}
],
init: function(hack, is2pa) {
this.registry.forEach(h => {
h.custom = h.createCustom(hack, is2pa);
h.custom.__hk_id = h.id;
});
const injectLoop = () => {
const lf = hack.client.loopFunctions;
let pending = false;
this.registry.forEach(h => {
if (h.injected) return;
const i = lf.findIndex(e => e?.fun?.__hk_id === h.id || (e?.fun && h.match(e.fun.toString())));
if (i !== -1) { lf[i].fun = h.custom; h.injected = true; }
else if (h.id === 'movement' && is2pa && lf.length > 7) { lf[7].fun = h.custom; h.injected = true; }
else pending = true;
});
if (pending) requestAnimationFrame(injectLoop);
};
injectLoop();
const oal = hack.client.addLoopFunction;
hack.client.addLoopFunction = function(f, p, t, e) {
if (f) {
const target = BH.Hooks.registry.find(h => f.__hk_id === h.id || h.match(f.toString()));
if (target) return oal.call(this, target.custom, p, t, e);
}
return oal.call(this, f, p, t, e);
};
}
};
})();