PlaytimeReward

  • community
  • economy
  • by limon
  • Takaro v0.0.24
  • all
Version
View export JSON

Limon_RewardPlaytime: Reward Players for Their Dedication

This module automatically rewards players for their time spent on your server, encouraging longer play sessions and regular participation.

Key Features:

Cronjob Configuration

  • Timed Rewards: Configure how often players receive rewards while online. The reward interval and cronjob schedule should be synchronized for best results (e.g., set both to 5 minutes).
  • Currency Rewards: Automatically grant in-game currency to active players based on your configuration.
  • Random Item Rewards: Set up a customizable chance to give random items from your configured list.

Reward Configuration

  • Role-Based Rewards: Use permission overrides to give VIP players increased rewards or higher chances for items.
  • Playtime Tracking: The included /playtime command lets players check their current session time, server playtime, and community-wide playtime statistics.
  • Customizable Messages: Personalize the reward notifications with dynamic placeholders for playtime, currency, and items.

User Message

Configuration Options:

  • rewardInterval: Set how frequently rewards are given (should match cronjob schedule)
  • baseReward: Amount of currency to award each interval
  • possibleItems: List of items that can be randomly awarded
  • itemChance: Percentage chance (1-100) to receive an item
  • rewardMessage: Customize notification with placeholders {minutes}, {currency}, {item}

Permission Options:

  • PLAYTIME_REWARD_OVERRIDE: Customize currency rewards for specific roles
  • PLAYTIME_ITEM_CHANCE_OVERRIDE: Modify item drop chances for specific roles

Perfect for servers looking to increase player retention and reward loyal community members.

Configuration 5

Settings you fill in when installing the module on a server.

SettingTypeDefaultDescription
rewardInterval rewardInterval number 1200000 Give rewards every X minutes of playtime
baseReward baseReward number 0 Currency given per reward
possibleItems Items array Items that can be randomly awarded
itemChance itemChance string Chance to receive an item (1-100)
rewardMessage rewardMessage string "\"You've been online for {minutes} minutes! Reward: {currency} currency and {item}. " Message with placeholders {minutes}, {currency}, {item}
Raw config schema
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "required": [],
  "additionalProperties": false,
  "properties": {
    "rewardInterval": {
      "title": "rewardInterval",
      "description": "Give rewards every X minutes of playtime",
      "default": 1200000,
      "x-component": "duration",
      "type": "number"
    },
    "baseReward": {
      "title": "baseReward",
      "description": "Currency given per reward",
      "default": 0,
      "type": "number"
    },
    "possibleItems": {
      "title": "Items",
      "description": "Items that can be randomly awarded",
      "x-component": "item",
      "type": "array",
      "uniqueItems": true,
      "items": {
        "type": "object",
        "title": "Item",
        "properties": {
          "item": {
            "type": "string",
            "title": "Item"
          },
          "amount": {
            "type": "number",
            "title": "Amount"
          },
          "quality": {
            "type": "string",
            "title": "Quality"
          }
        }
      }
    },
    "itemChance": {
      "title": "itemChance",
      "description": "Chance to receive an item (1-100)\n",
      "type": "string",
      "minLength": 1,
      "maxLength": 100
    },
    "rewardMessage": {
      "title": "rewardMessage",
      "description": "Message with placeholders {minutes}, {currency}, {item}",
      "default": "\"You've been online for {minutes} minutes! Reward: {currency} currency and {item}. ",
      "type": "string"
    }
  }
}
Raw UI schema
{}

Commands 1

