Files
tws-zombies-scripts/t5/tws_bank.gsc
T

230 lines
4.9 KiB
Plaintext

/*
tws_bank.gsc — persistent per-player bank, MariaDB-backed. See NOTES.md
for the DB-first / spawn-dispatch / command surface invariants.
*/
#include maps\_utility;
#include common_scripts\utility;
init()
{
if (GetDvar(#"zombiemode") != "1")
return;
scripts\sp\tws_common::ensure_config();
bank_init();
scripts\sp\tws_common::register_spawn_handler(::bank_load);
}
bank_init()
{
if (!isDefined(level.db_probe_done) || !level.db_probe_done)
level thread bank_deferred_start();
else
bank_start();
}
bank_deferred_start()
{
level waittill("db_probe_done");
bank_start();
}
bank_start()
{
if (!level.db_online)
return;
bank_register_commands();
}
bank_register_commands()
{
command::add_sv("bank", ::bank_cmd_bank);
command::add_sv("deposit", ::bank_cmd_deposit);
command::add_sv("withdraw", ::bank_cmd_withdraw);
}
// DIAGNOSTIC STUB — bare minimum to isolate the connect-handshake crash.
// Do not restore the getGuid / mysql body until this passes join.
bank_load()
{
self endon("disconnect");
self iPrintLnBold("^2bank_load: entered");
self.bank = 0;
self.bank_unavailable = false;
self iPrintLnBold("^2bank_load: done");
}
bank_available(player)
{
if (!level.db_online)
{
player iPrintLnBold("^1Bank offline");
return 0;
}
if (is_true(player.bank_unavailable))
{
player iPrintLnBold("^1Bank unavailable for your account");
return 0;
}
if (!isDefined(player.bank))
{
player iPrintLnBold("^3Bank still loading, try again");
return 0;
}
return 1;
}
bank_cmd_bank(args)
{
if (!bank_available(self))
return;
self iPrintLnBold("Bank: " + self.bank + " Score: " + self.score);
}
bank_cmd_deposit(args)
{
if (!bank_available(self))
return;
if (args.size < 2)
{
self iPrintLnBold("Usage: deposit <amount>");
return;
}
amount = int(args[1]);
if (amount <= 0)
{
self iPrintLnBold("^1Amount must be positive");
return;
}
if (amount > self.score)
{
self iPrintLnBold("^1Not enough score");
return;
}
self thread bank_deposit_write(amount);
}
bank_cmd_withdraw(args)
{
if (!bank_available(self))
return;
if (args.size < 2)
{
self iPrintLnBold("Usage: withdraw <amount>");
return;
}
amount = int(args[1]);
if (amount <= 0)
{
self iPrintLnBold("^1Amount must be positive");
return;
}
if (amount > self.bank)
{
self iPrintLnBold("^1Insufficient balance");
return;
}
self thread bank_withdraw_write(amount);
}
// upsert: first deposit creates the row, later ones increment
bank_deposit_write(amount)
{
self endon("disconnect");
name = bank_display_name(self);
server_id = bank_server_id();
q = mysql::prepared_statement(
"INSERT INTO bank (guid, balance, last_name, last_server) VALUES (?, ?, ?, ?) " +
"ON DUPLICATE KEY UPDATE balance = balance + VALUES(balance), " +
"last_name = VALUES(last_name), last_server = VALUES(last_server)",
self.bank_guid, amount, name, server_id);
q waittill("done", result, affected_rows, error);
if (isDefined(error) && error != "")
{
self iPrintLnBold("^1Deposit failed");
return;
}
// score can change between dispatch and write completing — refund if so
if (amount > self.score)
{
mysql::prepared_statement(
"UPDATE bank SET balance = balance - ? WHERE guid = ?",
amount, self.bank_guid);
self iPrintLnBold("^1Deposit rolled back (score changed)");
return;
}
self.score -= amount;
self.bank += amount;
self iPrintLnBold("Deposited " + amount + " Bank: " + self.bank);
}
// balance guard lives in the WHERE so concurrent commands can't overdraw
bank_withdraw_write(amount)
{
self endon("disconnect");
q = mysql::prepared_statement(
"UPDATE bank SET balance = balance - ? WHERE guid = ? AND balance >= ?",
amount, self.bank_guid, amount);
q waittill("done", result, affected_rows, error);
if (isDefined(error) && error != "")
{
self iPrintLnBold("^1Withdraw failed");
return;
}
if (!isDefined(affected_rows) || affected_rows == 0)
{
self iPrintLnBold("^1Insufficient balance");
self thread bank_load(); // re-sync in case memory drifted from DB
return;
}
self.score += amount;
self.bank -= amount;
self iPrintLnBold("Withdrew " + amount + " Bank: " + self.bank);
}
bank_display_name(player)
{
if (isDefined(player.name))
return player.name;
if (isDefined(player.playername))
return player.playername;
return "unknown";
}
bank_server_id()
{
id = GetDvar("tws_server_id");
if (id == "")
return "unknown";
return id;
}