Welcome to Code Forum!

Join a community that supports you and your coding journey from day one. We strive to be a friendly, supportive community that empowers everyone to be better developers. By registering with us, you'll be able to discuss, share and private message with other members of our community.

SignUp Now!
  • Guest, before posting your code please take these rules into consideration:
    • It is required to use our BBCode feature to display your code. While within the editor click < / > or >_ and place your code within the BB Code prompt. This helps others with finding a solution by making it easier to read and easier to copy.
    • You can also use markdown to share your code. When using markdown your code will be automatically converted to BBCode. For help with markdown check out the markdown guide.
    • Don't share a wall of code. All we want is the problem area, the code related to your issue.

    GIF shows where to locate </> in the thread and or post editor toolbar.
    To learn more about how to use our BBCode feature, review our "How to post your code into threads" here.

    Thank you, Code Forum.

PHP Problem with game development

fdk

Coder
Hi all,

I'm a super new coder and I've set myself a project of building a small text-based game played in the browser to learn skills. I've built some basic features of my game and one of these is a gym, which I've been struggling with for a few days now without success.

Essentially, as you will see from my code, I am attempting to set a system whereby a player can train a stat once every 30 minutes. However, no matter what I do, I can't seem to enforce the cooldown. I'm using a database column last_train_time which I have verified is being correctly updated upon a train, as is the stat, however, I cannot seem to force the cooldown - a player is able to bypass the cooldown by just refreshing and resubmitting, or is able to train again without my error message showing and completely bypassing the cooldown.

I've tried multiple versions of code and have recoded the page three times so far to no avail, I just cannot fix this problem. I've read the guidance about not posting a wall of code, but seeing as I have no idea which part I am making an error in, here is the full 130 or so line code for the file - I'd appreciate any help!

PHP:
<?php
session_start();
require_once 'db_connect.php';

if (!isset($_SESSION['user_id'])) {
    header("Location: login.php");
    exit();
}

$user_id = $_SESSION['user_id'];

// Fetch user details
$sql = "SELECT strength, defense, speed, agility, health, gold, in_hospital, last_train_time, last_super_train_time
        FROM users WHERE id = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("i", $user_id);
$stmt->execute();
$user = $stmt->get_result()->fetch_assoc();

$now = time();
$train_cooldown = 1800; // 30 minutes in seconds
$super_train_cooldown = 43200; // 12 hours in seconds

$last_train_time = strtotime($user['last_train_time']);
$last_super_train_time = strtotime($user['last_super_train_time']);

$feedback = "";

// Check if the player is in the hospital
if ($user['in_hospital']) {
    echo "You cannot use the gym whilst in hospital.";
    echo "<br><a href='hospital.php'>Go to Hospital</a>";
    exit();
}

// Check if the player has at least 50 health
if ($user['health'] < 50) {
    echo "You are currently too injured to use the gym, please wait until you have more health.";
    echo "<br><a href='home.php'>Home</a>";
    echo " | <a href='hospital.php'>Go to Hospital</a>";
    echo " | <a href='inventory.php'>Go to Inventory</a>";
    exit();
}

// Handle training requests
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if (isset($_POST['train_type']) && isset($_POST['stat'])) {
        $train_type = $_POST['train_type'];
        $stat = $_POST['stat'];

        if (!in_array($stat, ['strength', 'defense', 'speed', 'agility'])) {
            $feedback = "Invalid stat selection.";
        } else {
            if ($train_type === 'train') {
                if (($now - $last_train_time) < $train_cooldown) {
                    $remaining_time = ceil(($train_cooldown - ($now - $last_train_time)) / 60);
                    $feedback = "You have recently trained, and must wait 30 minutes between training sessions. Cooldown remaining: $remaining_time minutes.";
                } else {
                    $increase = $user[$stat] * 0.0001;
                    $user[$stat] += $increase;

                    // Update stat and last_train_time in the database
                    $stmt = $conn->prepare("UPDATE users SET $stat = ?, last_train_time = NOW() WHERE id = ?");
                    $stmt->bind_param("di", $user[$stat], $user_id);
                    $stmt->execute();

                    $feedback = "Your $stat increased by " . number_format($increase, 8) . "!";
                }
            } elseif ($train_type === 'super_train') {
                if (($now - $last_super_train_time) < $super_train_cooldown) {
                    $remaining_time = ceil(($super_train_cooldown - ($now - $last_super_train_time)) / 3600);
                    $feedback = "You have recently used super train and must wait 12 hours between sessions. Cooldown remaining: $remaining_time hours.";
                } else {
                    $increase = $user[$stat] * 0.001;
                    $user[$stat] += $increase;

                    // Update stat and last_super_train_time in the database
                    $stmt = $conn->prepare("UPDATE users SET $stat = ?, last_super_train_time = NOW() WHERE id = ?");
                    $stmt->bind_param("di", $user[$stat], $user_id);
                    $stmt->execute();

                    $feedback = "Your $stat super increased by " . number_format($increase, 8) . "!";
                }
            } else {
                $feedback = "Invalid training type.";
            }
        }
    } else {
        $feedback = "Please select a training type and stat.";
    }
}
?>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Gym</title>
</head>
<body>
<h2>Welcome to the Gym</h2>