Chat commands players trigger in game.

  • playtime

    Check your playtime statistics

    Command source
    // commands/playtime.js
    
    import { takaro, data } from '@takaro/helpers';
    
    async function main() {
        const { player, gameServerId, pog } = data;
    
        // Search for the most recent player connected event for this player
        const connectEvents = await takaro.event.eventControllerSearch({
            filters: {
                eventName: ['player-connected'],
                gameserverId: [gameServerId],
                playerId: [player.id]
            },
            sortBy: "createdAt",
            sortDirection: "desc",
            limit: 1
        });
    
        // Format session playtime
        let sessionTimeString = "No data available";
        if (connectEvents.data.data.length > 0) {
            // Get the connection timestamp
            const connectionTime = new Date(connectEvents.data.data[0].createdAt);
            const currentTime = new Date();
    
            // Calculate time difference in minutes
            const diffMs = currentTime - connectionTime;
            const diffMinutes = Math.floor(diffMs / 60000);
    
            sessionTimeString = formatPlaytime(diffMinutes);
        }
    
        // Get playtime from POG and player objects (in seconds)
        const serverPlaytimeMinutes = Math.floor(pog.playtimeSeconds / 60);
        const communityPlaytimeMinutes = Math.floor(player.playtimeSeconds / 60);
    
        const serverPlaytimeString = formatPlaytime(serverPlaytimeMinutes);
        const communityPlaytimeString = formatPlaytime(communityPlaytimeMinutes);
    
        await player.pm(
            `Playtime stats:\n` +
            `Current session: ${sessionTimeString}\n` +
            `This server: ${serverPlaytimeString}\n` +
            `Total community: ${communityPlaytimeString}`
        );
    }
    
    // Helper function to format minutes into readable time
    function formatPlaytime(minutes) {
        const days = Math.floor(minutes / 1440);
        const hours = Math.floor((minutes % 1440) / 60);
        const remainingMinutes = minutes % 60;
    
        let timeString = "";
    
        if (days > 0) {
            timeString += `${days} day${days !== 1 ? 's' : ''}`;
            if (hours > 0 || remainingMinutes > 0) timeString += `, `;
        }
    
        if (hours > 0) {
            timeString += `${hours} hour${hours !== 1 ? 's' : ''}`;
            if (remainingMinutes > 0) timeString += ` and `;
        }
    
        if (remainingMinutes > 0 || (days === 0 && hours === 0)) {
            timeString += `${remainingMinutes} minute${remainingMinutes !== 1 ? 's' : ''}`;
        }
    
        return timeString;
    }
    
    await main();

Cron jobs 1

