Server Integration Guide
This guide shows how to integrate LoreMind with popular game server frameworks. Choose the pattern that matches your stack.
Quick Links
- Nakama - Open-source game server
- PlayFab - Microsoft’s game backend
- Custom Node.js - Express/Fastify backend
- Mirror + Custom Backend - Unity networking with REST backend
Server Mediated vs Direct Client
| Consideration | Direct Client | Server Mediated |
|---|---|---|
| Setup complexity | Lower | Higher |
| Player authentication | Device-based | Your auth system |
| Rate limit control | LoreMind defaults | Your custom logic |
| Long-term memory | Not available | Full support (requires playerId) |
| Best for | Prototypes, single-player | Production, multiplayer |
Nakama (TypeScript)
Nakama is an open-source game server supporting realtime multiplayer, matchmaking, and server-side logic. LoreMind integrates via Nakama’s TypeScript runtime.
Register the RPC
// main.ts
const InitModule: nkruntime.InitModule = function(
ctx: nkruntime.Context,
logger: nkruntime.Logger,
nk: nkruntime.Nakama,
initializer: nkruntime.Initializer
) {
initializer.registerRpc('npc_interact', rpcNpcInteract);
logger.info('LoreMind NPC module loaded');
};
const LOREMIND_URL = 'https://loremind.peekgames.dev/api/loremind/v1/npc/interact';
const LOREMIND_API_KEY = 'sk_server_your_key_here'; // Use env var in productionNPC Interaction RPC
const rpcNpcInteract: nkruntime.RpcFunction = function(
ctx: nkruntime.Context,
logger: nkruntime.Logger,
nk: nkruntime.Nakama,
payload: string
): string {
const request = JSON.parse(payload);
const playerId = ctx.userId; // Nakama user ID from authenticated session
// Client sends context from Unity scene - server just forwards it
const body = JSON.stringify({
text: request.message,
entityMindId: request.entityMindId,
playerId: playerId, // Server provides authenticated player ID
memory: { retrieve: true },
context: request.context // Context comes from client (LocationZone, ContextTag, etc.)
});
// Call LoreMind API
const response = nk.httpRequest(LOREMIND_URL, 'post', {
'Content-Type': 'application/json',
'Authorization': `Bearer ${LOREMIND_API_KEY}`
}, body, 30000);
if (response.code !== 200) {
logger.error(`LoreMind error: ${response.code} ${response.body}`);
throw new Error('NPC service unavailable');
}
const result = JSON.parse(response.body);
logger.info(`NPC response for ${playerId}: ${result.metadata?.creditsUsed} credits`);
return JSON.stringify({
response: result.response,
character: result.character
});
};Unity Client (with Nakama)
The client gathers context from the scene using SDK components (LocationZone, ContextTag, etc.) and sends it to the server:
using Nakama;
using Peek.LoreMind;
public class NpcClient : MonoBehaviour
{
private IClient _client;
private ISession _session;
[SerializeField] private LoreMindNPC npc;
public async Task<string> TalkToNpc(string message)
{
// Client builds context from scene state
var payload = new
{
entityMindId = npc.EntityMindId,
message = message,
context = new
{
location = npc.Context.location,
locationDetails = npc.Context.locationDetails,
timeOfDay = npc.Context.timeOfDay,
weather = npc.Context.weather,
nearbyCharacters = npc.Context.nearbyCharacters,
playerAppearance = npc.Context.playerAppearance,
recentEvents = npc.Context.recentEvents
}
};
var response = await _client.RpcAsync(_session, "npc_interact",
JsonConvert.SerializeObject(payload));
var result = JsonConvert.DeserializeObject<NpcResponse>(response.Payload);
return result.response;
}
}Resources:
PlayFab CloudScript
PlayFab is Microsoft’s game backend with CloudScript for server-side logic.
CloudScript Function
// LoreMindNpc.js - PlayFab CloudScript
handlers.NpcInteract = function(args, context) {
const playerId = context.CallerEntityProfile.EntityId;
// Client sends context from Unity scene - server adds playerId and forwards
const requestBody = {
text: args.message,
entityMindId: args.entityMindId,
playerId: playerId, // Server provides authenticated player ID
memory: { retrieve: true },
context: args.context // Context comes from client
};
const response = http.request(
'https://loremind.peekgames.dev/api/loremind/v1/npc/interact',
'POST',
JSON.stringify(requestBody),
'application/json',
{
'Authorization': 'Bearer ' + GetSecretValue('LOREMIND_API_KEY')
}
);
if (response.responseCode !== 200) {
log.error('LoreMind error: ' + response.responseBody);
return { error: 'NPC_UNAVAILABLE' };
}
const result = JSON.parse(response.responseBody);
// Track credits used
server.WritePlayerEvent({
PlayFabId: playerId,
EventName: 'npc_interaction',
Body: {
npcId: args.entityMindId,
creditsUsed: result.metadata?.creditsUsed
}
});
return {
response: result.response,
character: result.character
};
};Unity Client (with PlayFab)
using PlayFab;
using PlayFab.CloudScriptModels;
using Peek.LoreMind;
public class NpcClient : MonoBehaviour
{
[SerializeField] private LoreMindNPC npc;
public async Task<string> TalkToNpc(string message)
{
// Client builds context from scene state
var request = new ExecuteFunctionRequest
{
FunctionName = "NpcInteract",
FunctionParameter = new
{
entityMindId = npc.EntityMindId,
message = message,
context = new
{
location = npc.Context.location,
locationDetails = npc.Context.locationDetails,
timeOfDay = npc.Context.timeOfDay,
nearbyCharacters = npc.Context.nearbyCharacters,
playerAppearance = npc.Context.playerAppearance
}
}
};
var tcs = new TaskCompletionSource<string>();
PlayFabCloudScriptAPI.ExecuteFunction(request,
result => {
var response = JsonConvert.DeserializeObject<NpcResponse>(
result.FunctionResult.ToString());
tcs.SetResult(response.response);
},
error => tcs.SetException(new Exception(error.ErrorMessage))
);
return await tcs.Task;
}
}Resources:
Custom Node.js
For studios with custom backends using Express, Fastify, or similar frameworks.
Express Server
// server.ts
import express from 'express';
import { createClient } from 'redis';
const app = express();
const redis = createClient();
const LOREMIND_API_KEY = process.env.LOREMIND_API_KEY;
app.use(express.json());
// Your auth middleware (Steam, Epic, custom JWT)
app.use('/api/npc', authenticatePlayer);
app.post('/api/npc/interact', async (req, res) => {
const playerId = req.player.id; // From your auth middleware
const { entityMindId, message, context } = req.body; // Context comes from client
// Rate limiting
const rateKey = `npc:rate:${playerId}`;
const count = await redis.incr(rateKey);
if (count === 1) await redis.expire(rateKey, 60);
if (count > 10) {
return res.status(429).json({ error: 'RATE_LIMITED' });
}
// Client sends context from Unity scene - server adds playerId and forwards
const response = await fetch(
'https://loremind.peekgames.dev/api/loremind/v1/npc/interact',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${LOREMIND_API_KEY}`
},
body: JSON.stringify({
text: message,
entityMindId: entityMindId,
playerId: playerId, // Server provides authenticated player ID
memory: { retrieve: true },
context: context // Context comes from client
})
}
);
if (!response.ok) {
console.error('LoreMind error:', await response.text());
return res.status(503).json({ error: 'NPC_UNAVAILABLE' });
}
const result = await response.json();
// Analytics
analytics.track('npc_interaction', {
playerId,
entityMindId,
creditsUsed: result.metadata?.creditsUsed
});
res.json({
response: result.response,
character: result.character
});
});Mirror + Dedicated Server
For games using Mirror networking with a dedicated server that calls external APIs.
Dedicated Server (C#)
// NpcService.cs - runs on dedicated server
public class NpcService
{
private readonly HttpClient _http;
public NpcService(string apiKey)
{
_http = new HttpClient
{
BaseAddress = new Uri("https://loremind.peekgames.dev/api/loremind/v1/"),
Timeout = TimeSpan.FromSeconds(30)
};
_http.DefaultRequestHeaders.Add("Authorization", $"Bearer {apiKey}");
}
public async Task<NpcResponse> GetResponse(NpcRequest request)
{
// Client sends context - server adds playerId and forwards
var payload = new
{
text = request.Message,
entityMindId = request.EntityMindId,
playerId = request.PlayerId, // Server provides authenticated player ID
memory = new { retrieve = true },
context = request.Context // Context comes from client
};
var json = JsonSerializer.Serialize(payload);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _http.PostAsync("npc/interact", content);
var body = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
Debug.LogError($"LoreMind error: {response.StatusCode} {body}");
return null;
}
return JsonSerializer.Deserialize<NpcResponse>(body);
}
}Mirror NetworkBehaviour
// NpcInteraction.cs
using Peek.LoreMind;
public class NpcInteraction : NetworkBehaviour
{
[SerializeField] private LoreMindNPC npc;
private static NpcService _npcService;
[Server]
public static void InitializeService(string apiKey)
{
_npcService = new NpcService(apiKey);
}
// Client calls this Command, sending context from their scene
[Command]
public void CmdTalkToNpc(string message, RuntimeContext context)
{
var player = connectionToClient.identity.GetComponent<Player>();
StartCoroutine(HandleNpcRequest(player, message, context));
}
[Server]
private IEnumerator HandleNpcRequest(Player player, string message, RuntimeContext context)
{
var request = new NpcRequest
{
PlayerId = player.PlayerId,
EntityMindId = npc.EntityMindId,
Message = message,
Context = context // Context came from client
};
var task = _npcService.GetResponse(request);
yield return new WaitUntil(() => task.IsCompleted);
if (task.Result != null)
{
TargetReceiveResponse(connectionToClient, task.Result.response);
}
else
{
TargetReceiveResponse(connectionToClient, "*The character doesn't respond*");
}
}
[TargetRpc]
private void TargetReceiveResponse(NetworkConnection conn, string response)
{
DialogueUI.Instance.ShowResponse(response);
}
}
// Client-side: gather context and send to server
public class NpcInteractionClient : MonoBehaviour
{
[SerializeField] private LoreMindNPC npc;
[SerializeField] private NpcInteraction networkNpc;
public void TalkToNpc(string message)
{
// Client builds context from scene state and sends to server
networkNpc.CmdTalkToNpc(message, npc.Context);
}
}Resources:
API Reference
Request Format
POST https://loremind.peekgames.dev/api/loremind/v1/npc/interact
Authorization: Bearer sk_server_...
Content-Type: application/json
{
"text": "What can you tell me about the dragon?",
"entityMindId": "em_blacksmith_grom",
"playerId": "steam_76561198012345678",
"memory": { "retrieve": true },
"context": {
"location": "Blacksmith Shop",
"timeOfDay": "evening",
"weather": "rainy",
"playerReputation": "trusted ally",
"recentEvents": ["Dragon spotted near village"]
}
}Response Format
{
"success": true,
"response": "The dragon? Aye, I've heard the rumors...",
"character": "Grom the Blacksmith",
"metadata": {
"creditsUsed": 0.45,
"creditsRemaining": 9543.20,
"memoriesRetrieved": 2,
"memoriesStored": 1
}
}Next Steps
- Authentication - Set up API keys
- Long-Term Memory - Player memory across sessions
- Entity Minds - Configure NPC personalities
Last updated on