Roblox GuidebookWissensbibliothek
Deutsch ⌄

Entwicklung / ROBLOX

Anfragelimits in Roblox: Eine Server-Hilfe vor wiederholten Klicks schützen

Ein Übungslimiter für die Werkstatthilfe erlaubt vier sofortige Versuche und füllt anschließend zwei Token pro Sekunde nach. Wir erklären getrennten Spielerzustand, Serverzeit, Schaltflächenverhalten und lokale Prüfungen ohne echten Roblox-Verkehr.

Aktualisiert:

Eine bestimmte Aktion begrenzen #

Stell dir eine Werkstatt vor, in der Anfänger mit „Wo ist die Station?“ einen Hinweis anfordern. Mehrere schnelle Klicks können versehentlich entstehen, eine Reaktion auf verzögerte Antworten sein oder unerwünschte Wiederholungen darstellen. Wenn jeder Aufruf teure Arbeit startet, kann eine kleine Schaltfläche unnötige Last erzeugen. Beschreibe zuerst die Serveroperation hinter dem Klick und den Grund für ihre Begrenzung.

Wir verwenden einen Texthinweis, keine Bestellung, Belohnung oder Profilspeicherung. Das ist ein eigenes Übungsszenario und keine neu hinzugefügte Funktion der Spiele des Website-Autors. Vier Versuche und zwei Token pro Sekunde sind gewählte Erklärungswerte. Sie sind weder Roblox-Quoten noch allgemeine Empfehlungen für jede Aktion. Ein echter Mehrspieler-Lasttest wurde nicht durchgeführt.

Die Client-Schaltfläche bestimmt keine Serverregel #

Eine vorübergehend deaktivierte Schaltfläche kann anzeigen, dass eine Antwort aussteht. Das hilft Spielern zu verstehen, dass ihre Anfrage bereits gesendet wurde. Trotzdem muss der Server unabhängig entscheiden, ob ein weiterer Aufruf erlaubt ist. Roblox weist ausdrücklich darauf hin, dass ein ausschließlich clientseitiges Anfragelimit nicht genügt.

Akzeptiere weder die vom Client gemeldete Anzahl verbleibender Versuche noch dessen behaupteten letzten Klickzeitpunkt als Erlaubnis. Unser Modul verwaltet Guthaben und Zeit auf dem Server. Der Client fragt, der Server entscheidet. Die Häufigkeitsprüfung macht ungültige Parameter nicht gültig: Typprüfung, zulässige Aktion und Spielzustand bleiben eigene Anforderungen.

Ein Token Bucket erlaubt eine kurze Serie #

Jeder Spieler besitzt gedanklich einen Behälter für höchstens vier Token. Ein Versuch kostet ein Token. Pro Sekunde kommen zwei zurück, jedoch nie über die Kapazität hinaus. Damit sind vier sofortige Versuche möglich; eine anhaltende Serie muss auf Nachfüllung warten. Dies ist der von Roblox beschriebene Token-Bucket-Ansatz.

Das bedeutet nicht „vier Aufrufe in jeder Kalendersekunde“. Nach kurzer Wartezeit kann nur ein Bruchteil eines Tokens vorhanden sein. Solange kein ganzes Token verfügbar ist, wird der nächste Versuch abgelehnt. Wir prüfen diesen Bruchteil ausdrücklich, damit die Wartezeit erklärbar bleibt und nicht wie ein zufälliger Schaltflächenfehler wirkt.

Übungsbehälter: 4 und 2/sBild in voller Größe öffnen ↗
Erfundener lokaler Testablauf. Zeitangaben beziehen sich auf den Übungsbeginn.
ZeitVersuchErgebnis und Guthaben
0Erster bis vierterErlaubt: 4 → 0
0FünfterAbgelehnt: 0
0.25 sNeuer VersuchAbgelehnt: 0,5
0.5 sNeuer VersuchErlaubt: 0

Spielerzustände getrennt halten #

