Le point d'entrée du SDK Android HappyOyster est l'objet singleton HappyOyster. À l'exception de initialize, updateToken, attachVideo et sendCommand, toutes les méthodes métier sont des fonctions suspend qui lèvent SDKError en cas d'échec. Couvre les trois modes aventure / réalisation / acting, la configuration requise du modèle et aspectRatio.
Si la coroutine appelante est annulée, le SDK annule la requête HTTP en cours de cet appel, le cas échéant, et propage CancellationException inchangé plutôt que de le convertir en SDKError. L'annulation ne fait pas revenir en arrière une requête que le serveur a déjà acceptée.
Pour le flux d'intégration, l'installation et les bonnes pratiques, voir Guide d'intégration du SDK Android Happy Oyster.
Terminologie de base
Terme | Signification |
|---|---|
token | Clé API de la passerelle Bailian : injectée en tant que jeton Bearer par votre application via |
ticket | Identifiant d'expérience à usage unique : échangé par votre serveur via la plateforme ouverte et livré au client, utilisé uniquement pour un seul |
Exigences environnementales
Élément | Exigence |
|---|---|
minSdk | 24 (Android 7.0) et versions ultérieures |
compileSdk | 36 |
Langage | Kotlin (APIs de coroutine |
ABI |
|
Réseau | Accès Internet public requis |
Concepts de base
Concept | Description |
|---|---|
Monde | Un monde IA, contenant des personnages et des scènes. Créé et géré par votre serveur. |
Travel | Une expérience temps réel unique. Cycle de vie de base : |
Mode |
|
Vidéo en temps réel | Établie et maintenue automatiquement par le SDK après le succès de |
Vue d'ensemble
Méthodes HappyOyster
Méthode | Description |
|---|---|
| Initialise le SDK. La réinitialisation à l'état inactif est prise en charge ; la réinitialisation pendant qu'un Travel est en cours de démarrage, actif ou en cours de fin est rejetée avec |
| Injecte/met à jour la clé API de la passerelle Bailian (jeton Bearer). |
| Démarre une expérience avec un identifiant à usage unique et établit automatiquement la connexion vidéo en temps réel. |
| Met en pause l'expérience de manière asynchrone (réalisation et Acting, et uniquement si l'expérience prend en charge la pause) ; après acceptation, la pause effective est déterminée par |
| Reprend une expérience en pause (réalisation et Acting) ; inclut une temporisation de nouvelle tentative 3× en interne. |
| Rembobine au nombre de secondes spécifié (réalisation uniquement, état |
| Envoie une instruction textuelle pour piloter la narration (réalisation et Acting, |
| Envoie des commandes de contrôle de direction/vue/action (mode aventure, |
| Retourne une |
| Met fin à l'expérience, déconnectant automatiquement la connexion en temps réel et libérant toutes les ressources de session. |
| Chaîne de version du SDK (SemVer), constante au moment de la compilation. |
Événements
Événement | Description |
|---|---|
| Changement d'état de l'expérience (y compris le cycle de vie vidéo en temps réel) ; les valeurs sont définies dans |
| Rappel lorsqu'un flux automatique interne échoue ; une erreur fatale met également fin à l'expérience en cours. |
Types de données clés
Type | Description |
|---|---|
| Configuration d'initialisation du SDK ( |
| État de l'expérience : |
| Mode d'expérience : |
| Modèle de création de monde : |
| Retourné par |
| Paramètre pour |
| Erreur SDK ; contient |
HappyOyster.initialize
Initialise le SDK ; appelez-le avant toute autre API (de préférence dans Application.onCreate). config est obligatoire et doit contenir le apiHost Bailian de votre compte ainsi qu'un model parmi happyoyster-1.0-directing / happyoyster-1.0-acting / happyoyster-1.0-adventure (correspondant à l'entrée Open API). Le SDK construit les URL de requête comme suit :
https://{apiHost}/api/v2/apps/{model}/openapi/v1/{endpoint}
model n'a pas de valeur par défaut : l'omettre lors de la construction de SDKConfig constitue une erreur de compilation. Passer une valeur vide fait que initialize lève de manière synchrone SDKError(100002) ; aucun runtime n'est créé ni remplacé et aucune requête réseau n'est effectuée. Le SDK ne conserve que le contexte de l'application, pas une Activity. La réinitialisation n'est prise en charge que lorsque le runtime précédent est inactif. Pour changer de modèle, réinitialisez avec le nouveau model pendant l'inactivité ; si un Travel démarre, est actif ou se termine, attendez d'abord endTravel().
Remarque
Avis de conformité régionale :- Pour garantir la conformité aux exigences légales et relatives aux données applicables, si vos utilisateurs cibles incluent des utilisateurs américains, vous devez configurer un API Host de région américaine lors de l'initialisation du SDK pour les services fournis à ces utilisateurs.
- Les développeurs sont responsables de la configuration correcte et assument la responsabilité correspondante en vertu de la loi applicable en cas de non-respect de cette exigence.
Signature
fun initialize(context: Context, config: SDKConfig)
Paramètres
Champ | Type | Obligatoire | Description |
|---|---|---|---|
|
| Oui | Il est recommandé de passer |
|
| Oui | Configuration du SDK ; doit contenir les paramètres obligatoires sans valeur par défaut |
Retourne
Aucune valeur de retour.
Erreurs
code | Description |
|---|---|
|
|
| Réinitialisation rejetée car un Travel est en cours de démarrage, actif ou en cours de fin. Attendre |
HappyOyster.updateToken
Injecte/met à jour la clé API de la passerelle Bailian. Thread-safe et appelable après initialize ; le SDK utilise le dernier jeton pour startTravel et les requêtes ultérieures de contrôle d'expérience. Le ticket est un identifiant d'expérience à usage unique et ne doit pas être confondu avec le jeton Bearer.
Signature
fun updateToken(token: String)
Paramètres
Champ | Type | Obligatoire | Description |
|---|---|---|---|
|
| Oui | La clé API de la passerelle Bailian, utilisée comme jeton Bearer. |
Retourne
Aucune valeur de retour.
Erreurs
code | Description |
|---|---|
| Le SDK n'a pas été initialisé. |
HappyOyster.startTravel
Démarre une expérience avec un ticket à usage unique. En cas de succès, le SDK établit automatiquement la connexion vidéo en temps réel et lance l'interrogation interne de l'état, l'état étant exposé via onStatusChanged.
ticketest un identifiant à usage unique et est considéré comme consommé dès que l'appel est effectué.- Un seul Travel simultané est autorisé par application à tout moment ; appeler à nouveau alors qu'une expérience est en cours lève
SDKError(103004), avecSDKError.rawne contenant qu'un résumé expurgé du ticket actuellement actif pour le diagnostic, jamais le ticket complet. - Passer facultativement
maxExperienceTimeSecpour plafonner la durée maximale de cette expérience (exploration de monde / mode aventure uniquement) ; voir Paramètres.
StartTravelData.creationModel (CreationModelValue, par défaut CreationModelValue.Simple) : indique le modèle de création du monde, c'est-à-dire la manière dont le contenu du script est géré. simple (par défaut) est un monde ordinaire piloté par instructions ; les mondes d'exploration de monde sont également normalisés à cette valeur. scriptlist est un monde ScriptList structuré — son contenu de script n'est pas accessible via ce SDK. En mode ScriptList (creationModel == CreationModelValue.ScriptList), l'appel à sendInstruct est rejeté et lève SDKError(103002). Ce champ a pour valeur par défaut simple.
StartTravelData.aspectRatio (String?) : le rapport d'aspect de lecture attribué par le serveur à cette session, sous forme de chaîne width:height. Non-null uniquement pour Acting—"9:16" (portrait, valeur par défaut côté serveur à la création) ou "16:9" (paysage) ; null pour les modes exploration de monde et réalisation, et null lorsque le serveur ne signale pas la valeur. Le SDK conserve les valeurs inconnues telles quelles (il ne les réduit pas à null), de sorte que les hôtes doivent traiter toute valeur non reconnue comme null et revenir à leur propre orientation par défaut ; le SDK lui-même n'utilise pas ce champ.
La valeur est fournie avec la valeur de retour de startTravel() : dimensionnez le conteneur de lecture à partir de celle-ci après le retour de startTravel() et avant d'appeler attachVideo() et d'ajouter la vue retournée à votre mise en page — le SDK ne commence à lier le flux distant pour le rendu que lorsque l'hôte attache la vue, donc décider de l'orientation à ce moment précède toujours la première image rendue (cependant, il n'est pas garanti que cela se produise avant que le SDK ne rejoigne la salle temps réel). La vue distante est liée avec un mode de rendu clip-to-fill, de sorte qu'un conteneur dont l'orientation ne correspond pas à cette valeur recadre l'image au lieu d'ajouter des bandes noires.
Signature
suspend fun startTravel(ticket: String): StartTravelData
suspend fun startTravel(ticket: String, maxExperienceTimeSec: Int?): StartTravelData
Paramètres
Champ | Type | Obligatoire | Description |
|---|---|---|---|
|
| Oui | L'identifiant d'expérience à usage unique, fourni par votre serveur. |
|
| Non | Durée maximale de cette expérience en secondes ; le monde termine la session automatiquement une fois cette durée atteinte. S'applique uniquement au mode exploration de monde (aventure) ; ignoré en mode réalisation (réalisation temps réel) et Acting. Les valeurs autorisées sont configurées côté serveur, actuellement |
Retourne
Renvoie StartTravelData, contenant les métadonnées de l'expérience (mode, version, creationModel, aspectRatio, etc.). Inspectez les métadonnées renvoyées avant de décider comment interagir (par ex., travel.mode, travel.creationModel, travel.version) ; pour le mode Acting, dimensionnez également le conteneur de lecture à partir de travel.aspectRatio.
Erreurs
code | Description |
|---|---|
|
|
|
|
| Paramètre invalide (par ex. |
| Monde non prêt |
| Allocation de ressources / échec du service interne |
| Un Travel est déjà en cours de démarrage, actif ou en cours de fin, ou |
HappyOyster.pauseTravel / HappyOyster.resumeTravel
Mettre en pause / reprendre l'expérience. Le SDK ne gère qu'un seul Travel à la fois ; encryptedTravelId est obtenu en interne par le SDK et n'a pas besoin d'être passé par l'appelant.
Préconditions :
pauseTravel: appelable uniquement en réalisation ou Acting et lorsque l'état estrunning.resumeTravel: appelable uniquement en réalisation ou Acting et lorsque l'état estpaused.
Toutes les expériences ne prennent pas en charge la pause / la reprise : l'expérience doit rapporter l'identifiant de version requis par son mode (StartTravelData.version—storyV2 pour directing, actingV2 pour Acting ; comparé en ignorant les espaces environnants et la casse des lettres), sinon pauseTravel et resumeTravel renvoient tous deux 103002. Le mode Acting prend en charge la pause / la reprise mais ne prend pas en charge rewindTravel.
La pause est asynchrone (important) : pauseTravel est une opération « lourde » — un appel réussi (retour de la méthode) signifie seulement que la pause a été acceptée ; à ce moment-là, l'expérience n'est pas encore réellement en pause. La pause n'est effective que lorsque le rappel onStatusChanged signale paused. Par conséquent, pilotez la machine à états de l'hôte et le contrôle des appels depuis ce rappel : entre « l'appel à pauseTravel » et « la réception du rappel paused », vous pouvez marquer l'état local comme pausing ; ce n'est qu'après avoir reçu paused que vous devez autoriser le lancement de resumeTravel ou rewindTravel. Ne considérez pas le retour de pauseTravel comme une mise en pause effective.
Gestion de la connexion en temps réel : après une pause effective, le SDK déconnecte la connexion en temps réel ; lors de la reprise, le SDK rejoint automatiquement et restaure l'image en utilisant l'identifiant fourni lors du même startTravel, sans intervention de l'hôte requise.
Barrière ordonnée de démantèlement de pause : après l'émission de Paused, le démantèlement de la salle temps réel côté serveur peut encore subir un léger délai. Le SDK établit une fenêtre de stabilisation de 3 secondes à partir de la confirmation de la pause. Si resumeTravel ou rewindTravel est appelé dans cette fenêtre, l'appel de suspension attend d'abord de manière non bloquante le temps restant, puis envoie la requête API de réouverture de la salle. Aucun délai n'est ajouté si 3 secondes se sont déjà écoulées naturellement. Cela empêche un démantèlement tardif de la pause de fermer la salle nouvellement ouverte et de faire apparaître 105001.
Nouvelle tentative d'API de reprise : resumeTravel effectue en interne jusqu'à 3 tentatives en cas d'échec (backoff de 1 s / 2 s / 3 s) pour gérer une indisponibilité temporaire du service après une pause ; ce n'est que si les 3 tentatives échouent que l'erreur est propagée vers le haut.
Remarque⚠️ Remarque : Il est recommandé que l'hôte attende jusqu'à 3 s après le retour réussi de resumeTravel avant de lancer à nouveau pauseTravel, afin d'éviter des basculements trop fréquents. Il s'agit d'une recommandation de temporisation d'appel côté hôte et elle n'est pas imposée par le SDK lui-même. Voir Guide d'intégration du SDK Android Happy Oyster pour plus de détails.
Signature
suspend fun pauseTravel(): TravelStateData
suspend fun resumeTravel(): TravelStateData
Paramètres
Aucun paramètre.
Retourne
Retourne TravelStateData (contient encryptedTravelId, status).
Erreurs
code | Description |
|---|---|
| Aucune expérience active |
| État non autorisé, ou cette expérience ne prend pas en charge la pause / reprise |
| Appelé en mode aventure ; seuls les modes réalisation et Acting prennent en charge la pause / reprise |
HappyOyster.rewindTravel
Rembobine au nombre de secondes spécifié. encryptedTravelId est obtenu en interne par le SDK.
Prérequis : appelable uniquement en mode directing et lorsque l'état est paused (le rembobinage n'est pas autorisé à l'état running). Toutes les expériences directing ne prennent pas en charge le rembobinage, et celles non prises en charge renvoient 103002. Après un rembobinage réussi, l'expérience reprend automatiquement et le SDK reconnecte automatiquement le RTC, sans intervention de l'hôte requise.
Le mode Acting n'a aucune capacité de rembobinage : l'appeler en mode Acting est rejeté localement par le SDK avec 103003, et aucune requête n'est émise. En mode Acting, les hôtes doivent masquer le point d'entrée du rembobinage plutôt que de simplement désactiver le bouton.
Signature
suspend fun rewindTravel(rewindToSec: Double): RewindTravelData
Paramètres
Champ | Type | Obligatoire | Description |
|---|---|---|---|
|
| Oui | Le nombre cible de secondes pour le rembobinage, qui doit être un multiple de 4 (par ex., 4, 8, 12). Une valeur non multiple de 4 est arrondie par le serveur au multiple inférieur de 4 le plus proche (par ex., passer 7 donne 4). |
Retourne
Retourne RewindTravelData (contient encryptedTravelId, status, resumedAtSec), où resumedAtSec est le nombre de secondes auquel le serveur a effectivement rembobiné (déjà arrondi à l'entier inférieur vers un multiple de 4).
Erreurs
code | Description |
|---|---|
| Aucune expérience active |
| L'état n'est pas |
| Appelé en mode aventure ou Acting ; seul le mode réalisation prend en charge le rembobinage |
HappyOyster.sendInstruct
Envoie une instruction textuelle pour piloter la narration. Valide en mode directing ou Acting et lorsque l'état de l'expérience est running ou paused. encryptedTravelId est obtenu en interne par le SDK.
Comportement à l'état paused : le SDK ne reprend pas automatiquement lorsqu'il est en pause ; l'instruction est envoyée directement. C'est à l'hôte de décider s'il doit appeler resumeTravel avant d'envoyer l'instruction.
Signature
suspend fun sendInstruct(content: String): SendInstructData
Paramètres
Champ | Type | Obligatoire | Description |
|---|---|---|---|
|
| Oui | Le contenu de l'instruction textuelle à envoyer. |
Retourne
Retourne SendInstructData (contient encryptedTravelId, content, accepted).
Erreurs
code | Description |
|---|---|
| Aucune expérience active ( |
| Le mode actuel n'est ni réalisation ni Acting (appelé en mode aventure) |
| L'état de l'expérience n'est ni |
| Blocage de modération de contenu |
| Le Travel n'existe pas |
HappyOyster.sendCommand
Envoie des commandes de contrôle de direction/vue/action. Valide uniquement en mode aventure et lorsque running.
- Appeler sur le thread principal ; un appel hors thread principal lève synchrone
IllegalStateException. - L'absence d'expérience active signale
103001. - Un appel en dehors du mode aventure (réalisation / Acting) signale
103003. - Un état non autorisé signale
103002. - La liaison montante est établie via un flux audio silencieux (le SDK n'enregistre ni ne téléverse d'audio réel) ;
RECORD_AUDIOn'est pas une condition stricte pour le DataChannel, mais le déclarer et l'accorder est recommandé pour la compatibilité entre appareils (voir Guide d'intégration du SDK Android Happy Oyster § Installation · Permissions).105004signifie que le canal temps réel n'est pas prêt ou qu'un envoi a échoué (par exemple, une interruption de connexion DataChannel ou une anomalie du canal temps réel) ; cela n'est pas déclenché directement par une permission manquante.
Limitation intégrée de 42 ms (24 fps, la dernière valeur l'emporte) : sendCommand limite en interne les écritures DataChannel par intervalles de 42 ms (environ 24 fps). La validation de l'état s'exécute de manière synchrone et immédiate, levant immédiatement une exception en cas d'appel illégal ; l'écriture DataChannel réelle est limitée de manière asynchrone. Si au moins 42 ms se sont écoulées depuis l'envoi précédent, la commande est envoyée immédiatement (y compris pour le premier appel). Sinon, la commande en attente est remplacée par la dernière valeur et envoyée une seule fois à la fin de la fenêtre de 42 ms en cours. Plusieurs appels dans une même fenêtre produisent donc une seule écriture sur le réseau contenant la dernière valeur. L'hôte peut appeler sendCommand à la fréquence d'images du jeu sans implémenter son propre limiteur de débit.
Échecs d'envoi pendant la limitation : le vidage limité est asynchrone, et un échec d'envoi ne peut pas être levé à l'appelant — l'erreur est exposée via le callback onError (non fatal, 105004). Les commandes en attente sont abandonnées lorsque la session se termine (elles ne seront pas envoyées après la fin de la session).
Champs et valeurs de AdventureCommand :
Champ | Sémantique | Valeurs |
|---|---|---|
| Mouvement : avant/gauche/arrière/droite/diagonale/inactif |
|
| Vue : haut/bas/gauche/droite/diagonale/none |
|
| Interaction : saut/attaque/accroupissement/sprint/none |
|
Les trois champs sont des groupes de commandes mutuellement exclusifs et indépendants. Le déplacement/la vue en diagonale utilise une valeur combinée unique (par exemple, avant+gauche est W_A, et non W et A simultanés dans le même champ). Chaque appel doit porter l'état actuel complet.
Utiliser l'intervalle de 42 ms comme modèle mental :
-
Action ponctuelle (par exemple, appuyer pour sauter/attaquer ou faire un pas) : appeler une seule fois. Le SDK l'envoie dans l'intervalle le plus proche ; aucun appel répété ni commande
Nonede suivi n'est requis.// Jump once HappyOyster.sendCommand(AdventureCommand("None", "None", "Jump")) -
Action maintenue (par exemple, continuer à se déplacer ou à tourner) : appeler à chaque frame tant que maintenu. Le SDK émet environ une commande toutes les 42 ms. Au relâchement, envoyer explicitement une commande contenant
Nonepour réinitialiser l'état. Le SDK ne génère jamais de commande de réinitialisation automatiquement.// While held: call from the host's frame loop HappyOyster.sendCommand(AdventureCommand("W", "None", "None")) // On release: explicitly reset once HappyOyster.sendCommand(AdventureCommand("None", "None", "None"))
Signature
fun sendCommand(command: AdventureCommand)
Paramètres
Champ | Type | Obligatoire | Description |
|---|---|---|---|
|
| Oui | Un objet de commande contenant les trois champs |
Retourne
Aucune valeur de retour (synchrone). La validation de l'état s'exécute de manière synchrone et immédiate ; l'écriture DataChannel est limitée de manière asynchrone.
Erreurs
code | Description |
|---|---|
| Aucune expérience active |
| État non autorisé |
| Appelé en dehors du mode aventure (réalisation / Acting) |
| Échec d'envoi différé par limitation de débit (non fatal, rappel asynchrone) |
HappyOyster.attachVideo
Retourne une SurfaceView pour la lecture, que vous ajoutez à votre mise en page ; le SDK effectue en interne la liaison de rendu avec le flux distant.
- Appeler sur le thread principal ; un appel hors thread principal lève synchrone
IllegalStateException. - Le SDK ne détient qu'une référence faible à la Vue retournée et libère la liaison de rendu lorsque l'expérience se termine ; vous devez retirer la Vue de la mise en page vous-même.
- Pour les expériences Acting, dimensionnez le conteneur de lecture à partir de
StartTravelData.aspectRatioavant d'ajouter la vue retournée à votre mise en page : le rendu est lié avec un mode clip-to-fill, de sorte qu'une orientation inadaptée recadre l'image (voirstartTravel).
Signature
fun attachVideo(): SurfaceView
Paramètres
Aucun paramètre.
Retourne
Retourne une SurfaceView ; ajoutez-la à votre mise en page pour lire la vidéo en temps réel.
Erreurs
code | Description |
|---|---|
| SDK non initialisé |
HappyOyster.endTravel
Met fin à l'expérience. encryptedTravelId est obtenu en interne par le SDK. Après un appel réussi (ou une sortie anormale), le SDK déconnecte automatiquement la connexion en temps réel, arrête l'interrogation interne et libère toutes les ressources de session, et le ticket actuel est invalidé en même temps.
Signature
suspend fun endTravel(): EndTravelData
Paramètres
Aucun paramètre.
Retourne
Retourne EndTravelData (contient encryptedTravelId, status, endedAt, durationSec).
Erreurs
code | Description |
|---|---|
| SDK non initialisé |
| Aucune expérience active |
HappyOyster.VERSION
Retourne la chaîne de version du SDK (SemVer), telle que "x.y.z". Cette valeur est une constante au moment de la compilation injectée par la propriété Gradle VERSION_NAME ; elle peut être lue en toute sécurité sans appeler initialize au préalable.
Signature
val VERSION: String
Retourne
La chaîne de version du SDK, telle que "x.y.z".
Log.d("MyApp", "SDK version: ${HappyOyster.VERSION}")
Écoute des événements
interface HappyOysterListener {
fun onStatusChanged(status: TravelStatusValue) {}
fun onError(error: SDKError) {}
}
fun addListener(listener: HappyOysterListener)
fun removeListener(listener: HappyOysterListener)
Les événements du SDK constituent un canal de push proactif vers vous, utilisé pour signaler des situations non déclenchées par vos propres appels explicites (par ex., des problèmes dans la connexion en temps réel ou l'interrogation d'état que le SDK maintient automatiquement).
Événement | Description |
|---|---|
| Changement d'état de l'expérience (y compris le cycle de vie vidéo en temps réel) ; les valeurs sont définies dans |
| Rappel lorsqu'un flux automatique interne échoue ; une erreur fatale met également fin à l'expérience en cours (voir la section des codes d'erreur). |
Remarque⚠️ Remarque : addListener / removeListener doivent être appelés après initialize ; les appeler avant l'initialisation lève SDKError(100001). Il est recommandé d'enregistrer les écouteurs immédiatement après le retour réussi de HappyOyster.initialize(...).
Modèles de données
// Configuration
data class SDKConfig(
// Required: your account's Bailian API Host (e.g., llm-xxxx.ap-southeast-1.maas.aliyuncs.com), copied from the API Key page in the Bailian console;
// must belong to the same account/region as the injected API Key, otherwise the gateway returns AccessDenied.
val apiHost: String,
// Required with no default: the complete HappyOyster model name and version (e.g., happyoyster-1.0);
// refer to the official Bailian HappyOyster model documentation for available values.
val model: String,
// Minimum level for the built-in Logcat sink (does not affect logHandler); defaults to INFO, which
// includes the lifecycle anchors (initialize, Travel start/status/end, RTC connect/first-frame) needed
// to reconstruct a session timeline. Drop to WARN for errors-only, or raise to DEBUG/VERBOSE to diagnose.
val logLevel: LogLevel = LogLevel.INFO,
// Timeout for RTC join (timeout triggers 105002), first-frame wait (timeout triggers 105003), and SDK gateway HTTP signaling calls (timeout triggers 105005);
// the two RTC phases are serial, so the worst-case wait is 2×callbackTimeoutMs (60s).
val callbackTimeoutMs: Long = SDKConfig.DEFAULT_CALLBACK_TIMEOUT_MS, // 30_000ms
// Whether the SDK writes its own logs to Android Logcat (tag HappyOysterSDK); defaults to false (silent).
val logcatEnabled: Boolean = false,
// Host log callback; receives the full stream of SDK LogRecords, unaffected by logLevel; defaults to null.
val logHandler: HappyOysterLogHandler? = null,
) {
companion object {
/** Default callback timeout (milliseconds). Public constant, usable for comparison or display. */
const val DEFAULT_CALLBACK_TIMEOUT_MS: Long = 30_000
}
}
enum class LogLevel { VERBOSE, DEBUG, INFO, WARN, ERROR, NONE }
// Host log sink; injected via SDKConfig.logHandler; full firehose, unaffected by logLevel.
fun interface HappyOysterLogHandler {
fun onLog(record: LogRecord)
}
// SDK structured log record; delivered to HappyOysterLogHandler; contains no sensitive values (Bearer /
// ticket / RTC token, RTC identity fields, and media URLs are all redacted). travelId (encryptedTravelId)
// is kept in full so it can be correlated with server-side session logs.
data class LogRecord(
val level: LogLevel,
val tag: String, // fixed as "HappyOysterSDK"
val message: String, // readable log line (includes event name and details)
val throwable: Throwable?,
val timestampMs: Long, // epoch milliseconds at emission time
)
// Status and mode (unknown values are preserved to allow service extension)
@JvmInline value class TravelStatusValue(val rawValue: String) {
companion object {
val Init = TravelStatusValue("init")
val Pending = TravelStatusValue("pending")
val Running = TravelStatusValue("running")
val Paused = TravelStatusValue("paused")
val Failed = TravelStatusValue("failed")
val Completed = TravelStatusValue("completed")
}
}
@JvmInline value class ModeValue(val rawValue: String) {
companion object {
val Adventure = ModeValue("adventure")
val Directing = ModeValue("directing")
val Acting = ModeValue("acting")
}
}
@JvmInline value class CreationModelValue(val rawValue: String) {
companion object {
val Simple = CreationModelValue("simple") // default; prompt/instruct-driven, world-exploration worlds normalize to this
val ScriptList = CreationModelValue("scriptlist") // structured ScriptList world; sendInstruct not supported (throws 103002)
}
}
// startTravel return
data class StartTravelData(
val encryptedTravelId: String, // identifier for subsequent control interfaces
val encryptedWorldId: String,
val mode: ModeValue, // adventure / directing / acting
val creationModel: CreationModelValue = CreationModelValue.Simple, // world creation model; Simple (default) is instruct-driven, ScriptList does not support sendInstruct
val playUrl: String?,
val firstFrame: String?, // first-frame image address, may be produced asynchronously
val bgmUrl: String?,
val version: String, // world version identifier; Acting is actingV2
val aspectRatio: String? = null, // Acting only: 9:16 / 16:9; other modes null. Use to set player orientation
val maxExperienceTimeSec: Int? = null, // server-reported max experience time (seconds); non-null only for adventure; null for directing / acting
)
// Control interface parameters and returns
data class AdventureCommand(
val translation: String, // movement: forward/left/back/right/diagonal (W_A, …)/idle
val rotation: String, // view: up/down/left/right/diagonal (Mouse_Up_Left, …)/none
val interaction: String, // interaction: jump/attack/squat/sprint/none
)
data class TravelStateData(val encryptedTravelId: String, val status: TravelStatusValue)
data class RewindTravelData(val encryptedTravelId: String, val status: TravelStatusValue, val resumedAtSec: Double)
data class EndTravelData(val encryptedTravelId: String, val status: TravelStatusValue, val endedAt: String, val durationSec: Int)
data class SendInstructData(val encryptedTravelId: String, val content: String, val accepted: Boolean)
// Error (identified by code; the original information is in raw)
// Note: SDKError is not a data class (no copy()/destructuring), it is a plain class.
class SDKError(val code: Int, val raw: Any? = null) : Exception("Happy Oyster SDK error: $code")
Codes d'erreur
Les erreurs sont identifiées par un code numérique. Le SDK transmet les codes d'erreur métier que l'appelant peut gérer (couramment 4xxxxx / 5xxxxx) ; les codes d'erreur locaux du SDK sont 1xxxxx.
Codes d'erreur métier (courants)
code | Signification | Traitement suggéré |
|---|---|---|
| Paramètre invalide (enum invalide, etc.) | Vérifier les paramètres de la requête ou la version du SDK |
|
| Demander à votre serveur de réémettre un identifiant |
|
| L'identifiant est à usage unique ; le réémettre |
| Le monde n'existe pas, a été supprimé ou n'appartient pas au développeur actuel (y compris un monde déjà supprimé dans l'identifiant | Resélectionner un Monde valide |
| Monde non prêt | Attendre que le monde soit prêt avant de commencer |
| Violation de contenu d'entrée (modération de contenu) ; s'applique aux instructions textuelles de | Modifier le contenu saisi et réessayer |
| Spécification de service non activée pour ce compte | Ne pas réessayer en tant que capacité pleine ; changer pour une spécification activée ou demander l'activation |
| Configuration de capacité temporairement indisponible | Réessayer plus tard |
| La ressource Travel n'existe pas ou l'ID n'appartient pas au compte actuel | Redémarrer le Travel |
| La requête est en conflit avec l'état actuel de la ressource | Vérifier l'état du Travel |
| Limite de concurrence SKU atteinte | Réessayer après la fin d'une session existante (ne pas confondre avec |
| Aucune capacité disponible actuellement | Réessayer plus tard |
| Allocation de ressources d'inférence / échec du service interne | Réessayer plus tard |
| Erreur système interne | Réessayer plus tard / signaler |
Codes d'erreur locaux côté client
code | Signification | Fatal ? | Traitement suggéré |
|---|---|---|---|
| Appelé avant l'initialisation du SDK | Appel rejeté |
|
|
| Échec de l'initialisation | Passer un nom de modèle complet et une version non vides, puis appeler |
| Clé API de la passerelle Bailian non injectée | Non | Réessayer après |
| Clé API Bearer de la passerelle Bailian expirée ou rejetée. Remarque : il s'agit de la clé Bearer de la passerelle, et non du ticket à usage unique ; les erreurs d'identification au niveau du ticket sont identifiées par des codes serveur à six chiffres (par ex., | Non | Obtenir à nouveau la clé Bearer et |
| Aucune expérience active actuellement | Appel rejeté |
|
| L'état actuel ne permet pas cette opération (incluant : incompatibilité d'état ; l'expérience ne prend pas en charge la pause / la reprise / le rembobinage pour | Appel rejeté | Vérifier l'état et le mode de l'expérience ; |
| Incompatibilité de mode : | Appel rejeté | Vérifier si le mode actuel correspond à l'exigence de l'interface |
| Un appel | Appel rejeté | Attendre |
| Échec de la connexion en temps réel | Oui | Terminer et redémarrer |
| Expiration du délai de jonction en temps réel | Oui | Terminer et redémarrer |
| Délai d'attente dépassé pour la première image vidéo | Oui | Terminer et redémarrer |
| Canal en temps réel non prêt / échec de l'envoi | Non | Confirmez que le canal en temps réel est prêt et réessayez |
| Un appel de signalisation HTTP de la passerelle SDK n'a pas répondu dans les | Non | Une nouvelle tentative est recommandée, ou augmenter |
| Fin automatique en l'absence de flux : la première image n'est jamais arrivée, ou le flux a été interrompu pendant l'exécution et n'a pas récupéré avant le délai d'attente. Le SDK met fin proactivement à l'expérience en cours | Oui | Terminer et redémarrer |
| Erreur réseau locale | Non | Réessayable |
| Échec de l'analyse de la réponse | Non | Levé à l'appelant lorsqu'il est déclenché par un appel explicite ; lors de l'interrogation d'état interne, seul |
| Le service en amont a renvoyé une réponse d'erreur non reconnaissable ; le SDK a conservé les informations d'origine dans | Non | Réessayable ; si le problème persiste, vérifiez la disponibilité du service ainsi que |
| SDK désactivé à distance (arrêt complet ou version trop ancienne) ; la raison se trouve dans | Oui (lorsque le SDK détecte un résultat de désactivation, il met automatiquement fin à l'expérience en cours ; vous pouvez redémarrer après récupération) | Informer l'utilisateur en fonction de la raison dans |
Remarque« Fatal ? » fait spécifiquement référence à si cela déclenche l'arrêt automatique de l'expérience en cours par le SDK. Les erreurs de validation synchrones telles que 100001/100002/103xxx rejettent/lèvent uniquement pour cet appel (100002 fait directement échouer l'initialisation) et ne mettent pas fin à une expérience.
Erreurs fatales vs non fatales :
- Erreurs fatales : le SDK termine automatiquement l'expérience en cours (déconnecte la connexion temps réel, libère les ressources, appelle
endTravel) et la signale viaonError; l'hôte doit nettoyer l'état de l'expérience actuelle et autoriser un redémarrage. Il existe quatre catégories : ① l'interrogation interne de statut lit un état d'expériencefailed(par exemple, échec d'inférence500001) ; ② erreur fatale de connexion temps réel (105001/105002/105003) ; ③ fin automatique en l'absence de flux (105006: la première image n'est jamais arrivée, ou le flux a été interrompu pendant l'exécution et n'a pas récupéré avant le délai d'attente, et le SDK y met fin proactivement) ; ④ SDK désactivé à distance (108001). - Erreurs non fatales : elles ne terminent pas l'expérience et sont uniquement signalées via
onError; vous pouvez appelerupdateTokenà nouveau ou attendre que le service ou le canal temps réel se rétablisse avant de continuer. Un seul échec de requête de l'interrogation interne de statut elle-même (réseau106001, analyse106002, anomalie en amont106003, erreur métier5xxxxx) entre dans cette catégorie — l'interrogation se poursuit au cycle suivant, et ce n'est que lorsqu'elle lit un statutfailedqu'elle termine l'expérience ; l'échec d'authentification101001et l'échec d'envoi asynchrone avec limitationsendCommand105004sont également non fatals. Remarque : le SDK n'envoie jamais de charge utile keepalive de sa propre initiative sur le canal temps réel, donc105004ne peut pas apparaître lorsque la session est inactive — cela ne peut être déclenché que par votre propre appel àsendCommand.