<p>Your current stats:</p>
<ul>
    <li>Strength: <?= number_format($user['strength'], 8) ?></li>
    <li>Defense: <?= number_format($user['defense'], 8) ?></li>
    <li>Speed: <?= number_format($user['speed'], 8) ?></li>
    <li>Agility: <?= number_format($user['agility'], 8) ?></li>
    <li>Health: <?= $user['health'] ?> / 5000</li>
    <li>Gold: <?= $user['gold'] ?></li>
</ul>

<form method="POST">
    <h3>Select a training type:</h3>
    <input type="radio" name="train_type" value="train" required> Regular Train<br>
    <input type="radio" name="train_type" value="super_train" required> Super Train<br>

    <h3>Select a stat to train:</h3>
    <input type="radio" name="stat" value="strength" required> Strength<br>
    <input type="radio" name="stat" value="defense" required> Defense<br>
    <input type="radio" name="stat" value="speed" required> Speed<br>
    <input type="radio" name="stat" value="agility" required> Agility<br><br>

    <button type="submit">Train</button>
</form>

<p><?= $feedback ?></p>

<a href="home.php">Home</a>
<a href="battle.php">Battle</a>
<a href="hospital.php">Hospital</a>
<a href="gym_c.php">Cooldowns</a>
</body>
</html>
 
Solution
I think your problem is like @JimmysNetwork suggested, it's likely down to the time configured on your server compared with the time configured for your database.

Check your attack script, you pass the time into your update statement from your php environment:

Code:
$cooldown_update_query = "
            INSERT INTO attack_cooldowns (player_id, opponent_id, last_attack_time)
            VALUES (?, ?, ?)
            ON DUPLICATE KEY UPDATE last_attack_time = VALUES(last_attack_time)";
        $stmt = $conn->prepare($cooldown_update_query);
        $stmt->bind_param("iii", $user_id, $opponent_id, $current_time);

Compare that with what you are doing in your gym script, where you're setting the last time to mysql's NOW()...
Hi all,

I'm a super new coder and I've set myself a project of building a small text-based game played in the browser to learn skills. I've built some basic features of my game and one of these is a gym, which I've been struggling with for a few days now without success.

Essentially, as you will see from my code, I am attempting to set a system whereby a player can train a stat once every 30 minutes. However, no matter what I do, I can't seem to enforce the cooldown. I'm using a database column last_train_time which I have verified is being correctly updated upon a train, as is the stat, however, I cannot seem to force the cooldown - a player is able to bypass the cooldown by just refreshing and resubmitting, or is able to train again without my error message showing and completely bypassing the cooldown.

I've tried multiple versions of code and have recoded the page three times so far to no avail, I just cannot fix this problem. I've read the guidance about not posting a wall of code, but seeing as I have no idea which part I am making an error in, here is the full 130 or so line code for the file - I'd appreciate any help!

PHP:
<?php
session_start();
require_once 'db_connect.php';

if (!isset($_SESSION['user_id'])) {
    header("Location: login.php");
    exit();
}

$user_id = $_SESSION['user_id'];

// Fetch user details
$sql = "SELECT strength, defense, speed, agility, health, gold, in_hospital, last_train_time, last_super_train_time
        FROM users WHERE id = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("i", $user_id);
$stmt->execute();
$user = $stmt->get_result()->fetch_assoc();

$now = time();
$train_cooldown = 1800; // 30 minutes in seconds
$super_train_cooldown = 43200; // 12 hours in seconds

$last_train_time = strtotime($user['last_train_time']);
$last_super_train_time = strtotime($user['last_super_train_time']);

$feedback = "";