Wenn A seine vier Token ausgibt, darf Bs nächste Anfrage nicht deshalb blockiert werden. Deshalb verwendet die Tabelle den Player aus dem serverseitigen OnServerEvent-Handler als Schlüssel. Ersetze dieses Objekt nicht durch einen zusätzlich vom Client übergebenen Namen oder eine Kennung. Sonst würde die Auswahl eines fremden Behälters von unvertrauenswürdigen Eingaben abhängen.

Getrennte Behälter begrenzen nicht den Gesamtverkehr aller Spieler. Viele Nutzer können ihre erlaubten Versuche gleichzeitig ausgeben. Teure Aktionen oder Backend-Aufrufe erfordern eine getrennte Betrachtung von Gesamtrate, Warteschlangen und Kosten. Das Modul besitzt weder ein gemeinsames Serverbudget noch eine Obergrenze paralleler Aufgaben oder zwischen Servern verteilten Zustand.

Das Übungsmodul verstehen #

Lege HintRequestLimiter in ServerScriptService ab. new(capacity, refillPerSecond, clock) legt Kapazität, Nachfüllrate und Serverzeitfunktion fest. Allow(player) liefert eine Erlaubnis und einen kurzen Ergebniscode. Forget(player) löscht den lokalen Zustand beim Verlassen. Das Modul greift nicht auf DataStore zu, erzeugt kein RemoteEvent und verschickt selbst keinen Hinweis.

Die Kapazität muss eine ganze Zahl von 1 bis 1000 sein. Die Nachfüllrate muss positiv, endlich und höchstens 1000 sein. Diese Grenzen gehören zu unserem Beispiel, nicht zu Plattformquoten. Gebrochene Raten sind erlaubt. Fehlerhafte Zeitwerte führen zur Ablehnung; rückwärtslaufende Zeit fügt keine Token hinzu. Der gesamte Zustand gehört ausschließlich zum aktuellen Server.

os.clock hat keinen festgelegten absoluten Ausgangspunkt. Vergleiche Differenzen statt Kalenderdaten. Das Übungsmodul akzeptiert einen endlichen negativen Ausgangswert; ein eigener lokaler Test prüft die Nachfüllung damit.

-- Original teaching limiter for server-side hint requests.
-- Pass the Player supplied by OnServerEvent, never a client-supplied identity.
-- Frequency control does not validate permissions or grant rewards.
local Limiter = {}

local function finite(value)
    return type(value) == "number" and value == value
        and value > -math.huge and value < math.huge
end

function Limiter.new(capacity, refillPerSecond, clock)
    assert(finite(capacity) and capacity == math.floor(capacity)
        and capacity >= 1 and capacity <= 1000, "Invalid capacity")
    assert(finite(refillPerSecond) and refillPerSecond > 0
        and refillPerSecond <= 1000, "Invalid refill rate")
    if clock == nil then clock = os.clock end
    assert(type(clock) == "function", "Invalid clock")
    local buckets = {}
    local adapter = {}

    function adapter:Allow(player)
        if player == nil then return false, "MissingPlayer" end
        local ok, now = pcall(clock)
        if not ok or not finite(now) then
            return false, "InvalidClock"
        end
        local bucket = buckets[player]
        if not bucket then
            bucket = {tokens = capacity, last = now}
            buckets[player] = bucket
        elseif now < bucket.last then
            return false, "ClockWentBackwards"
        else
            bucket.tokens = math.min(capacity,
                bucket.tokens + (now - bucket.last) * refillPerSecond)
            bucket.last = now
        end
        if bucket.tokens < 1 then return false, "RateLimited" end
        bucket.tokens -= 1
        return true, "Allowed"
    end

    function adapter:Forget(player)
        buckets[player] = nil
    end
    return adapter
end

return Limiter

Den Server-Handler bewusst anschließen #

Erstelle ein RemoteEvent namens GetHint in ReplicatedStorage und ein Server-Script neben dem Modul. Das Script holt die Dienste, lädt das Modul mit require und erstellt den Limiter mit 4, 2 und os.clock. Der Handler verwendet den von Roblox gelieferten Player, prüft die Anfrage und schickt ausschließlich einen vorgegebenen Hinweis an diesen Spieler.

