Lingara Lingara مستندات راهنماها API کتابخانه‌ها برنامه‌ها ساخت جاسازی نسخهٔ وب
زبان: فارسی

Godot

این صفحه از انگلیسی ترجمه شده است. اگر این دو با هم تفاوت داشته باشند، صفحهٔ انگلیسی درست است. خواندن صفحهٔ انگلیسی

Godot دو زبان ارائه می‌کند و Lingara برای هر کدام یک کیت دارد. یک بازی GDScript از افزونهٔ Lingara استفاده می‌کند. یک بازی Godot با C# از Lingara.Embed استفاده می‌کند، همان بسته‌ای که یک بازی Unity به کار می‌برد. در یک پروژه از یکی یا دیگری استفاده کنید، هرگز از هر دو. زبانه‌های پایین هر کدام را نشان می‌دهند.

نصب

GDScript. افزونهٔ Lingara را از Godot Asset Library نصب کنید، سپس آن را در Project Settings، بخش Plugins، فعال کنید. فعال کردن آن یک autoload به نام Lingara اضافه می‌کند که هر صحنه در /root/Lingara به آن دسترسی دارد.

C#. Lingara.Embed را به‌عنوان ارجاع بسته به پروژهٔ خود اضافه کنید. گرهٔ نمونهٔ پایین، LingaraEmbedNode، بخشی از بسته نیست: کد منبع آن را به پروژهٔ خود اضافه کنید و آن را در یک صحنه قرار دهید.

گرفتن توکن از سرور شما

هیچ‌یک از دو کیت رمزی نگه نمی‌دارد. وقتی هسته به توکن نیاز دارد، آن را از سرور خودتان درخواست می‌کند، که با یک کیت سرور توکنی صادر می‌کند و با توکن و زمان انقضای آن پاسخ می‌دهد.

با افزونه، مسیر توکن سرور خود را در Project Settings تنظیم کنید تا Lingara آن را روی رشتهٔ اجرایی خودش فراخوانی کند. اگر مسیر به نشست بازیکن نیاز دارد، آن را به‌جای این در زمان اجرا تنظیم کنید، زیرا تنظیمات پروژه درون خروجی شما منتشر می‌شوند. یا token_needed را متصل کنید و خودتان به هر درخواست پاسخ دهید. با C#، گرهٔ نمونه هر بار که کلاینت توکنی بخواهد به مسیر توکن شما درخواست می‌فرستد. در هر دو حالت، شکست را با token_unavailable یا TokenUnavailable گزارش دهید تا فراخوانی‌های منتظر فوراً شکست بخورند.

پردازش رویدادها در هر فریم

هسته ورودی/خروجی شبکهٔ خود را روی رشتهٔ اجرایی خودش اجرا می‌کند. autoload افزونه آن را یک بار در هر فریم خالی می‌کند و برای هر رویداد یک سیگنال منتشر می‌کند، پس شما سیگنال‌هایی را که بازی‌تان لازم دارد متصل می‌کنید و هرگز خودتان چیزی را خالی نمی‌کنید. گرهٔ C# در _process خالی می‌کند، حداکثر 256 رویداد در هر فریم، و هر کدام را دوباره به‌صورت یک سیگنال Godot منتشر می‌کند.

همین سیگنال‌ها پاسخ‌های جریانی NPC (نگاه کنید به گفت‌وگوی NPC) و به‌روزرسانی‌های تمرین (نگاه کنید به نتایج تمرین) را حمل می‌کنند.

<PackageReference Include="Lingara.Embed" Version="0.1.0-alpha.6" />
زبان نمونه‌کد

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])
زبان نمونه‌کد

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