From Concept to Code: Implementation Details

In Part 1, I covered why I built this time management system and the overall architecture. Now let’s get our hands dirty with the actual implementation. If you want to follow along with the complete source code, everything is available on GitHub.

The RCON Connection: Talking to Minecraft

The heart of this system is the RCON (Remote Console) protocol—Minecraft’s way of accepting external commands. <RCON – Minecraft Wiki>

Here’s what makes it work:

Setting Up RCON Communication

First, you need to enable RCON in your Minecraft server’s server.properties:

enable-rcon=true
rcon.port=25575
rcon.password=your_secure_password

The RCON client handles authentication and command execution. Every interaction follows this pattern:

  1. Open TCP connection to the Minecraft server
  2. Send authentication packet
  3. Execute command
  4. Receive and parse response
  5. Close connection (or keep alive for efficiency)

The protocol uses a specific packet structure with request IDs, packet types, and payload data. Getting this wrong means silent failures or cryptic errors, so proper error handling is crucial.

Command Execution in Practice

When you click “Give Item” in the web interface, here’s what happens under the hood:

# Simplified flow
def execute_custom_command(player, command_template):
    # Replace {player} placeholder with actual player name
    actual_command = command_template.replace('{player}', player)
    
    # Send via RCON
    response = rcon_client.execute(actual_command)
    
    # Log and return result
    return response

For multi-command sequences (like giving multiple items or teleporting then changing game mode), the system executes each command in order, waiting for confirmation before proceeding to the next.

The Background Monitor: Keeping Track of Time

The most critical component is the background timer that monitors player activity. This runs in a separate thread and wakes up every 60 seconds:

The Monitoring Loop

def monitor_players():
    while True:
        # Get list of online players from Minecraft
        online_players = get_online_players_via_rcon()
        
        for player in online_players:
            # Load player data from database
            player_data = db.get_player(player)
            
            # Calculate time played today
            time_today = calculate_daily_playtime(player_data)
            
            # Check against their limit
            if time_today >= player_data.daily_limit:
                kick_player_with_message(player)
            elif time_today >= (player_data.daily_limit - 5):
                send_warning(player, minutes_left=5)
            
            # Update session tracking
            db.update_player_session(player)
        
        sleep(60)

Handling Session Tracking

One tricky aspect is accurately tracking sessions. When a player joins:

  • Record current_session_start timestamp
  • Mark is_online = True
  • Increment session counter

When they disconnect (either voluntarily or kicked):

  • Calculate session duration
  • Add to time_played_today
  • Update total_time_played
  • Store session record for historical data
  • Mark is_online = False

The challenge? Detecting when players disconnect naturally versus being kicked by the system. The solution involves checking the RCON player list and comparing against the database state—if someone’s marked online but not in the RCON list, they’ve disconnected.

The Configuration System: Making It Flexible

One design goal was avoiding hard-coded commands. Everything lives in custom_commands.json:

{
  "items": [
    {
      "name": "Diamond Sword",
      "icon": "🗡️",
      "command": "give {player} diamond_sword{Enchantments:[{id:sharpness,lvl:5}]} 1",
      "description": "Enchanted diamond sword"
    }
  ],
  "commands": [
    {
      "name": "Teleport Home",
      "type": "multi",
      "commands": [
        "tp {player} 0 64 0",
        "title {player} subtitle {\"text\":\"Welcome home!\",\"color\":\"green\"}"
      ]
    }
  ]
}

The Config Editor

The web-based config editor parses this JSON and renders a clean interface for modifications. When you save changes:

  1. Validate JSON structure
  2. Write to file atomically (to prevent corruption)
  3. Reload config into memory
  4. Invalidate any cached command mappings

This means you can add new items, modify commands, or adjust settings without restarting the application or touching code.

Database Schema

SQLite provides the perfect balance of simplicity and functionality for this use case. The schema tracks three main entities:

Players Table

Stores core player information—username, daily time limit, current playtime, session state, and historical totals.

Sessions Table

Historical record of every login/logout, including duration and disconnect reason. This data is gold for understanding usage patterns.

Settings Table

Key-value pairs for system-wide configuration like default time limits, warning thresholds, and RCON connection details.


Frontend: Making It Usable

The web interface needed to be something my wife could use without calling me for help.

That meant:

Real-Time Updates

JavaScript polls the API every few seconds to refresh the player list and time remaining. AJAX requests keep the page responsive without full reloads.

Toast Notifications

Every action—executing a command, kicking a player, saving config—triggers a toast notification. Immediate feedback is crucial for non-technical users.

Responsive Design

Works on desktop, tablet, and mobile. Sometimes you need to check on things from your phone, and the interface adapts beautifully.

Modal Dialogs

Adding players, editing limits, and confirming destructive actions all use modals. This keeps the interface clean while preventing accidental clicks.


The Docker Setup: Easy Deployment

The docker-compose.yml ties everything together:

services:
  minecraft:
    image: itzg/minecraft-server
    ports:
      - "25565:25565"
      - "25575:25575"
    environment:
      EULA: "TRUE"
      RCON_PASSWORD: "${RCON_PASSWORD}"
    volumes:
      - ./minecraft-data:/data

  timemanager:
    build: .
    ports:
      - "5000:5000"
    environment:
      RCON_HOST: "minecraft"
      RCON_PORT: "25575"
      RCON_PASSWORD: "${RCON_PASSWORD}"
    volumes:
      - ./data:/app/data
    depends_on:
      - minecraft

The beauty of this setup:

  • Minecraft server and time manager run in isolated containers
  • They can communicate through Docker’s internal network
  • One .env file manages all sensitive config
  • Volumes persist data across container restarts
  • depends_on ensures proper startup order

Deploying Your Own Instance

Want to run this yourself? Here’s the quick-start:

  1. Clone the repository:
   git clone https://github.com/MichalHajduch85/Minecraft_TimeManager/
   cd Minecraft_TimeManager
  1. Set up environment variables:
   cp .env.example .env
   # Edit .env with your RCON password and settings
  1. Start everything:
   docker-compose up -d
  1. Access the dashboard: Open http://localhost:5000 in your browser
  2. Configure your first player: Connected players are added automatically, you can set their daily time limits through the web interface

The GitHub repository includes full documentation, example configurations, and troubleshooting guides.

.post scriptum

Building this tool scratched multiple itches—solving a real parental challenge, learning new technologies, and creating something actually useful. The fact that it’s now maybe helping other parents manage screen time, makes it even more rewarding.

If you’re running a local Minecraft server and dealing with time management challenges, give it a try. If you’re a developer looking to learn Flask, Docker, or RCON integration, the codebase is well-commented and ready for exploration.

Check out the complete source code on GitHub, star the repo if you find it useful, and feel free to contribute improvements or report issues.


Questions? Improvements? Bug reports? Open an issue on GitHub. I’m always interested in making this tool better and hearing how others are using it.


Leave a Reply

Your email address will not be published. Required fields are marked *