// Check if the player is in the hospital
if ($user['in_hospital']) {
    echo "You cannot use the gym whilst in hospital.";
    echo "<br><a href='hospital.php'>Go to Hospital</a>";
    exit();
}

// Check if the player has at least 50 health
if ($user['health'] < 50) {
    echo "You are currently too injured to use the gym, please wait until you have more health.";
    echo "<br><a href='home.php'>Home</a>";
    echo " | <a href='hospital.php'>Go to Hospital</a>";
    echo " | <a href='inventory.php'>Go to Inventory</a>";
    exit();
}

// Handle training requests
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if (isset($_POST['train_type']) && isset($_POST['stat'])) {
        $train_type = $_POST['train_type'];
        $stat = $_POST['stat'];

        if (!in_array($stat, ['strength', 'defense', 'speed', 'agility'])) {
            $feedback = "Invalid stat selection.";
        } else {
            if ($train_type === 'train') {
                if (($now - $last_train_time) < $train_cooldown) {
                    $remaining_time = ceil(($train_cooldown - ($now - $last_train_time)) / 60);
                    $feedback = "You have recently trained, and must wait 30 minutes between training sessions. Cooldown remaining: $remaining_time minutes.";
                } else {
                    $increase = $user[$stat] * 0.0001;
                    $user[$stat] += $increase;

                    // Update stat and last_train_time in the database
                    $stmt = $conn->prepare("UPDATE users SET $stat = ?, last_train_time = NOW() WHERE id = ?");
                    $stmt->bind_param("di", $user[$stat], $user_id);
                    $stmt->execute();

                    $feedback = "Your $stat increased by " . number_format($increase, 8) . "!";
                }
            } elseif ($train_type === 'super_train') {
                if (($now - $last_super_train_time) < $super_train_cooldown) {
                    $remaining_time = ceil(($super_train_cooldown - ($now - $last_super_train_time)) / 3600);
                    $feedback = "You have recently used super train and must wait 12 hours between sessions. Cooldown remaining: $remaining_time hours.";
                } else {
                    $increase = $user[$stat] * 0.001;
                    $user[$stat] += $increase;

                    // Update stat and last_super_train_time in the database
                    $stmt = $conn->prepare("UPDATE users SET $stat = ?, last_super_train_time = NOW() WHERE id = ?");
                    $stmt->bind_param("di", $user[$stat], $user_id);
                    $stmt->execute();

                    $feedback = "Your $stat super increased by " . number_format($increase, 8) . "!";
                }
            } else {
                $feedback = "Invalid training type.";
            }
        }
    } else {
        $feedback = "Please select a training type and stat.";
    }
}
?>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Gym</title>
</head>
<body>
<h2>Welcome to the Gym</h2>

<p>Your current stats:</p>
<ul>
    <li>Strength: <?= number_format($user['strength'], 8) ?></li>
    <li>Defense: <?= number_format($user['defense'], 8) ?></li>
    <li>Speed: <?= number_format($user['speed'], 8) ?></li>
    <li>Agility: <?= number_format($user['agility'], 8) ?></li>
    <li>Health: <?= $user['health'] ?> / 5000</li>
    <li>Gold: <?= $user['gold'] ?></li>
</ul>

<form method="POST">
    <h3>Select a training type:</h3>
    <input type="radio" name="train_type" value="train" required> Regular Train<br>
    <input type="radio" name="train_type" value="super_train" required> Super Train<br>

    <h3>Select a stat to train:</h3>
    <input type="radio" name="stat" value="strength" required> Strength<br>
    <input type="radio" name="stat" value="defense" required> Defense<br>
    <input type="radio" name="stat" value="speed" required> Speed<br>
    <input type="radio" name="stat" value="agility" required> Agility<br><br>

    <button type="submit">Train</button>
</form>

<p><?= $feedback ?></p>

<a href="home.php">Home</a>
<a href="battle.php">Battle</a>
<a href="hospital.php">Hospital</a>
<a href="gym_c.php">Cooldowns</a>
</body>
</html>

Hey there!

There are a couple things that could be causing the issues.

1. Database Timestamp and PHP Time Mismatch
  • Your database is storing last_train_time as a DATETIME (or similar), but PHP's time() returns a Unix timestamp.
  • strtotime() converts a string to a Unix timestamp, so this part should work. However, if the database time and PHP server time are not synchronized, the cooldown might behave incorrectly.
From what online this could help if that is the issue:

