Portal deweloperski
Eksperymentalne Platforma botów i aplikacji jest aktywnie rozwijana. Obsługa serwerów hostowanych samodzielnie pojawiła się 13.08.2026 i ma mniej funkcji niż chmura. Zobacz, co jest obsługiwane.
← Dokumentacja

Snowflake

GameVox zwraca identyfikatory w formacie 64-bitowego snowflake Discorda wszędzie tam, gdzie zrobiłoby to API Discorda. Układ bitów, kolejność sortowania i obliczanie znacznika czasu są identyczne, więc twój obecny kod parsujący snowflake działa bez zmian.

Format

Identyczny jak na Discordzie: 42-bitowy znacznik czasu + 5-bitowy worker + 5-bitowy proces + 12-bitowy licznik, big-endian w 64-bitowej liczbie całkowitej bez znaku.

┌─ 42 bits ─────────────────┬─ 5 ─┬─ 5 ─┬─ 12 ────────┐
│ ms od epoki GameVox       │ wkr │ prc │ increment   │
└───────────────────────────┴─────┴─────┴─────────────┘
 63                       22  21 17 16 12 11           0

Każdy segment odpowiada Discordowi:

  • Znacznik czasu (42 bity): milisekundy od epoki GameVox, patrz niżej.
  • Worker ID (5 bitów): przydzielany każdemu działającemu zadaniu api przy starcie przez atomowy licznik w Redis.
  • Process ID (5 bitów): w parze z workerem tworzy unikalną pulę generowania.
  • Licznik (12 bitów): sekwencja na milisekundę dla każdej pary (worker, proces).

Epoka

GameVox używa tej samej epoki co Discord: 1420070400000 (2015-01-01 00:00:00 UTC). Parser snowflake napisany dla Discorda działa z identyfikatorami GameVox bez modyfikacji, nie trzeba podmieniać żadnej stałej.

const SNOWFLAKE_EPOCH = 1420070400000n;

Format przesyłu

Każdy snowflake w REST i gatewayu jest serializowany jako ciąg znaków JSON, a nie liczba. JavaScript traci precyzję powyżej Number.MAX_SAFE_INTEGER (253-1), a snowflake regularnie przekracza tę wartość. Gdy potrzebujesz obliczeń, zawsze parsuj przez BigInt, a wszędzie indziej przechowuj jako ciąg znaków.

// Dobrze
const id = BigInt(message.id);

// Źle: cicha utrata precyzji
const id = Number(message.id);

Wyciąganie znacznika czasu

JavaScript

function snowflakeToDate(snowflake) {
  const EPOCH = 1420070400000n;          // tak samo jak Discord
  const ms = Number((BigInt(snowflake) >> 22n) + EPOCH);
  return new Date(ms);
}

console.log(snowflakeToDate('182955831900192769'));
// → obiekt Date: kiedy ten snowflake został wygenerowany

Python

from datetime import datetime, timezone

SNOWFLAKE_EPOCH = 1420070400000

def snowflake_to_dt(snowflake):
    ms = (int(snowflake) >> 22) + SNOWFLAKE_EPOCH
    return datetime.fromtimestamp(ms / 1000, tz=timezone.utc)

Java

long SNOWFLAKE_EPOCH = 1420070400000L;
long ms = (Long.parseUnsignedLong(snowflake) >>> 22) + SNOWFLAKE_EPOCH;
Instant when = Instant.ofEpochMilli(ms);

Kolejność sortowania

Ponieważ znacznik czasu jest w najstarszych bitach, leksykograficzne porównanie dwóch snowflake jako ciągów znaków układa je chronologicznie, tak jak na Discordzie. Dlatego paginacja before / after / around w endpoincie wiadomości działa bez osobnego pola ze znacznikiem czasu.

// W tablicy wiadomości, od najstarszej → do najnowszej:
messages.sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0);

Dane natywne i tabela aliasów

Pod spodem natywne obiekty GameVox (serwery, kanały, użytkownicy, wiadomości itd.) używają UUIDv4. Warstwa zgodności z botami generuje stały snowflake przy pierwszym udostępnieniu obiektu botowi i zapisuje tę parę w tabeli snowflake_aliases. Każde kolejne odwołanie do tego obiektu, przez REST lub gateway, używa tego samego snowflake.

Dla wiadomości uzupełniamy snowflake wstecznie, używając created_at wiadomości jako znacznika czasu, dzięki czemu historyczne wiadomości sortują się poprawnie, gdy bot po raz pierwszy dołącza do kanału. Dla wszystkich innych obiektów alias jest generowany leniwie przy pierwszym udostępnieniu z NOW() jako znacznikiem czasu, więc snowflake odzwierciedla moment, w którym bot zobaczył obiekt, a niekoniecznie moment jego utworzenia.

Hash shardingu

Standardowa formuła shardów Discorda działa bez zmian:

shardId = (BigInt(guildId) >> 22n) % BigInt(numShards);

Boty na mniej niż 2500 serwerach działają na jednym shardzie ([0, 1]) i nigdy nie muszą się tym przejmować. Większe boty dostają taki sam, mniej więcej równomierny rozkład hasha jak na Discordzie, bo najstarsze 42 bity rosną monotonicznie w czasie, a nie są losowe.

Pułapki

  • Nie porównuj snowflake operatorem == z literałem liczbowym. W przesyle to ciągi znaków: porównuj z ciągiem albo zamień oba na BigInt.
  • 42-bitowy znacznik czasu przepełni się po około 139 latach od epoki. Snowflake GameVox są ważne do 2165 roku.
  • Nie wyznaczaj daty utworzenia serwera, kanału ani roli z jego snowflake. Obiekty natywne starsze niż platforma botów dostały snowflake przy pierwszym udostępnieniu botowi, a nie przy utworzeniu. Wyjątkiem są wiadomości (uzupełnione wstecznie na podstawie created_at).

← Powrót do dokumentacji