|
1 | 1 | import { MODULE_ID, SOCKET_NAME, VIDEO_EXTENSIONS, DOC_BACKGROUNDS } from "../config.js"; |
2 | 2 | import { InvestigationBoardState } from "../state.js"; |
3 | 3 | import { collaborativeUpdate, collaborativeDelete, socket, activeGlobalSounds, activeVideoBroadcasts } from "../utils/socket-handler.js"; |
4 | | -import { truncateText, resolvePinImage, getAvailablePinFiles, resolveStampImage, getAvailableStampFiles } from "../utils/helpers.js"; |
| 4 | +import { truncateText, resolvePinImage, getAvailablePinFiles, pickPinFileForDrawing, resolveStampImage, getAvailableStampFiles } from "../utils/helpers.js"; |
5 | 5 | import { NotePreviewer } from "../apps/note-previewer.js"; |
6 | 6 | import { VideoPlayer } from "../apps/video-player.js"; |
7 | 7 | import { drawAllConnectionLines, beginConnectionFrom, resetPinConnectionState } from "./connection-manager.js"; |
@@ -940,9 +940,10 @@ export class CustomDrawing extends Drawing { |
940 | 940 | * |
941 | 941 | * The global "pinColor" setting controls visibility: |
942 | 942 | * "none" → destroy any existing sprite and bail out. |
943 | | - * "random" → pick a random image from the pin folder on first render, |
944 | | - * persist the choice to the note's flags so every client |
945 | | - * shows the same pin. |
| 943 | + * "random" → deterministically pick an image from the pin folder based |
| 944 | + * on the drawing ID (same result on every client); the GM |
| 945 | + * client persists the choice to the note's flags so it stays |
| 946 | + * stable even if the folder contents later change. |
946 | 947 | * |
947 | 948 | * The per-note flag `noteData.pinColor` stores the bare filename |
948 | 949 | * (e.g. "redPin.webp"). resolvePinImage() prepends the configured |
@@ -971,9 +972,16 @@ export class CustomDrawing extends Drawing { |
971 | 972 |
|
972 | 973 | let pinFilename = noteData.pinColor; |
973 | 974 | if (!pinFilename) { |
| 975 | + // Deterministic "random": hash the drawing ID into the sorted file list |
| 976 | + // so every client resolves the same pin without waiting on a flag write. |
974 | 977 | const files = await getAvailablePinFiles(); |
975 | | - pinFilename = files[Math.floor(Math.random() * files.length)]; |
976 | | - await collaborativeUpdate(this.document.id, { [`flags.${MODULE_ID}.pinColor`]: pinFilename }); |
| 978 | + pinFilename = pickPinFileForDrawing(this.document.id, files); |
| 979 | + if (!pinFilename) return; |
| 980 | + // Persist from the GM client only — avoids N clients racing to write |
| 981 | + // different values through the socket on first render. |
| 982 | + if (game.user.isGM) { |
| 983 | + await collaborativeUpdate(this.document.id, { [`flags.${MODULE_ID}.pinColor`]: pinFilename }); |
| 984 | + } |
977 | 985 | } |
978 | 986 |
|
979 | 987 | const pinImage = resolvePinImage(pinFilename); |
|
0 commit comments