Lingara Lingara Ohjeet Oppaat API Kirjastot Sovellukset Rakenna Upotus Verkkosovellus
Kieli: Suomi

Godot

Tämä sivu on käännetty englannista. Jos ne eroavat toisistaan, englanninkielinen sivu on oikea. Lue englanninkielinen sivu

Godot tarjoaa kaksi kieltä, ja Lingaralla on paketti kummallekin. GDScript-peli käyttää Lingara-lisäosaa. Godot C# -peli käyttää pakettia Lingara.Embed, samaa pakettia, jota Unity-peli käyttää. Käytä projektissa jompaakumpaa, älä koskaan molempia. Alla olevat välilehdet näyttävät kummankin.

Asennus

GDScript. Asenna Lingara-lisäosa Godot Asset Librarysta ja ota se sitten käyttöön kohdassa Project Settings, Plugins. Käyttöönotto lisää autoloadin nimeltä Lingara, jonka jokainen kohtaus tavoittaa polusta /root/Lingara.

C#. Lisää Lingara.Embed projektiisi pakettiviittauksena. Alla oleva esimerkkisolmu, LingaraEmbedNode, ei kuulu pakettiin: lisää sen lähdekoodi projektiisi ja sijoita se kohtaukseen.

Hae tunnukset palvelimeltasi

Kumpikaan paketti ei säilytä salaisuutta. Kun ydin tarvitsee tunnuksen, se pyytää sitä omalta palvelimeltasi, joka luo tunnuksen palvelinpaketilla ja vastaa tunnuksella ja sen vanhenemisajalla.

Lisäosaa käyttäessäsi aseta palvelimesi tunnusreitti kohdassa Project Settings, ja Lingara kutsuu sitä omassa säikeessään. Jos reitti tarvitsee pelaajan istunnon, aseta se sen sijaan ajon aikana, koska projektin asetukset kulkevat mukana viennissäsi. Tai yhdistä token_needed ja vastaa itse jokaiseen pyyntöön. C#:lla esimerkkisolmu lähettää pyynnön tunnusreitillesi aina, kun asiakas pyytää tunnusta. Kummassakin tapauksessa ilmoita epäonnistumisesta kutsulla token_unavailable tai TokenUnavailable, jotta odottavat kutsut epäonnistuvat heti.

Käsittele tapahtumat joka ruudunpäivityksessä

Ydin suorittaa verkko-I/O:nsa omassa säikeessään. Lisäosan autoload tyhjentää sen kerran ruudunpäivitystä kohden ja lähettää yhden signaalin tapahtumaa kohden, joten yhdistät pelisi tarvitsemat signaalit etkä koskaan tyhjennä mitään itse. C#-solmu tyhjentää kohdassa _process, enintään 256 tapahtumaa ruudunpäivitystä kohden, ja lähettää kunkin uudelleen Godot-signaalina.

Samat signaalit kuljettavat virtautetut NPC-vastaukset (katso NPC-dialogit) ja harjoittelun päivitykset (katso harjoittelun tulokset).

<PackageReference Include="Lingara.Embed" Version="0.1.0-alpha.6" />
Koodiesimerkin kieli

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])
Koodiesimerkin kieli

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"))