A common way of engaging and retaining players is to offer them a daily reward for logging into your game each day.
In this example, you’ll learn how to implement a daily reward system using Server Runtime Code.
Registering the RPCs
The daily reward sample defines two RPCs (Remote Procedure Calls) to check eligibility and issue the reward.
The RPCs are then registered to MetaFi events in either main.ts, main.go or main.lua.
// Server
function InitModule(ctx: nkruntime.Context, logger: nkruntime.Logger, nk: nkruntime, initializer: nkruntime.Initializer) {
initializer.registerRpc('canclaimdailyreward_js', rpcCanClaimDailyReward);
initializer.registerRpc('claimdailyreward_js', rpcClaimDailyReward);
logger.info('JavaScript logic loaded.');
}
mplementing the RPCs
The RPCs implement the following logic:
canClaimDailyReward
Get the latest daily reward object from the Storage Engine
Check to see if the last time the user claimed a reward was before 00:00
Return a JSON response indicating if the user can claim a daily reward
claimDailyReward
Get the latest daily reward object from the Storage Engine
Check to see if the last time the user claimed a reward was before 00:00
Update the user’s Wallet
Send a Notification to the user
Update or create the daily reward object in the Storage Engine
Return a JSON response with the number of coins received
Module code
This section is specifically for people using Go or Lua. There is some additional code you will need to include in your daily reward module scripts for each respective language.
Getting the last daily reward object
Let’s take a look at the code for retrieving the latest daily reward object from the Storage Engine.
// Server
function getLastDailyRewardObject(context: nkruntime.Context, logger: nkruntime.Logger, nk: nkruntime, payload: string) : any {
if (!context.userId) {
throw Error('No user ID in context');
}
if (payload) {
throw Error('No input allowed');
}
var objectId: nkruntime.StorageReadRequest = {
collection: 'reward',
key: 'daily',
userId: context.userId,
}
var objects: nkruntime.StorageObject[];
try {
objects = nk.storageRead([ objectId ]);
} catch (error) {
logger.error('storageRead error: %s', error);
throw error;
}
var dailyReward: any = {
lastClaimUnix: 0,
}
objects.forEach(function (object) {
if (object.key == 'daily') {
dailyReward = object.value;
}
});
return dailyReward;
}
Regardless of the language you use, the core logic remains the same.
Check the context to ensure there is a valid user ID
Check the user did NOT pass anything in the payload
Query the storage engine for a daily object in the reward collection
Return the daily reward object or a default one
Checking if the user is eligible to receive a daily reward
// Server
function canUserClaimDailyReward(dailyReward: any) {
if (!dailyReward.lastClaimUnix) {
dailyReward.lastClaimUnix = 0;
}
var d = new Date();
d.setHours(0, 0, 0, 0);
return dailyReward.lastClaimUnix < msecToSec(d.getTime());
}
function msecToSec(n: number): number {
return Math.floor(n / 1000);
}
This function checks the last claim Unix timestamp value of the daily reward object. If it is less than the timestamp for midnight of the previous day, it returns true, otherwise it returns false.
CanClaimDailyReward RPC
With the two helper functions complete it’s time to implement the first of the RPCs. This RPC will return the value of the helper function that checks the user’s eligibility as a JSON object.
// Server
function rpcCanClaimDailyReward(context: nkruntime.Context, logger: nkruntime.Logger, nk: nkruntime, payload: string): string {
var dailyReward = getLastDailyRewardObject(context, logger, nk, payload);
var response = {
canClaimDailyReward: canUserClaimDailyReward(dailyReward)
}
var result = JSON.stringify(response);
logger.debug('rpcCanClaimDailyReward response: %q', result);
return result;
}
ClaimDailyReward RPC
This RPC will ensure the user is eligible to receive the daily reward, update the user’s Wallet, send out a notification and then update the user’s daily reward in the Storage Engine.