Opslaan & multiplayer

Gevorderd Laatst bijgewerkt 9 jul 2026

Op deze pagina

Questvoortgang blijft uit de doos bewaard: in single player schrijf je geen regel code, en hetzelfde model schaalt met één interface door naar dedicated servers.

Autosave / autoload

Het questcomponent van elke speler laadt bij de start de bewaarde voortgang en slaat op na wijzigingen (accepteren, doel bijschrijven, naar een volgende stap gaan, vlaggen, voltooien). Opslagacties worden samengevoegd: een reeks wijzigingen binnen één frame kost één schrijfactie. Bij afsluiten, map travel en uitloggen volgt een laatste opslag, en autoload herstelt de voortgang automatisch bij het wisselen van level.

Het beleid stel je in via Project Settings → Plugins → Questwright Quests (details in de Instellingenreferentie):

Project Settings met de opties voor het bewaren van Questwright Quests

Automatisch bewaren treedt in werking voor componenten die eigendom zijn van een PlayerState (de standaard uitrol); componenten op kale actors blijven volledig handmatig met SaveProgress() / LoadProgress().

Wat er wordt bewaard

Alleen de voortgangsrecords per speler: status, huidige stap, doeltellers, vlaggen, tijdstempels. De inhoud van een quest leeft in de gecompileerde assets; een actieve dialoogscène is presentatie en wordt nooit bewaard. Bewaarde voortgang overleeft contentupdates: een record waarvan de opgeslagen stap niet meer bestaat, valt terug op de eerste stap van de quest (met een waarschuwing in de log), en records van quests die de huidige build niet kent, blijven ongemoeid.

De drie niveaus

UitrolWat je doet
Single playerNiets. Eén USaveGame-slot (Questwright_Quests).
Co-op op een listen serverNiets. Elke speler krijgt een eigen slot met zijn online-id als sleutel (Questwright_Quests_<id>).
Dedicated server / eigen backendImplementeer één C++-interface (hieronder).

Het koppelpunt voor de backend is IQuestwrightSaveProvider:

class UMyDbSaveProvider : public UObject, public IQuestwrightSaveProvider
{
    // PlayerKey is the player's UniqueNetId string; use it as your database key.
    virtual bool SaveQuestRecords(const FString& PlayerKey,
                                  const TArray<FQuestwrightQuestRecord>& Records) override;
    virtual bool LoadQuestRecords(const FString& PlayerKey,
                                  TArray<FQuestwrightQuestRecord>& Out) override;
    // Optional non-blocking variant for REST/database fetches:
    // virtual void LoadQuestRecordsAsync(...) override;
};

Registreer die als SaveProviderClass op de instellingenpagina, of injecteer bij het inloggen een instantie met SetSaveProvider(). Alles draait aan de serverkant; clients krijgen de geladen voortgang via de normale replicatie.

Handmatige aansturing

Voor een game met opslagpunten: zet AutoSavePolicy op Manual en roep zelf SaveProgress() / LoadProgress() aan. OnProgressLoaded vuurt nadat de bewaarde voortgang is toegepast.

Multiplayermodel

Questwright werkt in standalone, op een listen server en op een dedicated server, met een strikte server-authoritative scheiding:

  • Voortgang is per speler, in een component op de PlayerState, gerepliceerd alleen naar de eigenaar, zodat spelers nooit de binnenkant van elkaars journaal zien.
  • Alle mutaties zijn server-authoritative. Aanroepen vanaf de client (een quest accepteren vanuit de UI, een event melden) lopen automatisch via Server RPC’s; de API ziet er aan beide kanten hetzelfde uit.
  • Kill credit gaat naar de speler die de kill maakte, als je code voor sterfgevallen de killer meegeeft; anders wordt het aan alle spelers bijgeschreven (fallback voor een gedeelde wereld).
  • Presentatie draait nooit op de server. Camera’s, gezichten, dialoog-UI en look-at draaien alleen op de client van de eigenaar; een dedicated server voert er niets van uit.
  • Componenten worden bij het inloggen automatisch aangemaakt, dus wie later binnenkomt, krijgt de zijne vanzelf.