Ensure the server running PHP and the database server have synchronized time settings (e.g., both using NTP). If they aren't, use PHP's date_default_timezone_set() to set a consistent timezone at the start of your script.

Example:
PHP:
date_default_timezone_set('UTC'); // Server's timezone

-

You could also try using SQL to check if the cooldown has expired. This can simplify the PHP logic and make it less prone to errors.

Example:


PHP:
$stmt = $conn->prepare(
    "UPDATE users
     SET $stat = $stat + ?, last_train_time = NOW()
     WHERE id = ? AND TIMESTAMPDIFF(SECOND, last_train_time, NOW()) >= ?"
);
$stmt->bind_param("dii", $increase, $user_id, $train_cooldown);
$stmt->execute();

if ($stmt->affected_rows > 0) {
    $feedback = "Your $stat increased by " . number_format($increase, 8) . "!";
} else {
    $remaining_time = ceil(($train_cooldown - ($now - $last_train_time)) / 60);
    $feedback = "You must wait before training again. Cooldown remaining: $remaining_time minutes.";
}

Please let me know if either of the two issues help out. If not I will try to help out further with it.
 
Thanks so much for your reply!

The first solution did not help at all - so I'm guessing this is not the problem. The second solution has actually broken my page - it may be that the code isn't compatible with other code in the file. I've tried to play around but can't get it to work again. I can do a rebuild to make the page compatible around this code if you think it'll be a better option for me and it'll work - I'm just loath to do another rebuild if it still won't fix the cooldown problem!

Any suggestions?
 
Thanks so much for your reply!

The first solution did not help at all - so I'm guessing this is not the problem. The second solution has actually broken my page - it may be that the code isn't compatible with other code in the file. I've tried to play around but can't get it to work again. I can do a rebuild to make the page compatible around this code if you think it'll be a better option for me and it'll work - I'm just loath to do another rebuild if it still won't fix the cooldown problem!

Any suggestions?

Hmm. Let me see if I can rewrite it to work with your code. I’ll try to test it myself.
 
Hmm. Let me see if I can rewrite it to work with your code. I’ll try to test it myself.

Thanks so much for your help!

I really don't know what the issue could be - I'm using a cooldown in another area of the game and that functionality is working just fine with very similar code, so really stuck as to what I'm missing, hence why it's taken me so long to work on it.
 
Just to add - I attempted a full rewrite, using a separate database table as I did with my other cooldown feature that is working, and for some reason, I still can't resolve the issue.

Here is the new code:

PHP:
<?php
session_start();
require_once 'db_connect.php';

if (!isset($_SESSION['user_id'])) {
    header("Location: login.php");
    exit();
}

$user_id = $_SESSION['user_id'];

// Fetch user details
$sql = "SELECT strength, defense, speed, agility, health, gold, in_hospital FROM users WHERE id = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("i", $user_id);
$stmt->execute();
$user = $stmt->get_result()->fetch_assoc();

// Define cooldowns in seconds
$train_cooldown = 1800; // 30 minutes
$super_train_cooldown = 43200; // 12 hours
$now = new DateTime();

// Fetch cooldowns
$sql = "SELECT train_type, last_train_time FROM gym_cooldowns WHERE user_id = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("i", $user_id);
$stmt->execute();
$cooldowns = $stmt->get_result()->fetch_all(MYSQLI_ASSOC);

$last_train_time = null;
$last_super_train_time = null;

foreach ($cooldowns as $cooldown) {
    if ($cooldown['train_type'] === 'train') {
        $last_train_time = new DateTime($cooldown['last_train_time']);
    } elseif ($cooldown['train_type'] === 'super_train') {
        $last_super_train_time = new DateTime($cooldown['last_train_time']);
    }
}

$feedback = "";

// Check hospital status
if ($user['in_hospital']) {
    echo "You cannot use the gym while in the hospital.";
    echo "<br><a href='hospital.php'>Go to Hospital</a>";
    exit();
}

// Check health
if ($user['health'] < 50) {
    echo "You are currently too injured to use the gym.";
    echo "<br><a href='home.php'>Home</a>";
    echo " | <a href='hospital.php'>Go to Hospital</a>";
    echo " | <a href='inventory.php'>Go to Inventory</a>";
    exit();
}

// Handle training requests
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if (isset($_POST['train_type']) && isset($_POST['stat'])) {
        $train_type = $_POST['train_type'];
        $stat = $_POST['stat'];

        if (!in_array($stat, ['strength', 'defense', 'speed', 'agility'])) {
            $feedback = "Invalid stat selection.";
        } else {
            $cooldown_time = ($train_type === 'train') ? $train_cooldown : $super_train_cooldown;
            $last_time = ($train_type === 'train') ? $last_train_time : $last_super_train_time;

            // Check if cooldown has elapsed
            if ($last_time && ($now->getTimestamp() - $last_time->getTimestamp() < $cooldown_time)) {
                $remaining_time = ceil(($cooldown_time - ($now->getTimestamp() - $last_time->getTimestamp())) / 60);
                $feedback = "You must wait $remaining_time minutes before training again.";
            } else {
                // Calculate stat increase
                $increase = ($train_type === 'train') ? $user[$stat] * 0.0001 : $user[$stat] * 0.001;
                $user[$stat] += $increase;

                // Update user's stat
                $stmt = $conn->prepare("UPDATE users SET $stat = ? WHERE id = ?");
                $stmt->bind_param("di", $user[$stat], $user_id);
                $stmt->execute();

                // Update gym cooldowns
                $stmt = $conn->prepare("
                    INSERT INTO gym_cooldowns (user_id, train_type, last_train_time)
                    VALUES (?, ?, NOW())
                    ON DUPLICATE KEY UPDATE last_train_time = NOW()");
                $stmt->bind_param("is", $user_id, $train_type);
                $stmt->execute();

                $feedback = "Your $stat increased by " . number_format($increase, 8) . "!";
            }
        }
    } else {
        $feedback = "Please select a training type and stat.";
    }
}
?>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Gym</title>
</head>
<body>
<h2>Welcome to the Gym</h2>

<p>Your current stats:</p>
<ul>
    <li>Strength: <?= number_format($user['strength'], 8) ?></li>
    <li>Defense: <?= number_format($user['defense'], 8) ?></li>
    <li>Speed: <?= number_format($user['speed'], 8) ?></li>
    <li>Agility: <?= number_format($user['agility'], 8) ?></li>
    <li>Health: <?= $user['health'] ?> / 5000</li>
    <li>Gold: <?= $user['gold'] ?></li>
</ul>

<form method="POST">
    <h3>Select a training type:</h3>
    <input type="radio" name="train_type" value="train" required> Regular Train<br>
    <input type="radio" name="train_type" value="super_train" required> Super Train<br>

    <h3>Select a stat to train:</h3>
    <input type="radio" name="stat" value="strength" required> Strength<br>
    <input type="radio" name="stat" value="defense" required> Defense<br>
    <input type="radio" name="stat" value="speed" required> Speed<br>
    <input type="radio" name="stat" value="agility" required> Agility<br><br>

    <button type="submit">Train</button>
</form>

<p><?= $feedback ?></p>

<a href="home.php">Home</a>
<a href="battle.php">Battle</a>
<a href="hospital.php">Hospital</a>
</body>
</html>

As I said, this still isn't working. I'm sorry again for the walls of code, but I thought it might be helpful to share the other function I have that requires a cooldown, which I've managed to get working fine:

PHP:
<?php
session_start();
require_once 'db_connect.php';

// Check if player is logged in
if (!isset($_SESSION['user_id'])) {
    header("Location: login.php");
    exit;
}

$user_id = $_SESSION['user_id'];

// Get the player's details
$query = "SELECT id, username, health, experience, in_hospital FROM users WHERE id = ?";
$stmt = $conn->prepare($query);
$stmt->bind_param("i", $user_id);
$stmt->execute();
$result = $stmt->get_result();
$player = $result->fetch_assoc();

// Redirect if the player is in the hospital or dead
if ($player['in_hospital'] || $player['health'] <= 0) {
    header("Location: hospital.php");
    exit;
}

// Check if opponent is selected
if (isset($_GET['opponent_id']) && is_numeric($_GET['opponent_id'])) {
    $opponent_id = intval($_GET['opponent_id']);

    // Check individual cooldown
    $current_time = time();
    $cooldown_period = 10; // 10 seconds

    $cooldown_query = "SELECT last_attack_time FROM attack_cooldowns WHERE player_id = ? AND opponent_id = ?";
    $stmt = $conn->prepare($cooldown_query);
    $stmt->bind_param("ii", $user_id, $opponent_id);
    $stmt->execute();
    $cooldown_result = $stmt->get_result();

    if ($cooldown_result->num_rows > 0) {
        $cooldown_data = $cooldown_result->fetch_assoc();
        if ($current_time - $cooldown_data['last_attack_time'] < $cooldown_period) {
            $remaining_time = $cooldown_period - ($current_time - $cooldown_data['last_attack_time']);
            echo "<div class='alert alert-warning'>You must wait {$remaining_time} seconds before attacking this player again.</div>";
            echo '<div><a href="battle.php" class="btn btn-primary">Choose another battle</a></div>';
            exit;
        }
    }

    // Fetch opponent details
    $query = "SELECT id, username, health, experience, in_hospital FROM users WHERE id = ?";
    $stmt = $conn->prepare($query);
    $stmt->bind_param("i", $opponent_id);
    $stmt->execute();
    $opponent_result = $stmt->get_result();

    if ($opponent_result->num_rows == 0) {
        echo "<div class='alert alert-danger'>Invalid opponent. Please try again.</div>";
        exit;
    }

    $opponent = $opponent_result->fetch_assoc();

    // Ensure the opponent is not in the hospital or dead
    if ($opponent['in_hospital'] || $opponent['health'] <= 0) {
        echo "<div class='alert alert-warning'>This opponent is already incapacitated. Pick someone who can defend themselves!</div>";
        exit;
    }

    // Battle Simulation Logic
    $player_damage = rand(5, 20);
    $opponent_damage = rand(3, 12);
    $player_double_damage = false;
    $opponent_double_damage = false;

    // 10% chance for double damage for both players
    if (rand(1, 10) == 1) {
        $player_damage *= 2;
        $player_double_damage = true;
    }
    if (rand(1, 10) == 1) {
        $opponent_damage *= 2;
        $opponent_double_damage = true;
    }

    // Calculate new health values
    $new_player_health = max(0, $player['health'] - $opponent_damage);
    $new_opponent_health = max(0, $opponent['health'] - $player_damage);

    // Calculate experience gain for the attacker
    $exp_gain = rand(1, 3);
    $new_player_exp = $player['experience'] + $exp_gain;

    // Update database with new health values, experience, and attack cooldown
    $conn->begin_transaction();
    try {
        // Update player health and experience
        $update_player = "UPDATE users SET health = ?, experience = ? WHERE id = ?";
        $stmt = $conn->prepare($update_player);
        $stmt->bind_param("iii", $new_player_health, $new_player_exp, $user_id);
        $stmt->execute();

        // Update opponent health
        $update_opponent = "UPDATE users SET health = ? WHERE id = ?";
        $stmt = $conn->prepare($update_opponent);
        $stmt->bind_param("ii", $new_opponent_health, $opponent_id);
        $stmt->execute();

        // Update or insert cooldown data
        $cooldown_update_query = "
            INSERT INTO attack_cooldowns (player_id, opponent_id, last_attack_time)
            VALUES (?, ?, ?)
            ON DUPLICATE KEY UPDATE last_attack_time = VALUES(last_attack_time)";
        $stmt = $conn->prepare($cooldown_update_query);
        $stmt->bind_param("iii", $user_id, $opponent_id, $current_time);
        $stmt->execute();

        // If player health is 0, send them to the hospital
        if ($new_player_health == 0) {
            $in_hospital = 1;
            $update_hospital = "UPDATE users SET in_hospital = ? WHERE id = ?";
            $stmt = $conn->prepare($update_hospital);
            $stmt->bind_param("ii", $in_hospital, $user_id);
            $stmt->execute();
        }

        $conn->commit();
    } catch (Exception $e) {
        $conn->rollback();
        echo "<div class='alert alert-danger'>Battle failed. Please try again later.</div>";
        exit;
    }

    // Display Battle Results
    echo "<div class='battle-results'>";
    echo "<h3>Battle Results</h3>";
    echo "<p>You attacked <strong>{$opponent['username']}</strong>!</p>";
    echo "<p>You dealt <strong>{$player_damage}</strong> damage" . ($player_double_damage ? " with a <strong>double hit</strong>!" : ".") . "</p>";
    echo "<p><strong>{$opponent['username']}</strong> dealt <strong>{$opponent_damage}</strong> damage" . ($opponent_double_damage ? " with a <strong>double hit</strong>!" : ".") . "</p>";

    if ($new_opponent_health == 0) {
        echo "<p><strong>You defeated {$opponent['username']}!</strong></p>";
    } else {
        echo "<p>{$opponent['username']} now has <strong>{$new_opponent_health}</strong> health left.</p>";
    }

    echo "<p>You gained <strong>{$exp_gain} experience</strong>!</p>";

    if ($new_player_health == 0) {
        echo "<p>You have been defeated and sent to the hospital.</p>";
        header("Refresh: 3; URL=hospital.php");
        exit;
    } else {
        echo "<p>Your current health is <strong>{$new_player_health}</strong>.</p>";
    }

    echo '<div class="button-group">';
    echo '<a href="battle.php" class="btn">Choose Your Next Battle</a>';
    echo '</div>';
    echo "</div>";

} else {
    echo "<div class='alert alert-danger'>No opponent selected. Please return to the battle page.</div>";
    exit;
}
?>