Das Beispiel erwartet TutorialStep=1 als Attribut aus bereits vorhandener serverseitiger Tutoriallogik. Es beginnt kein Tutorial und ändert das Attribut nicht auf Wunsch des Clients. Ohne diesen Zustand wird kein Hinweis gesendet. Auch die Client-Oberfläche fehlt: Verbinde sie separat mit GetHint und dessen Antwort. Das Script zeigt die Integration, kein geprüftes vollständiges Spiel.

Ablauf der Server-HilfeBild in voller Größe öffnen ↗
Eigene Handler-Grafik: Token ersetzen keine Eingabe- und Spielzustandsprüfungen.
-- Server Script; create ReplicatedStorage.GetHint as a RemoteEvent first.
-- TutorialStep must be maintained by your existing SERVER gameplay logic.
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Limiter = require(
    game:GetService("ServerScriptService"):WaitForChild("HintRequestLimiter")
)
local request = ReplicatedStorage:WaitForChild("GetHint")
assert(request:IsA("RemoteEvent"), "GetHint must be a RemoteEvent")
local limiter = Limiter.new(4, 2, os.clock)

request.OnServerEvent:Connect(function(player, hintName)
    -- Each attempt consumes quota before further processing.
    if not limiter:Allow(player) then return end
    if type(hintName) ~= "string" or #hintName > 32 then return end
    if hintName ~= "FindWorkshop" then return end
    if player:GetAttribute("TutorialStep") ~= 1 then return end
    local character = player.Character
    local humanoid = character and character:FindFirstChildOfClass("Humanoid")
    if not humanoid or humanoid.Health <= 0 then return end
    request:FireClient(player, "Hint", "Look for the workshop sign.")
end)

Players.PlayerRemoving:Connect(function(player)
    limiter:Forget(player)
end)

Festlegen, was einen Versuch verbraucht #

Unser Handler ruft Allow vor der Zeichenkettenprüfung auf. Ein Versuch verbraucht daher auch bei ungültigem Hinweisnamen oder falscher Tutorialstufe ein Token. Das ist beabsichtigt: Begrenzt werden Eingänge in den Handler, nicht nur erfolgreich gelieferte Hinweise. Ungültige Eingaben erhalten keinen unbegrenzten kostenlosen Weg in die nachfolgende Verarbeitung.

Erstatte Token nicht nach jeder abgelehnten Anfrage automatisch. Sonst könnte ein Strom ungültiger Eingaben diese Regel umgehen. Die Spielbedeutung braucht dennoch eigene Prüfungen. Ein Token bestätigt weder Belohnungsberechtigung noch Gegenstandsbesitz, Aufgabenabschluss oder Zugriff auf fremde Objekte. Eine andere Aufgabe kann eine andere Reihenfolge oder Kostenregel erfordern, die ausdrücklich definiert werden muss.

Nicht jede abgelehnte Wiederholung beantworten #

Der Übungshandler beendet eine begrenzte Anfrage still. Eine Antwort oder ausführliche Protokollzeile für jede Ablehnung könnte auf diesem Pfad neue Arbeit erzeugen. Lass den Limiter keine endlosen Benachrichtigungen auslösen. Wähle für Beobachtungen eine begrenzte Zusammenfassung der Ablehnungen statt einer Speicherung jedes eingehenden Pakets.

Bei stiller Ablehnung muss die Oberfläche ihre Schaltfläche nach sinnvoller lokaler Wartezeit wieder freigeben und einen erneuten Versuch verständlich machen. Diesen Client-Timer implementieren wir hier nicht. Lass die Schaltfläche niemals dauerhaft auf eine Antwort warten, die der Server absichtlich nicht sendet. Bedienkomfort und serverseitige Erlaubnis sind verschiedene Aufgaben.

Mit kontrollierter Zeit testen #

