minetest-gamehub/gamehub/sql.lua

669 lines
16 KiB
Lua

--[[
gamehub mod (C) shivajiva101@hotmail.com 2019
This file is part of gamehub.
gamehub is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
gamehub is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with gamehub. If not, see <https://www.gnu.org/licenses/>.
]]
-- localise access to the db handler
local _sql = gamehub.sql
-- localise db
local db = _sql.open(minetest.get_worldpath().."/gamehub.sqlite") -- connection
-- Create db:exec wrapper for error reporting
-- @param stmt: sql statement to execute
-- @return nothing
local function db_exec(stmt)
if db:exec(stmt) ~= _sql.OK then
minetest.log("error", "[Gamehub] Sqlite ERROR: " .. db:errcode() ..
" " .. db:errmsg())
end
end
---------------------
--- DB TABLES ---
---------------------
local create_db = [[
CREATE TABLE IF NOT EXISTS game (
name VARCHAR PRIMARY KEY,
type INTEGER,
pos VARCHAR,
facing VARCHAR,
reward VARCHAR,
cap INTEGER,
privs VARCHAR,
author VARCHAR,
credits VARCHAR,
description VARCHAR,
created INTEGER,
played INTEGER,
completed INTEGER,
items VARCHAR,
active BOOLEAN DEFAULT (0),
data VARCHAR DEFAULT ('return = {}')
);
CREATE TABLE IF NOT EXISTS player (
name VARCHAR PRIMARY KEY,
game INTEGER,
privs VARCHAR,
pos VARCHAR,
facing VARCHAR,
counters VARCHAR DEFAULT ('return = {}'),
data VARCHAR DEFAULT ('return = {}'),
created INTEGER
);
CREATE TABLE IF NOT EXISTS bank (
name VARCHAR PRIMARY KEY,
coins INTEGER,
created INTEGER
);
CREATE TABLE IF NOT EXISTS shop (
item VARCHAR PRIMARY KEY,
moq INTEGER,
buy INTEGER,
sell INTEGER,
created INTEGER
);
CREATE TABLE IF NOT EXISTS jail (
name VARCHAR PRIMARY KEY,
reason VARCHAR,
source VARCHAR,
created INTEGER,
expires INTEGER
);
CREATE TABLE IF NOT EXISTS settings (
data VARCHAR
);
CREATE TABLE IF NOT EXISTS stats (
data VARCHAR
);
]]
--create tables if reqd
db_exec(create_db)
----------------
-- QUERIES ---
----------------
-- Loads games into gamehub.game table
-- @return nothing
local function load_games()
local q = "SELECT * FROM game;"
for row in db:nrows(q) do
row.pos = minetest.string_to_pos(row.pos)
row.facing = minetest.deserialize(row.facing)
row.privs = minetest.string_to_privs(row.privs)
row.active = row.active == 1
row.data = minetest.deserialize(row.data)
gamehub.game[row.name] = row
end
end
load_games()
-- Loads shop items into gamehub.shop table
-- @return nothing
local function load_shop()
local q = "SELECT * FROM shop;"
for row in db:nrows(q) do
gamehub.shop[row.item] = row
end
end
load_shop()
-- Loads jailed players into gamehub.jail.roll table
-- @return nothing
local function load_jail()
local q = "SELECT * FROM jail;"
for row in db:nrows(q) do
gamehub.jail.roll[row.name] = row
end
end
load_jail()
-- Loads internal settings into gamehub.settings table
-- @return nothing
local function load_settings()
local q = "SELECT * FROM settings;"
local it, state = db:nrows(q)
local row = it(state)
if row then
gamehub.settings = minetest.deserialize(row.data)
end
end
load_settings()
-- Load a players data unpacking as necessary
-- @param name: playes name
-- @return data as table
gamehub.load_player = function(name)
local q = ([[
SELECT * from player WHERE name = '%s' LIMIT 1;
]]):format(name)
local it, state = db:nrows(q)
local row = it(state)
if row then
-- unpack
row.pos = minetest.string_to_pos(row.pos)
row.facing = minetest.deserialize(row.facing)
row.privs = minetest.string_to_privs(row.privs)
row.counters = minetest.deserialize(row.counters)
row.data = minetest.deserialize(row.data)
return row
end
end
-- Load a players money into gamehub.bank table
-- @param name: players name
-- @return nothing
gamehub.load_bank_account = function(name)
local q = ([[
SELECT * from bank WHERE name = '%s' LIMIT 1;
]]):format(name)
local it, state = db:nrows(q)
local row = it(state)
if row then
gamehub.bank[name] = row
end
end
-- Loads internal settings into gamehub.settings table
-- @return nothing
local function load_stats()
local q = "SELECT * FROM stats;"
local it, state = db:nrows(q)
local row = it(state)
if row then
gamehub.stats = minetest.deserialize(row.data)
end
end
load_stats()
-------------------
--- INSERTS ---
-------------------
-- Create & cache a new game record
-- @param data: table of params
-- @return nothing
gamehub.new_game = function(data)
-- prepare data
local pos_string = minetest.pos_to_string(vector.round(data.pos))
local facing = minetest.serialize(data.facing)
local priv_string = minetest.privs_to_string(data.privs)
local active = 0
local data_string = minetest.serialize(data.data)
if data.active then active = 1 end
local stmt = ([[
INSERT INTO game (
name,
type,
pos,
facing,
reward,
cap,
privs,
author,
credits,
description,
created,
played,
completed,
items,
active,
data
) VALUES ('%s','%s','%s','%s',%i,%i,'%s','%s','%s','%s',
%i,%i,%i,'%s',%i,'%s');]]):format(data.name,data.type,
pos_string,facing,data.reward,data.cap,priv_string,data.author,
data.credits,data.description,data.created,data.played,
data.completed,data.items,active,data_string)
db_exec(stmt)
-- cache
gamehub.game[data.name] = data
end
-- Create & cache a new shop stock item record
-- @param item: item string
-- @param buy: price the shop pays for the item
-- @param sell: price the shop sells the item for
-- @param moq: minimum qty the player can buy
-- @return nothing
gamehub.new_shop_item = function(item, buy, sell, moq)
local ts = os.time()
local stmt = ([[
INSERT INTO shop (
item,
moq,
buy,
sell,
created
) VALUES ('%s',%i,%i,%i,%i);
]]):format(item, moq, buy, sell, ts)
db_exec(stmt)
-- cache
gamehub.shop[item] = {
item = item,
moq = moq,
buy = buy,
sell = sell,
created = ts
}
end
-- Create & cache a new player record
-- @param name: players name
-- @return nothing
gamehub.new_player = function(name)
local ts = os.time()
local game = "world"
local privs = minetest.get_player_privs(name)
local privS = minetest.privs_to_string(privs)
local player = minetest.get_player_by_name(name)
local pos = player:get_pos()
local poS = minetest.pos_to_string(vector.round(pos))
local facing = {
h = player:get_look_horizontal(), v = player:get_look_vertical()
}
local facinG = minetest.serialize(facing)
local counters = minetest.serialize({})
local data = minetest.serialize({})
local stmt = ([[
INSERT INTO player (
name,
game,
privs,
pos,
facing,
counters,
data,
created
) VALUES ('%s','%s','%s','%s','%s','%s','%s',%i);
]]):format(name, game, privS, poS, facinG, counters, data, ts)
db_exec(stmt)
-- cache
gamehub.player[name] = {
name = name,
game = game,
pos = pos,
facing = facing,
counters = {},
data = {},
created = ts
}
gamehub.privs[name] = privs
end
-- Create & cache a new jail record
-- @param name: jailed player name string
-- @param reason: reason string
-- @param source: name string of player creating the record
-- @param expires: expiry time as utc integer
-- @return nothing
gamehub.jail.new_record = function(name, reason, source, expires)
local ts = os.time()
local stmt = ([[
INSERT INTO jail VALUES ('%s','%s','%s',%i,%i)
]]):format(name, reason, source, ts, expires)
db_exec(stmt)
gamehub.jail[name] = {
name = name,
reason = reason,
source = source,
created = ts,
expires = expires
}
end
-- Create & cache a new bank account record
-- @param name: players name
-- @return nothing
gamehub.new_bank_account = function(name)
local ts = os.time()
local stmt = ([[
INSERT INTO bank VALUES ('%s',%i,%i)
]]):format(name, 100, ts)
db_exec(stmt)
gamehub.bank[name] = {
name=name,
coins=100,
created=ts
}
end
-- Create new settings record
-- @return nothing
gamehub.new_settings_data = function()
local data = minetest.serialize(gamehub.settings)
local stmt = ([[
INSERT INTO settings VALUES ('%s');
]]):format(data)
db_exec(stmt)
end
-- Create new stats record
-- @return nothing
gamehub.new_stats_data = function()
local data = minetest.serialize(gamehub.stats)
local stmt = ([[
INSERT INTO stats VALUES ('%s');
]]):format(data)
db_exec(stmt)
end
-------------------
--- UPDATES ---
-------------------
-- Updates a game record field
-- @param name: game name string
-- @param field: field name string
-- @param value: new value
-- @return nothing
gamehub.update_game_field = function(name, field)
local value = gamehub.game[name][field]
-- format
if field == "pos" then
value = minetest.pos_to_string(vector.round(value))
elseif field == "privs" then
value = minetest.privs_to_string(value)
elseif field == "data" or field == "facing" then
value = minetest.serialize(value)
elseif field == "active" and value then
value = 1
end
-- db
local stmt
if type(value) == "string" then
stmt = ([[
UPDATE game SET %s = '%s' WHERE name = '%s' LIMIT 1;
]]):format(field, value, name)
else
stmt = ([[
UPDATE game SET %s = %i WHERE name = '%s' LIMIT 1;
]]):format(field, value, name)
end
db_exec(stmt)
end
-- Updates & caches game information in the record
-- @param data: table of game fields
-- @return nothing
gamehub.update_game_form = function(game)
local active = 0
if game.active then active = 1 end
local data = minetest.serialize(game.data)
local stmt = ([[
UPDATE game SET
type = '%s',
privs = '%s',
author = '%s',
credits = '%s',
description = '%s',
reward = %i,
cap = %i,
items = '%s',
active = %i,
data = '%s'
WHERE name = '%s' LIMIT 1;
]]):format(game.type, game.privs, game.author, game.credits,
game.description, game.reward, game.cap, game.items, active,
data, game.name)
db_exec(stmt)
-- cache
gamehub.game[game.name].type = game.type
gamehub.game[game.name].privs = minetest.string_to_privs(game.privs)
gamehub.game[game.name].author = game.author
gamehub.game[game.name].credits = game.credits
gamehub.game[game.name].description = game.description
gamehub.game[game.name].items = game.items
gamehub.game[game.name].reward = game.reward
gamehub.game[game.name].cap = game.cap
gamehub.game[game.name].active = game.active
gamehub.game[game.name].data = game.data
end
-- Updates game record play count
-- @param game: game name string
-- @return nothing
gamehub.update_game_played = function(game)
local stmt = ([[
UPDATE game SET played = %i WHERE name = '%s' LIMIT 1;
]]):format(gamehub.game[game].played, game)
db_exec(stmt)
end
-- Updates game record completed count
-- @param game: game name string
-- @return nothing
gamehub.update_game_completed = function(game)
local stmt = ([[
UPDATE game SET completed = %i WHERE name = '%s';
]]):format(gamehub.game[game].completed, game)
db_exec(stmt)
end
-- Update stats record
-- @return nothing
gamehub.update_stats = function()
local data = minetest.serialize(gamehub.stats)
local stmt = ([[
UPDATE stats SET data = '%s'
]]):format(data)
db_exec(stmt)
end
-- Updates player record on departure
-- @param player: player object
-- @return nothing
gamehub.update_on_leaveplayer = function(player)
local name = player:get_player_name()
local game = gamehub.player[name].game
local counters = minetest.serialize(gamehub.player[name].counters)
local data = minetest.serialize(gamehub.player[name].data)
-- default sql string for a subgame
local stmt = ([[
UPDATE player SET
game = '%s',
counters = '%s',
data = '%s'
WHERE name = '%s' LIMIT 1;
]]):format(game,counters,data,name)
if game == "world" then
local pos = minetest.pos_to_string(vector.round(player:get_pos()))
local facing = minetest.serialize({
h = player:get_look_horizontal(),
v = player:get_look_vertical()
})
-- replace
stmt = ([[
UPDATE player SET
game = '%s',
pos = '%s',
facing = '%s',
counters = '%s',
data = '%s'
WHERE name = '%s' LIMIT 1;
]]):format(game,pos,facing,counters,data,name)
end
db_exec(stmt)
end
-- Updates player position in record
-- @param name: player name string
-- @return nothing
gamehub.update_player_pos = function(name)
local pos, facing, stmt
pos = minetest.pos_to_string(gamehub.player[name].pos)
facing = minetest.serialize(gamehub.player[name].facing)
stmt = ([[
UPDATE player SET
pos = '%s',
facing = '%s'
WHERE name = '%s' LIMIT 1;
]]):format(pos, facing, name)
db_exec(stmt)
end
-- Updates a player record field
-- @param name: player name string
-- @param field: field name string
-- @return nothing
gamehub.update_player_field = function(name, field)
local value = gamehub.player[name][field]
if field == "counters" or field == "data" then
value = minetest.serialize(value)
elseif field == "privs" then
value = minetest.privs_to_string(gamehub.privs[name])
end
local stmt
if type(value) == "string" then
stmt = ([[
UPDATE player SET %s = '%s' WHERE name = '%s' LIMIT 1;
]]):format(field, value, name)
else
stmt = ([[
UPDATE player SET %s = %i WHERE name = '%s' LIMIT 1;
]]):format(field, value, name)
end
db_exec(stmt)
end
-- Updates shop item record
-- @param item: item name string
-- @param buy: price the shop pays
-- @param sell: price the buyer pays
-- @param moq: minimum order quantity
-- @return nothing
gamehub.update_shop_item = function(item, buy, sell, moq)
-- cache
gamehub.shop[item].moq = moq
gamehub.shop[item].buy = buy
gamehub.shop[item].sell = sell
-- db
local stmt = ([[
UPDATE shop SET moq = %i, buy = %i, sell = %i
WHERE name = '%s' LIMIT 1;
]]):format(moq, buy, sell, item)
db_exec(stmt)
end
-- Update game record play count
-- @param name: player name string
-- @return nothing
gamehub.update_bank_account = function(name)
local balance = gamehub.bank[name].coins
local stmt = ([[
UPDATE bank SET coins = %i WHERE name = '%s' LIMIT 1;
]]):format(balance, name)
db_exec(stmt)
end
-- Updates data in settings record
-- @return nothing
gamehub.update_setting_data = function()
local data = minetest.serialize(gamehub.settings)
local stmt = ([[
UPDATE settings SET data = '%s';
]]):format(data)
db_exec(stmt)
end
-------------------
--- DELETES ---
-------------------
-- Deletes a game record
-- @param name: game name string
-- @return nothing
gamehub.delete_game = function(name)
local stmt = ([[
DELETE FROM game WHERE name = '%s' LIMIT 1;
]]):format(name)
db_exec(stmt)
gamehub.game[name] = nil -- update cache
end
-- Deletes a player record
-- @param name: player name string
-- @return nothing
gamehub.delete_player = function(name)
local stmt = ([[
DELETE FROM player WHERE name = '%s'
]]):format(name)
db_exec(stmt)
gamehub.player[name] = nil -- update cache
--gamehub.delete_account(name)
end
-- Deletes a jail record
-- @param name: player name string
-- @return nothing
gamehub.jail.delete_record = function(name)
local stmt = ([[
DELETE FROM jail WHERE name = '%s';
]]):format(name)
db_exec(stmt)
gamehub.jail[name] = nil -- update cache
end
-- Deletes a shop item record
-- @param item: item name string
-- @return nothing
gamehub.delete_shop_item = function(item)
local stmt = ([[
DELETE FROM shop WHERE item = '%s'
]]):format(item)
db_exec(stmt)
gamehub.shop[item] = nil -- update cache
end
-- Deletes players bank account record
-- @param name: player name string
-- @return nothing
gamehub.delete_bank_account = function(name)
local stmt = ([[
DELETE FROM bank WHERE name = '%s';
]]):format(name)
db_exec(stmt)
gamehub.bank[name] = nil
end