Session Keep-Alive and Duplicate Login Check
This API periodically sends session keep-alive signals to the server and checks for duplicate logins from other devices.
The example code in this document is provided for reference. The callback invocation method using Update() checks every frame, so calling the callback directly within the coroutine may be more efficient. When applying this to your actual project, feel free to modify it according to your project's architecture and coding conventions.
This API uses the service-account.playnanoo.com domain.
API Information
- URL:
https://service-account.playnanoo.com/api/v20240401/alive - Method:
PUT - Authentication Required: Yes
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| platform | string | Required | Platform (e.g., "aos", "ios") |
| device_id | string | Required | Device unique ID |
| device_model | string | Required | Device model name |
| device_os | string | Required | Device OS |
| device_language | string | Required | Device language (e.g., "KO", "EN") |
The Req class for this API inherits from DeviceInfo. All properties of DeviceInfo are automatically included.
Response Data
Success Response
| Field | Type | Description |
|---|---|---|
| Status | string | Status (success: "success") |
Error Response
| Error Code | Description |
|---|---|
| 30006 | DuplicatedDeviceException - Already authenticated on another device |
Unity C# Implementation
CheckAlive Class
using System;
using System.Collections;
using UnityEngine.Networking;
public class CheckAlive
{
static string path = "https://service-account.playnanoo.com/api/v20240401/alive";
[Serializable]
public class Req : DeviceInfo
{
public IEnumerator Send(
Action<Res> onSuccess,
Action<BaseResponse> onError)
{
yield return HttpClient.Send<Req, Res>(
UnityWebRequest.kHttpVerbPUT,
path,
requireToken: true,
body: this,
onSuccess: onSuccess,
onError: onError
);
}
}
[Serializable]
public class Res : BaseResponse
{
public string Status;
}
}
Add to Singleton Manager Class
Add periodic CheckAlive calls and duplicate login detection to your singleton manager.
// Variable declarations
private bool _isDuplicate = false;
private Coroutine _aliveCoroutine;
private Action<bool> _duplicateCallback;
void Update()
{
// If duplicate callback is registered and duplicate is detected, invoke callback
if (_duplicateCallback != null && _isDuplicate)
{
_duplicateCallback.Invoke(true);
_isDuplicate = false; // Reset after one call
}
}
// Start session keep-alive (minimum 300 seconds)
public void CheckAliveStart(int delayTime = 300)
{
if (_aliveCoroutine != null) return;
int timer = delayTime < 300 ? 300 : delayTime;
_aliveCoroutine = StartCoroutine(AliveStatus(timer));
}
// Stop session keep-alive
public void CheckAliveStop()
{
if (_aliveCoroutine != null)
{
StopCoroutine(_aliveCoroutine);
_aliveCoroutine = null;
}
}
IEnumerator AliveStatus(float delayTime)
{
while (true)
{
yield return new WaitForSeconds(delayTime);
if (string.IsNullOrEmpty(DataManager.Instance.AccessToken)) continue;
CheckAlive.Req req = new CheckAlive.Req();
yield return req.Send(
onSuccess: res =>
{
// Success response - not duplicate
},
onError: err =>
{
// 30006: DuplicatedDeviceException - Already authenticated on another device
if (err != null && err.ErrorCode == "30006")
{
_isDuplicate = true;
}
}
);
}
}
// Register duplicate login check callback
public void CheckDuplicate(Action<bool> callback)
{
_duplicateCallback = callback;
}
Usage Example
Basic Usage
void Start()
{
// Start session keep-alive after successful login (every 300 seconds)
YourSingleton.Instance.CheckAliveStart(300);
// Register duplicate login check callback
YourSingleton.Instance.CheckDuplicate(OnCheckAccountDuplicate);
}
void OnCheckAccountDuplicate(bool isDuplicate)
{
if (isDuplicate)
{
Debug.LogError("Duplicate connection has been detected.");
// Force logout processing
// e.g., Navigate to login screen, delete tokens, etc.
}
}
Stop Session Keep-Alive on Logout
public void Logout()
{
// Stop session keep-alive
YourSingleton.Instance.CheckAliveStop();
// Other logout processing...
}
Flow
- After successful login, call
CheckAliveStart(300) - Every 300 seconds, send alive request to server
- Success response: Session maintained
- Error 30006: Logged in from another device →
_isDuplicate = true - Update(): When duplicate detected, invoke registered callback
- Callback processing: Force logout, show notification, etc.
Caution
CheckAliveStart() should be called only once in your entire application.
- Do not call it from multiple scenes or components.
- Multiple calls may cause the server to detect abnormal request patterns, resulting in false positive duplicate login errors (30006).
- Use a singleton pattern to manage session keep-alive from a single location.
- While the implementation internally prevents duplicate calls when a timer is already running, it's best practice to maintain a single call point structurally.