Lingara Lingara Documentazione Guide API Librerie App Crea Incorporare App web
Lingua: Italiano

Godot

Questa pagina è tradotta dall'inglese. Se le due versioni differiscono, fa fede la pagina in inglese. Leggi la pagina in inglese

Godot offre due linguaggi, e Lingara ha un kit per ciascuno. Un gioco GDScript usa l’addon Lingara. Un gioco Godot in C# usa Lingara.Embed, lo stesso pacchetto usato da un gioco Unity. In un progetto usa l’uno o l’altro, mai entrambi. Le schede qui sotto mostrano ciascuno.

Installazione

GDScript. Installa l’addon Lingara dalla Godot Asset Library, poi abilitalo in Project Settings, Plugins. Abilitarlo aggiunge un autoload chiamato Lingara, che ogni scena raggiunge in /root/Lingara.

C#. Aggiungi Lingara.Embed al tuo progetto come riferimento a pacchetto. Il nodo di esempio qui sotto, LingaraEmbedNode, non fa parte del pacchetto: aggiungi il suo sorgente al tuo progetto e inseriscilo in una scena.

Ottenere i token dal tuo server

Nessuno dei due kit conserva un segreto. Quando il core ha bisogno di un token, lo chiede al tuo server, che ne emette uno con un kit server e risponde con il token e la sua scadenza.

Con l’addon, imposta la route dei token del tuo server in Project Settings e Lingara la chiama su un proprio thread. Se la route ha bisogno della sessione del giocatore, impostala invece in fase di esecuzione, perché le impostazioni del progetto vengono incluse nella tua esportazione. Oppure collega token_needed e rispondi tu stesso a ogni richiesta. Con C#, il nodo di esempio invia una richiesta alla tua route dei token ogni volta che il client chiede un token. In entrambi i casi, segnala un errore con token_unavailable o TokenUnavailable, così le chiamate in attesa falliscono subito.

Elaborare gli eventi a ogni frame

Il core esegue il suo I/O di rete su un proprio thread. L’autoload dell’addon lo svuota una volta per frame ed emette un segnale per ogni evento, così colleghi i segnali di cui il tuo gioco ha bisogno e non svuoti mai nulla tu stesso. Il nodo C# svuota in _process, al massimo 256 eventi per frame, e riemette ciascuno come segnale Godot.

Gli stessi segnali trasportano le risposte degli NPC in streaming (vedi Dialoghi con gli NPC) e gli aggiornamenti della pratica (vedi risultati della pratica).

<PackageReference Include="Lingara.Embed" Version="0.1.0-alpha.6" />
Linguaggio dell'esempio di codice

Godot C#

// Your own server mints the token: never put a client secret in a game. The
// continuation may run off the main thread, which the client allows.
private async Task FetchToken()
{
    try
    {
        using var response = await Http.PostAsync(TokenUrl, new StringContent("{}"));
        response.EnsureSuccessStatusCode();
        using var body = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
        Client?.ProvideToken(body.RootElement.GetProperty("token").GetString(), body.RootElement.GetProperty("expires_at").GetString());
    }
    catch (Exception e) when (e is HttpRequestException || e is JsonException || e is KeyNotFoundException)
    {
        Client?.TokenUnavailable(e.Message); // fail the waiting calls now
    }
}

GDScript

# The common case: set Project Settings > lingara/token_url to your server's
# token route, which answers exactly {"token", "expires_at"}. Lingara POSTs to
# it on its own thread whenever it needs a token. If the route needs the
# player's session, set it at run time instead (never in ProjectSettings,
# which ships inside the export):
func use_my_session(session_cookie: String) -> void:
	lingara.set_token_endpoint("https://game.example.com/lingara-token", {"cookie": session_cookie})


# Or answer each token_needed yourself, with whatever login your game has.
func _on_token_needed(_request: int, _reason: String) -> void:
	var http := HTTPRequest.new()
	add_child(http)
	http.request("https://game.example.com/lingara-token", [], HTTPClient.METHOD_POST, "{}")
	var reply: Array = await http.request_completed
	http.queue_free()
	var body: Variant = JSON.parse_string((reply[3] as PackedByteArray).get_string_from_utf8())
	if reply[1] == 200 and body is Dictionary:
		lingara.provide_token(body["token"], body["expires_at"])
	else:
		lingara.token_unavailable("the token route answered %d" % reply[1])
Linguaggio dell'esempio di codice

Godot C#

public override void _Process(double delta)
{
    if (Client == null) return;
    events.Clear();
    Client.Drain(events); // at most 256 a frame; the rest wait for the next
    foreach (var e in events)
    {
        switch (e)
        {
            case EmbedEvent.TokenNeeded _: _ = FetchToken(); break;
            case EmbedEvent.NpcTurn { Update: NpcTurnUpdate.Fragment f } t:
                EmitSignal(SignalName.NpcTurnFragment, t.Dialogue.Value, t.Request.Value, f.Text); break;
            case EmbedEvent.NpcTurn { Update: NpcTurnUpdate.Finished f } t:
                EmitSignal(SignalName.NpcTurnFinished, t.Dialogue.Value, t.Request.Value, f.Text); break;
            case EmbedEvent.Practice p: EmitSignal(SignalName.PracticeChanged, p.Session.Value, p.Snapshot.Phase.ToString()); break;
            case EmbedEvent.Failed f: EmitSignal(SignalName.Failed, f.Error.KindName, f.Error.Message, f.Error.Request?.Value ?? 0); break;
            case EmbedEvent.Log log: GD.Print($"[lingara] {log.Level} {log.Target}: {log.Message}"); break;
        }
    }
}

GDScript

# The Lingara autoload the plugin adds, reached by path and typed, so every
# call and signal below is checked against LingaraEmbed.
@onready var lingara: LingaraEmbed = get_node("/root/Lingara")

# Enable the plugin (Project > Project Settings > Plugins): it adds the
# Lingara autoload, which drains the core once per frame and emits one signal
# per event. Connect the signals your game needs; you never call drain.
func _ready() -> void:
	lingara.token_needed.connect(_on_token_needed)
	lingara.event_received.connect(_on_event)
	lingara.error_raised.connect(func(error: Dictionary) -> void: push_warning(error.message))
	lingara.practice_changed.connect(_on_practice_changed)
	lingara.practice_finished.connect(_on_practice_finished)
	mei.replied.connect(_on_mei_replied)


func _on_event(_id: String, event_type: String, _created_at: String, data: Dictionary) -> void:
	if event_type == "lesson_plan.ready":
		print("a plan is ready: ", data.get("plan_id"))