Last active
April 29, 2024 19:44
-
-
Save hobione2k/eadcec8392eaa082e09ef4b3101951a5 to your computer and use it in GitHub Desktop.
ClusterScript Template
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const logLvl = () => $.getStateCompat(Tg.this, "LOG_LEVEL", T.integer); | |
const pd = (n, l = 2) => { | |
return n.toString().padStart(l, "0"); | |
}; | |
const now = () => { | |
const d = new Date(); | |
const time = `${pd(d.getHours())}:${pd(d.getMinutes())}:${pd( | |
d.getSeconds() | |
)}.${pd(d.getMilliseconds(), 3)}`; | |
return time; | |
}; | |
const dmp = (o) => (typeof o === "object" ? JSON.stringify(o) : o); | |
const _lg = (...msg) => $.log(`${now()} ${msg.map(dmp).join()}`); | |
const dlog = (...msg) => { | |
if (logLvl() <= 0) _lg(msg); | |
}; | |
const log = (...msg) => { | |
if (logLvl() <= 1) $.log(`${now()} ${msg.map(dmp).join()}`); | |
}; | |
const elog = (...msg) => { | |
if (logLvl() <= 2) $.log(`${now()} ${msg.map(dmp).join()}`); | |
}; | |
const clamp = (n, a, b) => Math.min(Math.max(n, a), b); | |
const saturate = (n) => clamp(n, 0, 1); | |
const max2 = (v) => Math.max(v.x, v.y); | |
const rand = (min, max) => Math.random() * (max - min) + min; | |
const rand2 = (v) => rand(v.x, v.y); | |
const randInt = (min, max) => Math.floor(Math.random() * (max - min) + min); | |
const randInt2 = (v) => rand(v.x, v.y); | |
const randD = (v, d) => rand(v - v * d * 0.5, v + v * d * 0.5); | |
const shuffle = (array, N = 3) => { | |
for (let i = 0; i < N; i++) array.sort(() => Math.random() - 0.5); | |
return array; | |
}; | |
const rad2Deg = (r) => (r * 180) / Math.PI; | |
const deg2Rad = (d) => (d * Math.PI) / 180; | |
const dotRad = (a, b) => | |
Math.acos(a.clone().normalize().dot(b.clone().normalize())); | |
const dotAngle = (a, b) => rad2Deg(dotRad(a, b)); | |
const cross = (a, b) => a.clone().normalize().cross(b.clone().normalize()); | |
const distance = (a, b) => a.clone().sub(b).length(); | |
const Tg = Object.freeze({ | |
this: "this", | |
owner: "owner", | |
global: "global", | |
}); | |
const T = Object.freeze({ | |
signal: "signal", | |
boolean: "boolean", | |
float: "float", | |
double: "double", | |
integer: "integer", | |
vector2: "vector2", | |
vector3: "vector3", | |
}); | |
const detectSignal = (n) => { | |
const sg = $.getStateCompat(Tg.this, n, T.double); | |
const ky = `last_${n}`; | |
const lsg = $.getStateCompat(Tg.this, ky, T.double); | |
if (sg > lsg) { | |
$.setStateCompat(Tg.this, ky, sg); | |
return true; | |
} | |
return false; | |
}; | |
const setTimer = (n, s) => ($.state[n] = s); | |
const isTimeOver = (n, dt) => { | |
let t = $.state[n]; | |
if (t == null) return true; | |
t -= dt; | |
if (t <= 0) { | |
$.state[n] = null; // タイマー終了 | |
return true; | |
} | |
$.state[n] = t; | |
return false; | |
}; | |
// ベクトル演算 ////////////////// | |
// ベクトル角度 | |
const dirAngle = (dir) => { | |
const y = rad2Deg(Math.atan2(dir.x, dir.z)); | |
const d = new Vector2(dir.x, dir.z).length(); | |
const x = -rad2Deg(Math.atan2(dir.y, d)); | |
return new Vector3(x, y, 0); | |
}; | |
// 特定座標への角度 | |
const aimAtAngle = (p, t) => { | |
const dir = t.clone().sub(p); | |
return dirAngle(dir); | |
}; | |
const aimAt = (p, t) => new Quaternion().setFromEulerAngles(aimAtAngle(p, t)); | |
// 通信 //////////////// | |
const QMX = 50; | |
const isRtExcd = (e) => e.rateLimitExceeded || e.message.includes("RateLimit"); // CSEmu向けに文字列でチェック | |
// 初期化 | |
const inNet = () => { | |
$.state.sndQue = $.state.sndd = $.state.invld = []; | |
$.state.rcvCt = $.state.sndCt = $.state.sndTm = 0; | |
}; | |
// キューに追加 | |
const queSend = (ds) => { | |
const que = $.state.sndQue; | |
const sndd = $.state.sndd; | |
const invld = $.state.invld; | |
for (const d of ds) { | |
const s = d.snd; | |
if (s == null) continue; | |
if (que.length >= QMX) { | |
dlog(`${QMX} over`, d); | |
break; // 追加できない | |
} | |
if (d.on && sndd.some((sd) => sd.id == s.id && sd.t == d.t)) continue; | |
if (invld.includes(s.id)) continue; | |
const i = que.findIndex((q) => q.snd.id == s.id && q.t == d.t); | |
if (i < 0) que.push(d); // 新規 | |
if (d.ow) que[i] = d; // 更新 | |
} | |
$.state.sndQue = que.slice(0, QMX); | |
return que; | |
}; | |
const sendData = (its, t, a = [], ow = true, on = false) => { | |
// on: 送信対象に一度だけ送信 | |
// ow: キューにすでにあれば上書き | |
const d = its.map((i) => ({ | |
snd: i, | |
t: t, | |
a: a ?? [], // null/undefinedの送信用に空配列を設定 | |
ow: ow, | |
on: on, | |
})); | |
return queSend(d); | |
}; | |
const sendOnce = (its, t, a = []) => sendData(its, t, a, false, true); | |
const _snd = (s, t, a) => { | |
s.send(t, a); | |
dlog("send", s.id, t, a); | |
$.state.sndCt++; | |
}; | |
const onRecv = (t, a, s) => { | |
dlog(`Rcv: ${t} from ${s.id}`, a); | |
$.state.rcvCt++; | |
}; | |
const onSend = (dt) => { | |
const que = $.state.sndQue; | |
if (que.length <= 0) return; | |
const sndd = $.state.sndd; | |
const invld = $.state.invld; | |
while (que.length > 0) { | |
// 送信先があれば実行 | |
const d = que.shift(); // 取り出す | |
if (d == null) { | |
dlog(`send data is null`); // 空データ | |
continue; | |
} | |
const s = d.snd; | |
try { | |
_snd(s, d.t, d.a); | |
if (d.on) sndd.push({ id: s.id, t: d.t }); // 一度だけ送信 | |
} catch (e) { | |
if (isRtExcd(e)) { | |
que.unshift(d); // 上限エラーになったら次回 | |
break; | |
} else { | |
invld.push(s.id); // 送信エラーは除外 | |
elog(`invalid:"${s.id}", error:"${e.message}"`); | |
} | |
} | |
} | |
// 更新 | |
$.state.sndQue = que; | |
$.state.sndd = sndd.slice(-QMX); | |
$.state.invld = invld.slice(-QMX); | |
$.state.sndTm += dt; | |
if ($.state.sndTm >= 1) { | |
if ($.state.sndCt > 0 || $.state.rcvCt > 0) | |
dlog(`send: ${$.state.sndCt} recv: ${$.state.rcvCt}`); | |
$.state.sndCt = $.state.rcvCt = 0; | |
} | |
}; | |
// 不要になったIDを削除 | |
const delSnd = (id) => { | |
$.state.sndQue = $.state.sndQue.filter((d) => d.snd.id != id); | |
$.state.sndd = $.state.sndd.filter((s) => s.id != id); | |
$.state.invld = $.state.invld.filter((i) => i != id); | |
}; | |
const PING = "PING"; | |
const ACK = "ACK"; | |
const ack = (s, t) => sendOnce([s], t, ACK); // ACK | |
const ping = (s, t) => sendOnce([s], t, PING); // PING | |
const search = (t) => { | |
const near = $.getItemsNear($.getPosition(), 30); | |
const invld = $.state.invld; | |
const its = near.filter((i) => !invld.includes(i.id)); | |
sendOnce(its, t, PING); | |
}; | |
// サンプル //////////////// | |
{ | |
$.sendSignalCompat(Tg.this, "Signal"); | |
$.sendSignalCompat(Tg.owner, "Signal"); | |
$.setStateCompat(Tg.this, "Trigger", true); | |
$.getStateCompat(Tg.this, "Triger", T.boolean); | |
} | |
$.onStart(() => { | |
// 初期化 | |
}); | |
$.onInteract((player) => { | |
// インタラクト | |
}); | |
$.onGrab((isGrab, isLeftHand, player) => { | |
// アイテムをつかんだ | |
}); | |
$.onUse((isDown, player) => { | |
// アイテムを使った | |
}); | |
$.onRide((osGetOn, player) => { | |
// 乗った | |
}); | |
$.onUpdate((deltaTime) => { | |
// 毎フレーム実行 | |
onSend(deltaTime); | |
}); | |
$.onReceive((messageType, arg, sender) => { | |
// 受信 | |
onRecv(messageType, arg, sender); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment