230 lines
4.9 KiB
Plaintext
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;
|
|
}
|