Can anyone please see where I'm going wrong with my first code?

Thank you all so much, I'm so appreciative of your help and patience with a newbie coder 🙂
 
I think your problem is like @JimmysNetwork suggested, it's likely down to the time configured on your server compared with the time configured for your database.

Check your attack script, you pass the time into your update statement from your php environment:

Code:
$cooldown_update_query = "
            INSERT INTO attack_cooldowns (player_id, opponent_id, last_attack_time)
            VALUES (?, ?, ?)
            ON DUPLICATE KEY UPDATE last_attack_time = VALUES(last_attack_time)";
        $stmt = $conn->prepare($cooldown_update_query);
        $stmt->bind_param("iii", $user_id, $opponent_id, $current_time);

Compare that with what you are doing in your gym script, where you're setting the last time to mysql's NOW():

Code:
$stmt = $conn->prepare("
                    INSERT INTO gym_cooldowns (user_id, train_type, last_train_time)
                    VALUES (?, ?, NOW())
                    ON DUPLICATE KEY UPDATE last_train_time = NOW()");

change last_train_time to a bound parameter and I'm sure that will help you solve the problem 🙂

FWIW I ran your code on my machine and didn't encounter a problem, but my local sql server and php time are both configured to UTC.
 
Solution
I think your problem is like @JimmysNetwork suggested, it's likely down to the time configured on your server compared with the time configured for your database.

Check your attack script, you pass the time into your update statement from your php environment:

Code:
$cooldown_update_query = "
            INSERT INTO attack_cooldowns (player_id, opponent_id, last_attack_time)
            VALUES (?, ?, ?)
            ON DUPLICATE KEY UPDATE last_attack_time = VALUES(last_attack_time)";
        $stmt = $conn->prepare($cooldown_update_query);
        $stmt->bind_param("iii", $user_id, $opponent_id, $current_time);

Compare that with what you are doing in your gym script, where you're setting the last time to mysql's NOW():

Code:
$stmt = $conn->prepare("
                    INSERT INTO gym_cooldowns (user_id, train_type, last_train_time)
                    VALUES (?, ?, NOW())
                    ON DUPLICATE KEY UPDATE last_train_time = NOW()");

change last_train_time to a bound parameter and I'm sure that will help you solve the problem 🙂

FWIW I ran your code on my machine and didn't encounter a problem, but my local sql server and php time are both configured to UTC.

Gah! This is such an obvious issue and has had me absolutely stumped for almost a week... goes to show I have a long way to learn.

I've resolved the issue now and cooldowns are being enforced properly!

Thank you both of you guys so much for your help 🙂
 
Hi all,

I'm a super new coder and I've set myself a project of building a small text-based game played in the browser to learn skills. I've built some basic features of my game and one of these is a gym, which I've been struggling with for a few days now without success.

Essentially, as you will see from my code, I am attempting to set a system whereby a player can train a stat once every 30 minutes. However, no matter what I do, I can't seem to enforce the cooldown. I'm using a database column last_train_time which I have verified is being correctly updated upon a train, as is the stat, however, I cannot seem to force the cooldown - a player is able to bypass the cooldown by just refreshing and resubmitting, or is able to train again without my error message showing and completely bypassing the cooldown.

I've tried multiple versions of code and have recoded the page three times so far to no avail, I just cannot fix this problem. I've read the guidance about not posting a wall of code, but seeing as I have no idea which part I am making an error in, here is the full 130 or so line code for the file - I'd appreciate any help!

PHP:
<?php
session_start();
require_once 'db_connect.php';

if (!isset($_SESSION['user_id'])) {
    header("Location: login.php");
    exit();
}

$user_id = $_SESSION['user_id'];

// Fetch user details
$sql = "SELECT strength, defense, speed, agility, health, gold, in_hospital, last_train_time, last_super_train_time
        FROM users WHERE id = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("i", $user_id);
$stmt->execute();
$user = $stmt->get_result()->fetch_assoc();

$now = time();
$train_cooldown = 1800; // 30 minutes in seconds
$super_train_cooldown = 43200; // 12 hours in seconds

$last_train_time = strtotime($user['last_train_time']);
$last_super_train_time = strtotime($user['last_super_train_time']);

$feedback = "";

// Check if the player is in the hospital
if ($user['in_hospital']) {
    echo "You cannot use the gym whilst in hospital.";
    echo "<br><a href='hospital.php'>Go to Hospital</a>";
    exit();
}

// Check if the player has at least 50 health
if ($user['health'] < 50) {
    echo "You are currently too injured to use the gym, please wait until you have more health.";
    echo "<br><a href='home.php'>Home</a>";
    echo " | <a href='hospital.php'>Go to Hospital</a>";
    echo " | <a href='inventory.php'>Go to Inventory</a>";
    exit();
}

// Handle training requests
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if (isset($_POST['train_type']) && isset($_POST['stat'])) {
        $train_type = $_POST['train_type'];
        $stat = $_POST['stat'];

        if (!in_array($stat, ['strength', 'defense', 'speed', 'agility'])) {
            $feedback = "Invalid stat selection.";
        } else {
            if ($train_type === 'train') {
                if (($now - $last_train_time) < $train_cooldown) {
                    $remaining_time = ceil(($train_cooldown - ($now - $last_train_time)) / 60);
                    $feedback = "You have recently trained, and must wait 30 minutes between training sessions. Cooldown remaining: $remaining_time minutes.";
                } else {
                    $increase = $user[$stat] * 0.0001;
                    $user[$stat] += $increase;

                    // Update stat and last_train_time in the database
                    $stmt = $conn->prepare("UPDATE users SET $stat = ?, last_train_time = NOW() WHERE id = ?");
                    $stmt->bind_param("di", $user[$stat], $user_id);
                    $stmt->execute();

                    $feedback = "Your $stat increased by " . number_format($increase, 8) . "!";
                }
            } elseif ($train_type === 'super_train') {
                if (($now - $last_super_train_time) < $super_train_cooldown) {
                    $remaining_time = ceil(($super_train_cooldown - ($now - $last_super_train_time)) / 3600);
                    $feedback = "You have recently used super train and must wait 12 hours between sessions. Cooldown remaining: $remaining_time hours.";
                } else {
                    $increase = $user[$stat] * 0.001;
                    $user[$stat] += $increase;

                    // Update stat and last_super_train_time in the database
                    $stmt = $conn->prepare("UPDATE users SET $stat = ?, last_super_train_time = NOW() WHERE id = ?");
                    $stmt->bind_param("di", $user[$stat], $user_id);
                    $stmt->execute();

                    $feedback = "Your $stat super increased by " . number_format($increase, 8) . "!";
                }
            } else {
                $feedback = "Invalid training type.";
            }
        }
    } else {
        $feedback = "Please select a training type and stat.";
    }
}
?>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Gym</title>
</head>
<body>
<h2>Welcome to the Gym</h2>

<p>Your current stats:</p>
<ul>
    <li>Strength: <?= number_format($user['strength'], 8) ?></li>
    <li>Defense: <?= number_format($user['defense'], 8) ?></li>
    <li>Speed: <?= number_format($user['speed'], 8) ?></li>
    <li>Agility: <?= number_format($user['agility'], 8) ?></li>
    <li>Health: <?= $user['health'] ?> / 5000</li>
    <li>Gold: <?= $user['gold'] ?></li>
</ul>

<form method="POST">
    <h3>Select a training type:</h3>
    <input type="radio" name="train_type" value="train" required> Regular Train<br>
    <input type="radio" name="train_type" value="super_train" required> Super Train<br>

    <h3>Select a stat to train:</h3>
    <input type="radio" name="stat" value="strength" required> Strength<br>
    <input type="radio" name="stat" value="defense" required> Defense<br>
    <input type="radio" name="stat" value="speed" required> Speed<br>
    <input type="radio" name="stat" value="agility" required> Agility<br><br>

    <button type="submit">Train</button>
</form>

<p><?= $feedback ?></p>

<a href="home.php">Home</a>
<a href="battle.php">Battle</a>
<a href="hospital.php">Hospital</a>
<a href="gym_c.php">Cooldowns</a>
</body>
</html>
you need to make sure that the server side logic checks the last train time before allowing the palyer to train again
 
Back
Top Bottom