Der lokale Luau-Test übergibt eine eigene Zeitfunktion. Bei Zeitpunkt 0 darf A vier Versuche ausführen; der fünfte liefert RateLimited. Bei 0,25 ist nur ein halbes Token vorhanden, also bleibt der nächste Versuch abgelehnt. Bei 0,5 ist ein Token verfügbar: Ein Versuch gelingt, der nächste nicht. Diese Zahlen beschreiben einen lokalen Test, keine echten Spielbesuche.

Der Test prüft ebenfalls Bs unabhängigen Behälter. Eine lange Pause stellt höchstens vier Versuche wieder her. Ungültige oder rückwärtslaufende Zeit erzeugt kein zusätzliches Guthaben. Konfiguration, Bereinigung und gebrochene Nachfüllraten werden geprüft. Alle Prüfungen verwenden Ersatzobjekte und kontrollierte Zeit; echte Roblox-Remotes, Spieler und Serverleistungsmessungen fehlen.

PrüfungErwartung
Sofortiger fünfter VersuchRateLimited
Anfrage eines anderen SpielersGetrennter Behälter
Lange PauseHöchstens 4 Token
Zeit läuft rückwärtsKeine Nachfüllung
Ungültige ZeitInvalidClock
Zustand nach BereinigungNeuer voller Behälter

Beim Verlassen bereinigen #

Verbinde Players.PlayerRemoving mit limiter:Forget(player). Andernfalls hält die Tabelle den Zustand ausgeschiedener Spieler bis zum Serverende fest. Rufe Forget nicht bei einer gewöhnlichen Ablehnung auf: Danach bekäme die nächste Anfrage wieder einen vollen Behälter. Bereinigung gehört zum Spielerlebenszyklus und ist kein Mittel zur Freigabe einer Schaltfläche.

Nach gelöschtem Zustand beginnt ein neuer Eintritt mit einem neuen Behälter. Das ist vorgesehenes lokales Verhalten, kein Schutz gegen erneute Verbindung und kein kontoweites Limit. Zustand bleibt nicht zwischen Servern erhalten. Anforderungen über verschiedene Sitzungen hinweg brauchen ein eigenes Konzept; das Löschen eines Tabelleneintrags beweist keine solche Garantie.

Häufigkeit begrenzt nicht alle Folgearbeit #

Eine erlaubte Anfrage kann an anderer Stelle eine lange Aufgabe starten. Unser Modul wartet nicht auf deren Ende und zählt keine bereits laufenden Aufgaben. Auch die erlaubte Rate kann für eine teure Operation zu hoch sein. Prüfe, was nach der Erlaubnis geschieht: Werden große Modelle kopiert, API-Aufrufe gestartet oder andere Spieler beeinflusst?

Käufe, Datenspeicherung und gemeinsame Wirtschaftssysteme benötigen eigene Regeln, nicht nur Token. Das Beispiel prüft auch keinen Abstand zu einer Station: Sein Texthinweis erfordert das bewusst nicht. Ergänze beim Übertragen auf Objektinteraktionen die benötigten Serverprüfungen für Position, Berechtigung und Zustand. Wir haben keinen vorhandenen Spielcode des Autors verändert.

Die Übergabe vorbereiten #

Bewahre begrenzte Aktion, Bedeutung des Tokenverbrauchs, Kapazität, Nachfüllrate und erforderliche Serverprüfungen zusammen auf. Ergänze lokale Testabläufe und markiere echte Netzwerk- und Lasttests als ausstehend. Übertrage vier nicht als universelle Einstellung für jede Schaltfläche oder Waffe; der Wert gehört nur zu dieser Übung.

Bitte den Entwickler, zwei Spieler, lange Pausen, Verlassen und ungültige Anfragen sowie die Schaltflächenfreigabe nach stiller Ablehnung zu prüfen. Liste fehlende Funktionen getrennt auf: gemeinsames Serverbudget, parallele Operationen, Zustand über Sitzungen hinweg und Client-Oberfläche. So lässt sich die Idee im nächsten Spiel verwenden, ohne den tatsächlichen Fertigstellungsgrad zu verschleiern.

Originalquellen

Roblox Creator Hub — Client-server boundary
Luau — Standard library
Roblox Creator Hub — os