Skip to content

Instantly share code, notes, and snippets.

@hobione2k
Last active April 29, 2024 19:44
Show Gist options
  • Save hobione2k/eadcec8392eaa082e09ef4b3101951a5 to your computer and use it in GitHub Desktop.
Save hobione2k/eadcec8392eaa082e09ef4b3101951a5 to your computer and use it in GitHub Desktop.
ClusterScript Template
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