perfectionist-plugins 1.0.3 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ban-blocker.js +107 -30
- package/package.json +1 -1
- package/perfectionist-utils.js +7 -3
package/ban-blocker.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const version = "3.4.
|
|
1
|
+
export const version = "3.4.2";
|
|
2
2
|
|
|
3
3
|
const PLUGIN_ID = "ban-blocker";
|
|
4
4
|
const STYLE_ID = "perfectionist-ban-blocker-style";
|
|
@@ -13,6 +13,7 @@ const FAIL_COOLDOWN_MAX_MS = 30000;
|
|
|
13
13
|
const POLL_MS = 40;
|
|
14
14
|
const ENSURE_MS = 300;
|
|
15
15
|
const DETECT_RETRY_MS = 2000;
|
|
16
|
+
const GONE_RESET_MS = 1500;
|
|
16
17
|
|
|
17
18
|
const BAN_GLYPH_SVG =
|
|
18
19
|
"<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'>" +
|
|
@@ -58,11 +59,14 @@ let ensureTimer = null;
|
|
|
58
59
|
let detectTimer = null;
|
|
59
60
|
let refreshTimer = null;
|
|
60
61
|
let pumpTimer = null;
|
|
62
|
+
let goneTimer = null;
|
|
61
63
|
let observer = null;
|
|
62
64
|
let pollInFlight = false;
|
|
63
65
|
let uiScheduled = false;
|
|
64
66
|
|
|
65
67
|
const enabled = new Map();
|
|
68
|
+
const alias = new Map();
|
|
69
|
+
const fetchedNames = new Set();
|
|
66
70
|
const playerNames = new Map();
|
|
67
71
|
const cellToPlayer = new Map();
|
|
68
72
|
const buttons = new Map();
|
|
@@ -96,13 +100,62 @@ function isPicked(members, champ) {
|
|
|
96
100
|
return (members ?? []).some((p) => p.championId === champ && p.completed);
|
|
97
101
|
}
|
|
98
102
|
|
|
103
|
+
function canonOf(id) {
|
|
104
|
+
return (id != null && alias.get(id)) || id;
|
|
105
|
+
}
|
|
106
|
+
|
|
99
107
|
function isBlocked(id) {
|
|
100
|
-
return enabled.get(id) === true;
|
|
108
|
+
return enabled.get(canonOf(id)) === true;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function isWeakKey(k) {
|
|
112
|
+
return typeof k === "string" && k.startsWith("cell:");
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function payloadName(p) {
|
|
116
|
+
return norm(`${p?.gameName ?? ""}${p?.tagLine ?? ""}`) || norm(p?.summonerName) || "";
|
|
101
117
|
}
|
|
102
118
|
|
|
103
|
-
function
|
|
119
|
+
function keyOf(p) {
|
|
104
120
|
if (typeof p === "number") return p;
|
|
105
|
-
|
|
121
|
+
const strong = p?.summonerId || p?.puuid;
|
|
122
|
+
if (strong) return strong;
|
|
123
|
+
const n = payloadName(p);
|
|
124
|
+
if (n) return `name:${n}`;
|
|
125
|
+
return p?.cellId != null ? `cell:${p.cellId}` : null;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function linkPlayer(p, rowName, cellId) {
|
|
129
|
+
const strong = typeof p === "number" ? p : p?.summonerId || p?.puuid;
|
|
130
|
+
const pn = typeof p === "number" ? "" : payloadName(p);
|
|
131
|
+
const rn = norm(rowName);
|
|
132
|
+
const keys = [];
|
|
133
|
+
if (strong) keys.push(strong);
|
|
134
|
+
if (pn) keys.push(`name:${pn}`);
|
|
135
|
+
if (rn && rn !== pn) keys.push(`name:${rn}`);
|
|
136
|
+
const weak = cellId != null ? `cell:${cellId}` : null;
|
|
137
|
+
|
|
138
|
+
let root = null;
|
|
139
|
+
for (const k of keys) {
|
|
140
|
+
const r = alias.get(k);
|
|
141
|
+
if (r != null) {
|
|
142
|
+
root = r;
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (root == null && keys.length) {
|
|
147
|
+
root = keys[0];
|
|
148
|
+
const prev = weak ? alias.get(weak) : null;
|
|
149
|
+
if (prev != null && isWeakKey(prev) && enabled.has(prev)) {
|
|
150
|
+
if (!enabled.has(root)) enabled.set(root, enabled.get(prev));
|
|
151
|
+
enabled.delete(prev);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (root == null) root = (weak && alias.get(weak)) || weak;
|
|
155
|
+
if (root == null) return null;
|
|
156
|
+
for (const k of keys) alias.set(k, root);
|
|
157
|
+
if (weak) alias.set(weak, root);
|
|
158
|
+
return root;
|
|
106
159
|
}
|
|
107
160
|
|
|
108
161
|
function banPending(s) {
|
|
@@ -149,7 +202,10 @@ function decide(s) {
|
|
|
149
202
|
const me = s?.localPlayerCellId;
|
|
150
203
|
|
|
151
204
|
cellToPlayer.clear();
|
|
152
|
-
for (const p of m)
|
|
205
|
+
for (const p of m) {
|
|
206
|
+
const row = rows.find((r) => r.slotId === p.cellId);
|
|
207
|
+
cellToPlayer.set(p.cellId, linkPlayer(p, row?.name, p.cellId));
|
|
208
|
+
}
|
|
153
209
|
|
|
154
210
|
let myPick = null;
|
|
155
211
|
for (const a of actions) {
|
|
@@ -160,7 +216,7 @@ function decide(s) {
|
|
|
160
216
|
}
|
|
161
217
|
myActionId = myPick ? myPick.id : null;
|
|
162
218
|
|
|
163
|
-
const blockedCells = new Set(m.filter((p) => isBlocked(
|
|
219
|
+
const blockedCells = new Set(m.filter((p) => isBlocked(cellToPlayer.get(p.cellId))).map((p) => p.cellId));
|
|
164
220
|
|
|
165
221
|
let threat = 0;
|
|
166
222
|
for (const a of actions) {
|
|
@@ -193,6 +249,15 @@ function onSessionGone() {
|
|
|
193
249
|
goneAway = true;
|
|
194
250
|
uiVisible = false;
|
|
195
251
|
removeButtons();
|
|
252
|
+
if (!goneTimer) {
|
|
253
|
+
goneTimer = setTimeout(() => {
|
|
254
|
+
goneTimer = null;
|
|
255
|
+
enabled.clear();
|
|
256
|
+
alias.clear();
|
|
257
|
+
playerNames.clear();
|
|
258
|
+
fetchedNames.clear();
|
|
259
|
+
}, GONE_RESET_MS);
|
|
260
|
+
}
|
|
196
261
|
}
|
|
197
262
|
|
|
198
263
|
function onSession(raw) {
|
|
@@ -208,6 +273,8 @@ function onSession(raw) {
|
|
|
208
273
|
return;
|
|
209
274
|
}
|
|
210
275
|
if (!s?.myTeam) return;
|
|
276
|
+
clearTimeout(goneTimer);
|
|
277
|
+
goneTimer = null;
|
|
211
278
|
goneAway = false;
|
|
212
279
|
lastSession = s;
|
|
213
280
|
|
|
@@ -256,29 +323,27 @@ function getRows() {
|
|
|
256
323
|
}
|
|
257
324
|
|
|
258
325
|
function ensureName(cell) {
|
|
259
|
-
const
|
|
260
|
-
if (
|
|
261
|
-
playerNames.set(id, []);
|
|
326
|
+
const strong = cell?.summonerId || cell?.puuid;
|
|
327
|
+
if (!strong || fetchedNames.has(strong)) return;
|
|
262
328
|
const get = api?.lcu?.get;
|
|
263
|
-
if (!get)
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
}
|
|
267
|
-
const byPuuid = typeof id === "string" && id.length > 20;
|
|
268
|
-
const path = byPuuid ? `/lol-summoner/v2/summoners/puuid/${id}` : `/lol-summoner/v1/summoners/${id}`;
|
|
329
|
+
if (!get) return;
|
|
330
|
+
fetchedNames.add(strong);
|
|
331
|
+
const byPuuid = typeof strong === "string" && strong.length > 20;
|
|
332
|
+
const path = byPuuid ? `/lol-summoner/v2/summoners/puuid/${strong}` : `/lol-summoner/v1/summoners/${strong}`;
|
|
269
333
|
get(path)
|
|
270
334
|
.then((data) => {
|
|
271
335
|
const names = [norm(data?.displayName), norm(data?.gameName)].filter(Boolean);
|
|
272
|
-
if (names.length)
|
|
273
|
-
|
|
336
|
+
if (!names.length) return;
|
|
337
|
+
const root = canonOf(strong);
|
|
338
|
+
playerNames.set(root, [...new Set([...(playerNames.get(root) ?? []), ...names])]);
|
|
274
339
|
})
|
|
275
|
-
.catch(() =>
|
|
340
|
+
.catch(() => fetchedNames.delete(strong));
|
|
276
341
|
}
|
|
277
342
|
|
|
278
343
|
function nameMatches(rowName, id) {
|
|
279
344
|
const rn = norm(rowName);
|
|
280
345
|
if (!rn) return false;
|
|
281
|
-
return (playerNames.get(id) ?? []).some(
|
|
346
|
+
return (playerNames.get(canonOf(id)) ?? []).some(
|
|
282
347
|
(n) => n === rn || (n.length >= 3 && rn.length >= 3 && (n.startsWith(rn) || rn.startsWith(n))),
|
|
283
348
|
);
|
|
284
349
|
}
|
|
@@ -292,8 +357,13 @@ function bindRowsToTeam(session) {
|
|
|
292
357
|
for (const r of list) {
|
|
293
358
|
const cell =
|
|
294
359
|
(r.slotId != null ? cells.find((c) => c.cellId === r.slotId) : null) ??
|
|
295
|
-
cells.find((c) => nameMatches(r.name,
|
|
296
|
-
|
|
360
|
+
cells.find((c) => nameMatches(r.name, keyOf(c)));
|
|
361
|
+
if (cell) {
|
|
362
|
+
r.player = linkPlayer(cell, r.name, cell.cellId);
|
|
363
|
+
cellToPlayer.set(cell.cellId, r.player);
|
|
364
|
+
} else {
|
|
365
|
+
r.player = null;
|
|
366
|
+
}
|
|
297
367
|
}
|
|
298
368
|
}
|
|
299
369
|
|
|
@@ -303,14 +373,18 @@ function stopEvent(e) {
|
|
|
303
373
|
e.preventDefault();
|
|
304
374
|
}
|
|
305
375
|
|
|
376
|
+
function playerForBadge(btn) {
|
|
377
|
+
const rowEl = btn.closest(".summoner-object");
|
|
378
|
+
const r = rowEl ? rows.find((x) => x.el === rowEl) : rows[Number(btn.dataset.slot)];
|
|
379
|
+
const byCell = btn.dataset.cell ? cellToPlayer.get(Number(btn.dataset.cell)) ?? null : null;
|
|
380
|
+
return canonOf(byCell ?? r?.player ?? null);
|
|
381
|
+
}
|
|
382
|
+
|
|
306
383
|
function toggle(btn) {
|
|
307
|
-
let id =
|
|
308
|
-
if (id == null) {
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
const rowEl = btn.closest(".summoner-object");
|
|
312
|
-
const r = list.find((x) => x.el === rowEl) ?? list[Number(btn.dataset.slot)];
|
|
313
|
-
id = r?.player ?? null;
|
|
384
|
+
let id = playerForBadge(btn);
|
|
385
|
+
if (id == null && lastSession) {
|
|
386
|
+
bindRowsToTeam(lastSession);
|
|
387
|
+
id = playerForBadge(btn);
|
|
314
388
|
}
|
|
315
389
|
if (id == null) return;
|
|
316
390
|
const myCell = lastSession?.localPlayerCellId;
|
|
@@ -386,7 +460,7 @@ function ensureButtons() {
|
|
|
386
460
|
function renderButtons() {
|
|
387
461
|
const hasSession = !!lastSession?.myTeam;
|
|
388
462
|
for (const [, btn] of buttons) {
|
|
389
|
-
const id =
|
|
463
|
+
const id = playerForBadge(btn);
|
|
390
464
|
const on = id != null && isBlocked(id);
|
|
391
465
|
btn.classList.toggle("on", on);
|
|
392
466
|
btn.classList.toggle("unresolved", id == null && hasSession);
|
|
@@ -505,7 +579,8 @@ function onDisable() {
|
|
|
505
579
|
clearTimeout(refreshTimer);
|
|
506
580
|
clearTimeout(detectTimer);
|
|
507
581
|
clearTimeout(pumpTimer);
|
|
508
|
-
|
|
582
|
+
clearTimeout(goneTimer);
|
|
583
|
+
pollTimer = ensureTimer = refreshTimer = detectTimer = pumpTimer = goneTimer = null;
|
|
509
584
|
observer?.disconnect();
|
|
510
585
|
observer = null;
|
|
511
586
|
|
|
@@ -518,6 +593,8 @@ function onDisable() {
|
|
|
518
593
|
cellToPlayer.clear();
|
|
519
594
|
enabled.clear();
|
|
520
595
|
playerNames.clear();
|
|
596
|
+
alias.clear();
|
|
597
|
+
fetchedNames.clear();
|
|
521
598
|
lastSession = null;
|
|
522
599
|
rows = [];
|
|
523
600
|
removeButtons();
|
package/package.json
CHANGED
package/perfectionist-utils.js
CHANGED
|
@@ -6,7 +6,7 @@ const PANEL_ID = "perfectionist-plugins-panel";
|
|
|
6
6
|
const STYLE_ID = "perfectionist-plugins-style";
|
|
7
7
|
const TAB_TITLE = "Perfectionism";
|
|
8
8
|
|
|
9
|
-
export const version = "1.0.
|
|
9
|
+
export const version = "1.0.4";
|
|
10
10
|
|
|
11
11
|
const PKG = "perfectionist-plugins";
|
|
12
12
|
const CDN = "https://cdn.jsdelivr.net/npm";
|
|
@@ -26,7 +26,7 @@ const BUNDLED_MODULES = [
|
|
|
26
26
|
id: "ban-blocker",
|
|
27
27
|
file: "ban-blocker.js",
|
|
28
28
|
name: "Ban Blocker",
|
|
29
|
-
version: "3.4.
|
|
29
|
+
version: "3.4.2",
|
|
30
30
|
description: "During bans, blocking a teammate instantly claims the champion they hover to ban.",
|
|
31
31
|
},
|
|
32
32
|
{
|
|
@@ -123,11 +123,15 @@ function saveState() {
|
|
|
123
123
|
enabled: state.enabled,
|
|
124
124
|
};
|
|
125
125
|
try {
|
|
126
|
-
if (typeof DataStore !== "undefined") DataStore.set(STATE_KEY, record);
|
|
127
126
|
localStorage.setItem(STATE_KEY, JSON.stringify(record));
|
|
128
127
|
} catch (error) {
|
|
129
128
|
console.error("[Perfectionism] Could not save plugin state.", error);
|
|
130
129
|
}
|
|
130
|
+
try {
|
|
131
|
+
if (typeof DataStore !== "undefined") DataStore.set(STATE_KEY, record);
|
|
132
|
+
} catch (error) {
|
|
133
|
+
console.error("[Perfectionism] Could not save plugin state.", error);
|
|
134
|
+
}
|
|
131
135
|
}
|
|
132
136
|
|
|
133
137
|
function logError(scope, error) {
|