Work the module runs on a schedule.

  • rewardOnlinePlaytime

    Cron job source
    // cronJobs/rewardOnlinePlaytime.js
    
    import { takaro, data, checkPermission } from '@takaro/helpers';
    
    // Helper function to format playtime into readable time
    function formatPlaytime(minutes) {
        if (minutes < 60) {
            return `${minutes} minute${minutes === 1 ? '' : 's'}`;
        } else if (minutes < 1440) {
            const hours = Math.floor(minutes / 60);
            const remainingMinutes = minutes % 60;
            return `${hours} hour${hours === 1 ? '' : 's'} ${remainingMinutes} minute${remainingMinutes === 1 ? '' : 's'}`;
        } else {
            const days = Math.floor(minutes / 1440);
            const hours = Math.floor((minutes % 1440) / 60);
            return `${days} day${days === 1 ? '' : 's'} ${hours} hour${hours === 1 ? '' : 's'}`;
        }
    }
    
    async function main() {
        const { gameServerId, module: mod } = data;
    
        // Get online players
        const onlinePlayers = await takaro.playerOnGameserver.playerOnGameServerControllerSearch({
            filters: {
                gameServerId: [gameServerId],
                online: [true]
            }
        });
    
        // If no players online, exit early
        if (onlinePlayers.data.meta.total === 0) {
            return;
        }
    
        // Parse configuration values
        const interval = mod.userConfig.rewardInterval;
        const baseReward = mod.userConfig.baseReward;
        const possibleItems = mod.userConfig.possibleItems || [];
        const configItemChance = parseInt(mod.userConfig.itemChance || '0');
        const rewardMessage = mod.userConfig.rewardMessage;
    
        // Get currency name
        let currencyName = "coins";
        try {
            const currencyNameSetting = await takaro.settings.settingsControllerGetOne('currencyName', gameServerId);
            if (currencyNameSetting && currencyNameSetting.data.data) {
                currencyName = currencyNameSetting.data.data.value;
            }
        } catch (error) {
            // Use default if setting not found
        }
    
        // Process each online player
        for (const pog of onlinePlayers.data.data) {
            try {
                // Find last reward time variable
                const lastRewardVar = await takaro.variable.variableControllerSearch({
                    filters: {
                        key: ['last_playtime_reward'],
                        playerId: [pog.playerId],
                        gameServerId: [gameServerId],
                        moduleId: [mod.moduleId]
                    }
                });
    
                // Get current time
                const now = Date.now();
                let lastRewardTime = 0;
                let lastRewardVarId = null;
    
                if (lastRewardVar.data.data.length > 0) {
                    lastRewardTime = parseInt(lastRewardVar.data.data[0].value);
                    lastRewardVarId = lastRewardVar.data.data[0].id;
                }
    
                // Check if it's time for a reward
                if (now - lastRewardTime >= interval) {
                    // Get player's session time
                    let sessionTimeString = "some time";
                    try {
                        const connectEvents = await takaro.event.eventControllerSearch({
                            filters: {
                                eventName: ['player-connected'],
                                gameserverId: [gameServerId],
                                playerId: [pog.playerId]
                            },
                            sortBy: "createdAt",
                            sortDirection: "desc",
                            limit: 1
                        });
    
                        if (connectEvents.data.data.length > 0) {
                            const connectionTime = new Date(connectEvents.data.data[0].createdAt);
                            const currentTime = new Date();
                            const diffMs = currentTime - connectionTime;
                            const diffMinutes = Math.floor(diffMs / 60000);
                            sessionTimeString = formatPlaytime(diffMinutes);
                        }
                    } catch (error) {
                         // Continue with default session time string
                    }
    
                    // Variables to track rewards
                    let currencyAwarded = 0;
                    let itemReceived = "nothing";
    
                    // Check for permission override for currency reward
                    const currencyPermission = checkPermission(pog, 'PLAYTIME_REWARD_OVERRIDE');
                    const actualReward = currencyPermission && currencyPermission.count != null ? currencyPermission.count : baseReward;
    
                    // Grant currency if configured
                    if (actualReward > 0) {
                        await takaro.playerOnGameserver.playerOnGameServerControllerAddCurrency(gameServerId, pog.playerId, {
                            currency: actualReward
                        });
                        currencyAwarded = actualReward;
                    }
    
                    // Check for item chance override
                    const itemChancePermission = checkPermission(pog, 'PLAYTIME_ITEM_CHANCE_OVERRIDE');
                    const actualItemChance = itemChancePermission && itemChancePermission.count != null
                        ? itemChancePermission.count
                        : configItemChance;
    
                    // Determine if player gets an item
                    if (possibleItems.length > 0 && Math.random() * 100 < actualItemChance) {
                        // Select random item from the list
                        const randomItem = possibleItems[Math.floor(Math.random() * possibleItems.length)];
    
                       if (randomItem && randomItem.item) {
                            try {
                                // Get item details
                                const itemDetails = await takaro.item.itemControllerFindOne(randomItem.item);
                                if (itemDetails && itemDetails.data.data) {
                                    // Give item to player
                                    await takaro.gameserver.gameServerControllerGiveItem(gameServerId, pog.playerId, {
                                        name: itemDetails.data.data.code,
                                        amount: randomItem.amount || 1,
                                        quality: randomItem.quality || ''
                                    });
                                    itemReceived = `${randomItem.amount || 1}x ${itemDetails.data.data.name}`;
                                }
                            } catch (error) {
                                // Failed to give item, continue with default
                            }
                        }
                    }
    
                    // Send reward message to player
                    const formattedMessage = rewardMessage
                        .replace('{minutes}', sessionTimeString)
                        .replace('{currency}', `${currencyAwarded} ${currencyName}`)
                        .replace('{item}', itemReceived);
    
                    await takaro.gameserver.gameServerControllerSendMessage(gameServerId, {
                        message: formattedMessage,
                        opts: {
                            recipient: {
                                gameId: pog.gameId
                            }
                        }
                    });
    
                    // Update last reward time
                    if (lastRewardVarId) {
                        await takaro.variable.variableControllerUpdate(lastRewardVarId, {
                            value: now.toString()
                        });
                    } else {
                        await takaro.variable.variableControllerCreate({
                            key: 'last_playtime_reward',
                            value: now.toString(),
                            playerId: pog.playerId,
                            gameServerId: gameServerId,
                            moduleId: mod.moduleId
                        });
                    }
                }
            } catch (error) {
                // Skip to next player if there's an error
                continue;
            }
        }
    }
    
    await main();

Permissions 2

Roles you can grant to decide who may use what.

  • Playtime Reward Override

    Override the base currency reward for playtime. Count value determines the reward amount.

  • Playtime Item Chance Override

    Override the chance to receive an item. Count value is the percentage (0-100).