diff --git a/.github/workflows/ci_suite.yml b/.github/workflows/ci_suite.yml index 0dc42dd6329..4b537353d3a 100644 --- a/.github/workflows/ci_suite.yml +++ b/.github/workflows/ci_suite.yml @@ -89,8 +89,9 @@ jobs: - name: Setup database run: | sudo systemctl start mysql - mysql -u root -proot -e 'CREATE DATABASE tg_ci;' - mysql -u root -proot tg_ci < SQL/tgstation_schema.sql + mysql -u root -proot -e 'CREATE DATABASE ss13_ci;' + mysql -u root -proot ss13_ci < SQL/database_schema_prefixed.sql + mysql -u root -proot ss13_ci < SQL/unified_schema.sql # mysql -u root -proot -e 'CREATE DATABASE tg_ci_prefixed;' # mysql -u root -proot tg_ci_prefixed < SQL/tgstation_schema_prefixed.sql - name: Install rust-g diff --git a/SQL/database_changelog.md b/SQL/database_changelog.md new file mode 100644 index 00000000000..8325a8ab26c --- /dev/null +++ b/SQL/database_changelog.md @@ -0,0 +1,21 @@ +# Changelog + +This covers the **game** database. The unified database has no unified schema + changelog yet. + +## Schema Versioning + +Any time you make a change to the schema files, remember to increment the database schema version. Generally increment the minor number, major should be reserved for significant changes to the schema. Both values go up to 255. + +The latest database version is 5.12; The query to update the schema revision table is: + +INSERT INTO `schema_revision` (`major`, `minor`) VALUES (5, 12); +or +INSERT INTO `SS13_schema_revision` (`major`, `minor`) VALUES (5, 12); + +In any query remember to add a prefix to the table names if you use one. + +## Changelog + +### 6/16/22 - 1.1 - silicons + +Database migrated to DBCore. Schema will start at MAJOR 1, MINOR 1. diff --git a/SQL/database_schema.sql b/SQL/database_schema.sql new file mode 100644 index 00000000000..4ed8abc8ab5 --- /dev/null +++ b/SQL/database_schema.sql @@ -0,0 +1,227 @@ +/** + * make sure to bump schema version and mark changes in database_changelog.md! + * + * default prefix is rp_ + * find replace case sensitive %_PREFIX_% + * PRESERVE ANY vr_'s! We need to replace those tables and features at some point, that's how we konw. + **/ + +-- +-- Table structure for table `schema_revision` +-- +CREATE TABLE IF NOT EXISTS `%_PREFIX_%schema_revision` ( + `major` TINYINT(3) unsigned NOT NULL, + `minor` TINYINT(3) unsigned NOT NULL, + `date` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`major`, `minor`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- +-- Table structure for table `round` +-- +CREATE TABLE IF NOT EXISTS `%_PREFIX_%round` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `initialize_datetime` DATETIME NOT NULL, + `start_datetime` DATETIME NULL, + `shutdown_datetime` DATETIME NULL, + `end_datetime` DATETIME NULL, + `server_ip` INT(10) UNSIGNED NOT NULL, + `server_port` SMALLINT(5) UNSIGNED NOT NULL, + `commit_hash` CHAR(40) NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE IF NOT EXISTS `%_PREFIX_%admin` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `ckey` varchar(32) NOT NULL, + `rank` varchar(32) NOT NULL DEFAULT 'Administrator', + `level` int(2) NOT NULL DEFAULT '0', + `flags` int(16) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE IF NOT EXISTS `%_PREFIX_%admin_log` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `datetime` datetime NOT NULL, + `adminckey` varchar(32) NOT NULL, + `adminip` varchar(18) NOT NULL, + `log` text NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE IF NOT EXISTS `%_PREFIX_%ban` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `bantime` datetime NOT NULL, + `serverip` varchar(32) NOT NULL, + `bantype` varchar(32) NOT NULL, + `reason` text NOT NULL, + `job` varchar(32) DEFAULT NULL, + `duration` int(11) NOT NULL, + `rounds` int(11) DEFAULT NULL, + `expiration_time` datetime NOT NULL, + `ckey` varchar(32) NOT NULL, + `computerid` varchar(32) NOT NULL, + `ip` varchar(32) NOT NULL, + `a_ckey` varchar(32) NOT NULL, + `a_computerid` varchar(32) NOT NULL, + `a_ip` varchar(32) NOT NULL, + `who` text NOT NULL, + `adminwho` text NOT NULL, + `edits` text, + `unbanned` tinyint(1) DEFAULT NULL, + `unbanned_datetime` datetime DEFAULT NULL, + `unbanned_ckey` varchar(32) DEFAULT NULL, + `unbanned_computerid` varchar(32) DEFAULT NULL, + `unbanned_ip` varchar(32) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE IF NOT EXISTS `%_PREFIX_%feedback` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `time` datetime NOT NULL, + `round_id` int(8) NOT NULL, + `var_name` varchar(32) NOT NULL, + `var_value` int(16) DEFAULT NULL, + `details` text, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 ; + +CREATE TABLE IF NOT EXISTS `%_PREFIX_%player` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `ckey` varchar(32) NOT NULL, + `firstseen` datetime NOT NULL, + `lastseen` datetime NOT NULL, + `ip` varchar(18) NOT NULL, + `computerid` varchar(32) NOT NULL, + `lastadminrank` varchar(32) NOT NULL DEFAULT 'Player', + PRIMARY KEY (`id`), + UNIQUE KEY `ckey` (`ckey`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE IF NOT EXISTS `%_PREFIX_%poll_option` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `pollid` int(11) NOT NULL, + `text` varchar(255) NOT NULL, + `percentagecalc` tinyint(1) NOT NULL DEFAULT '1', + `minval` int(3) DEFAULT NULL, + `maxval` int(3) DEFAULT NULL, + `descmin` varchar(32) DEFAULT NULL, + `descmid` varchar(32) DEFAULT NULL, + `descmax` varchar(32) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE IF NOT EXISTS `%_PREFIX_%poll_question` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `polltype` varchar(16) NOT NULL DEFAULT 'OPTION', + `starttime` datetime NOT NULL, + `endtime` datetime NOT NULL, + `question` varchar(255) NOT NULL, + `adminonly` tinyint(1) DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE IF NOT EXISTS `%_PREFIX_%poll_textreply` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `datetime` datetime NOT NULL, + `pollid` int(11) NOT NULL, + `ckey` varchar(32) NOT NULL, + `ip` varchar(18) NOT NULL, + `replytext` text NOT NULL, + `adminrank` varchar(32) NOT NULL DEFAULT 'Player', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE IF NOT EXISTS `%_PREFIX_%poll_vote` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `datetime` datetime NOT NULL, + `pollid` int(11) NOT NULL, + `optionid` int(11) NOT NULL, + `ckey` varchar(255) NOT NULL, + `ip` varchar(16) NOT NULL, + `adminrank` varchar(32) NOT NULL, + `rating` int(2) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE IF NOT EXISTS `%_PREFIX_%privacy` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `datetime` datetime NOT NULL, + `ckey` varchar(32) NOT NULL, + `option` varchar(128) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE IF NOT EXISTS `%_PREFIX_%vr_player_hours` ( + `ckey` varchar(32) CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL, + `department` varchar(64) CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL, + `hours` double NOT NULL, + PRIMARY KEY (`ckey`,`department`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE IF NOT EXISTS `%_PREFIX_%death` ( + `id` INT(11) NOT NULL AUTO_INCREMENT , + `pod` TEXT NOT NULL COMMENT 'Place of death' , + `coord` TEXT NOT NULL COMMENT 'X, Y, Z POD' , + `tod` DATETIME NOT NULL COMMENT 'Time of death' , + `job` TEXT NOT NULL , + `special` TEXT NOT NULL , + `name` TEXT NOT NULL , + `byondkey` TEXT NOT NULL , + `laname` TEXT NOT NULL COMMENT 'Last attacker name' , + `lakey` TEXT NOT NULL COMMENT 'Last attacker key' , + `gender` TEXT NOT NULL , + `bruteloss` INT(11) NOT NULL , + `brainloss` INT(11) NOT NULL , + `fireloss` INT(11) NOT NULL , + `oxyloss` INT(11) NOT NULL , + PRIMARY KEY (`id`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE IF NOT EXISTS `%_PREFIX_%karma` ( + `id` INT(11) NOT NULL AUTO_INCREMENT , + `spendername` TEXT NOT NULL , + `spenderkey` TEXT NOT NULL , + `receivername` TEXT NOT NULL , + `receiverkey` TEXT NOT NULL , + `receiverrole` TEXT NOT NULL , + `receiverspecial` TEXT NOT NULL , + `isnegative` TINYINT(1) NOT NULL , + `spenderip` TEXT NOT NULL , + `time` DATETIME NOT NULL , + PRIMARY KEY (`id`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE IF NOT EXISTS `%_PREFIX_%karmatotals` ( + `id` INT(11) NOT NULL AUTO_INCREMENT , + `byondkey` TEXT NOT NULL , + `karma` INT(11) NOT NULL , + PRIMARY KEY (`id`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE IF NOT EXISTS `%_PREFIX_%library` ( + `id` INT(11) NOT NULL AUTO_INCREMENT , + `author` TEXT NOT NULL , + `title` TEXT NOT NULL , + `content` TEXT NOT NULL , + `category` TEXT NOT NULL , + PRIMARY KEY (`id`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE IF NOT EXISTS `%_PREFIX_%population` ( + `id` INT(11) NOT NULL AUTO_INCREMENT , + `playercount` INT(11) NULL DEFAULT NULL , + `admincount` INT(11) NULL DEFAULT NULL , + `time` DATETIME NOT NULL , + PRIMARY KEY (`id`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE IF NOT EXISTS `%_PREFIX_%connection_log` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `datetime` datetime NOT NULL, + `serverip` varchar(16) NOT NULL, + `ckey` varchar(32) NOT NULL, + `ip` varchar(16) NOT NULL, + `computerid` varchar(32) NOT NULL, + PRIMARY KEY(`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; diff --git a/SQL/database_schema_prefixed.sql b/SQL/database_schema_prefixed.sql new file mode 100644 index 00000000000..27628e32ced --- /dev/null +++ b/SQL/database_schema_prefixed.sql @@ -0,0 +1,227 @@ +/** + * make sure to bump schema version and mark changes in database_changelog.md! + * + * default prefix is rp_ + * find replace case sensitive %_PREFIX_% + * PRESERVE ANY vr_'s! We need to replace those tables and features at some point, that's how we konw. + **/ + +-- +-- Table structure for table `schema_revision` +-- +CREATE TABLE IF NOT EXISTS `rp_schema_revision` ( + `major` TINYINT(3) unsigned NOT NULL, + `minor` TINYINT(3) unsigned NOT NULL, + `date` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`major`, `minor`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- +-- Table structure for table `round` +-- +CREATE TABLE IF NOT EXISTS `rp_round` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `initialize_datetime` DATETIME NOT NULL, + `start_datetime` DATETIME NULL, + `shutdown_datetime` DATETIME NULL, + `end_datetime` DATETIME NULL, + `server_ip` INT(10) UNSIGNED NOT NULL, + `server_port` SMALLINT(5) UNSIGNED NOT NULL, + `commit_hash` CHAR(40) NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE IF NOT EXISTS `rp_admin` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `ckey` varchar(32) NOT NULL, + `rank` varchar(32) NOT NULL DEFAULT 'Administrator', + `level` int(2) NOT NULL DEFAULT '0', + `flags` int(16) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE IF NOT EXISTS `rp_admin_log` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `datetime` datetime NOT NULL, + `adminckey` varchar(32) NOT NULL, + `adminip` varchar(18) NOT NULL, + `log` text NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE IF NOT EXISTS `rp_ban` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `bantime` datetime NOT NULL, + `serverip` varchar(32) NOT NULL, + `bantype` varchar(32) NOT NULL, + `reason` text NOT NULL, + `job` varchar(32) DEFAULT NULL, + `duration` int(11) NOT NULL, + `rounds` int(11) DEFAULT NULL, + `expiration_time` datetime NOT NULL, + `ckey` varchar(32) NOT NULL, + `computerid` varchar(32) NOT NULL, + `ip` varchar(32) NOT NULL, + `a_ckey` varchar(32) NOT NULL, + `a_computerid` varchar(32) NOT NULL, + `a_ip` varchar(32) NOT NULL, + `who` text NOT NULL, + `adminwho` text NOT NULL, + `edits` text, + `unbanned` tinyint(1) DEFAULT NULL, + `unbanned_datetime` datetime DEFAULT NULL, + `unbanned_ckey` varchar(32) DEFAULT NULL, + `unbanned_computerid` varchar(32) DEFAULT NULL, + `unbanned_ip` varchar(32) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE IF NOT EXISTS `rp_feedback` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `time` datetime NOT NULL, + `round_id` int(8) NOT NULL, + `var_name` varchar(32) NOT NULL, + `var_value` int(16) DEFAULT NULL, + `details` text, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 ; + +CREATE TABLE IF NOT EXISTS `rp_player` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `ckey` varchar(32) NOT NULL, + `firstseen` datetime NOT NULL, + `lastseen` datetime NOT NULL, + `ip` varchar(18) NOT NULL, + `computerid` varchar(32) NOT NULL, + `lastadminrank` varchar(32) NOT NULL DEFAULT 'Player', + PRIMARY KEY (`id`), + UNIQUE KEY `ckey` (`ckey`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE IF NOT EXISTS `rp_poll_option` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `pollid` int(11) NOT NULL, + `text` varchar(255) NOT NULL, + `percentagecalc` tinyint(1) NOT NULL DEFAULT '1', + `minval` int(3) DEFAULT NULL, + `maxval` int(3) DEFAULT NULL, + `descmin` varchar(32) DEFAULT NULL, + `descmid` varchar(32) DEFAULT NULL, + `descmax` varchar(32) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE IF NOT EXISTS `rp_poll_question` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `polltype` varchar(16) NOT NULL DEFAULT 'OPTION', + `starttime` datetime NOT NULL, + `endtime` datetime NOT NULL, + `question` varchar(255) NOT NULL, + `adminonly` tinyint(1) DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE IF NOT EXISTS `rp_poll_textreply` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `datetime` datetime NOT NULL, + `pollid` int(11) NOT NULL, + `ckey` varchar(32) NOT NULL, + `ip` varchar(18) NOT NULL, + `replytext` text NOT NULL, + `adminrank` varchar(32) NOT NULL DEFAULT 'Player', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE IF NOT EXISTS `rp_poll_vote` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `datetime` datetime NOT NULL, + `pollid` int(11) NOT NULL, + `optionid` int(11) NOT NULL, + `ckey` varchar(255) NOT NULL, + `ip` varchar(16) NOT NULL, + `adminrank` varchar(32) NOT NULL, + `rating` int(2) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE IF NOT EXISTS `rp_privacy` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `datetime` datetime NOT NULL, + `ckey` varchar(32) NOT NULL, + `option` varchar(128) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE IF NOT EXISTS `rp_vr_player_hours` ( + `ckey` varchar(32) CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL, + `department` varchar(64) CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL, + `hours` double NOT NULL, + PRIMARY KEY (`ckey`,`department`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE IF NOT EXISTS `rp_death` ( + `id` INT(11) NOT NULL AUTO_INCREMENT , + `pod` TEXT NOT NULL COMMENT 'Place of death' , + `coord` TEXT NOT NULL COMMENT 'X, Y, Z POD' , + `tod` DATETIME NOT NULL COMMENT 'Time of death' , + `job` TEXT NOT NULL , + `special` TEXT NOT NULL , + `name` TEXT NOT NULL , + `byondkey` TEXT NOT NULL , + `laname` TEXT NOT NULL COMMENT 'Last attacker name' , + `lakey` TEXT NOT NULL COMMENT 'Last attacker key' , + `gender` TEXT NOT NULL , + `bruteloss` INT(11) NOT NULL , + `brainloss` INT(11) NOT NULL , + `fireloss` INT(11) NOT NULL , + `oxyloss` INT(11) NOT NULL , + PRIMARY KEY (`id`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE IF NOT EXISTS `rp_karma` ( + `id` INT(11) NOT NULL AUTO_INCREMENT , + `spendername` TEXT NOT NULL , + `spenderkey` TEXT NOT NULL , + `receivername` TEXT NOT NULL , + `receiverkey` TEXT NOT NULL , + `receiverrole` TEXT NOT NULL , + `receiverspecial` TEXT NOT NULL , + `isnegative` TINYINT(1) NOT NULL , + `spenderip` TEXT NOT NULL , + `time` DATETIME NOT NULL , + PRIMARY KEY (`id`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE IF NOT EXISTS `rp_karmatotals` ( + `id` INT(11) NOT NULL AUTO_INCREMENT , + `byondkey` TEXT NOT NULL , + `karma` INT(11) NOT NULL , + PRIMARY KEY (`id`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE IF NOT EXISTS `rp_library` ( + `id` INT(11) NOT NULL AUTO_INCREMENT , + `author` TEXT NOT NULL , + `title` TEXT NOT NULL , + `content` TEXT NOT NULL , + `category` TEXT NOT NULL , + PRIMARY KEY (`id`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE IF NOT EXISTS `rp_population` ( + `id` INT(11) NOT NULL AUTO_INCREMENT , + `playercount` INT(11) NULL DEFAULT NULL , + `admincount` INT(11) NULL DEFAULT NULL , + `time` DATETIME NOT NULL , + PRIMARY KEY (`id`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE IF NOT EXISTS `rp_connection_log` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `datetime` datetime NOT NULL, + `serverip` varchar(16) NOT NULL, + `ckey` varchar(32) NOT NULL, + `ip` varchar(16) NOT NULL, + `computerid` varchar(32) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; diff --git a/SQL/feedback_schema.sql b/SQL/feedback_schema.sql deleted file mode 100644 index ee8975263b5..00000000000 --- a/SQL/feedback_schema.sql +++ /dev/null @@ -1,127 +0,0 @@ -CREATE TABLE `erro_admin` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `ckey` varchar(32) NOT NULL, - `rank` varchar(32) NOT NULL DEFAULT 'Administrator', - `level` int(2) NOT NULL DEFAULT '0', - `flags` int(16) NOT NULL DEFAULT '0', - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1 ; - -CREATE TABLE `erro_admin_log` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `datetime` datetime NOT NULL, - `adminckey` varchar(32) NOT NULL, - `adminip` varchar(18) NOT NULL, - `log` text NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1 ; - -CREATE TABLE `erro_ban` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `bantime` datetime NOT NULL, - `serverip` varchar(32) NOT NULL, - `bantype` varchar(32) NOT NULL, - `reason` text NOT NULL, - `job` varchar(32) DEFAULT NULL, - `duration` int(11) NOT NULL, - `rounds` int(11) DEFAULT NULL, - `expiration_time` datetime NOT NULL, - `ckey` varchar(32) NOT NULL, - `computerid` varchar(32) NOT NULL, - `ip` varchar(32) NOT NULL, - `a_ckey` varchar(32) NOT NULL, - `a_computerid` varchar(32) NOT NULL, - `a_ip` varchar(32) NOT NULL, - `who` text NOT NULL, - `adminwho` text NOT NULL, - `edits` text, - `unbanned` tinyint(1) DEFAULT NULL, - `unbanned_datetime` datetime DEFAULT NULL, - `unbanned_ckey` varchar(32) DEFAULT NULL, - `unbanned_computerid` varchar(32) DEFAULT NULL, - `unbanned_ip` varchar(32) DEFAULT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1 ; - -CREATE TABLE `erro_feedback` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `time` datetime NOT NULL, - `round_id` int(8) NOT NULL, - `var_name` varchar(32) NOT NULL, - `var_value` int(16) DEFAULT NULL, - `details` text, - PRIMARY KEY (`id`) -) ENGINE=MyISAM DEFAULT CHARSET=latin1 ; - -CREATE TABLE `erro_player` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `ckey` varchar(32) NOT NULL, - `firstseen` datetime NOT NULL, - `lastseen` datetime NOT NULL, - `ip` varchar(18) NOT NULL, - `computerid` varchar(32) NOT NULL, - `lastadminrank` varchar(32) NOT NULL DEFAULT 'Player', - PRIMARY KEY (`id`), - UNIQUE KEY `ckey` (`ckey`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1 ; - -CREATE TABLE `erro_poll_option` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `pollid` int(11) NOT NULL, - `text` varchar(255) NOT NULL, - `percentagecalc` tinyint(1) NOT NULL DEFAULT '1', - `minval` int(3) DEFAULT NULL, - `maxval` int(3) DEFAULT NULL, - `descmin` varchar(32) DEFAULT NULL, - `descmid` varchar(32) DEFAULT NULL, - `descmax` varchar(32) DEFAULT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1 ; - -CREATE TABLE `erro_poll_question` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `polltype` varchar(16) NOT NULL DEFAULT 'OPTION', - `starttime` datetime NOT NULL, - `endtime` datetime NOT NULL, - `question` varchar(255) NOT NULL, - `adminonly` tinyint(1) DEFAULT '0', - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1 ; - -CREATE TABLE `erro_poll_textreply` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `datetime` datetime NOT NULL, - `pollid` int(11) NOT NULL, - `ckey` varchar(32) NOT NULL, - `ip` varchar(18) NOT NULL, - `replytext` text NOT NULL, - `adminrank` varchar(32) NOT NULL DEFAULT 'Player', - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1 ; - -CREATE TABLE `erro_poll_vote` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `datetime` datetime NOT NULL, - `pollid` int(11) NOT NULL, - `optionid` int(11) NOT NULL, - `ckey` varchar(255) NOT NULL, - `ip` varchar(16) NOT NULL, - `adminrank` varchar(32) NOT NULL, - `rating` int(2) DEFAULT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1 ; - -CREATE TABLE `erro_privacy` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `datetime` datetime NOT NULL, - `ckey` varchar(32) NOT NULL, - `option` varchar(128) NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1 ; - -CREATE TABLE `vr_player_hours` ( - `ckey` varchar(32) CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL, - `department` varchar(64) CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL, - `hours` double NOT NULL, - PRIMARY KEY (`ckey`,`department`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; \ No newline at end of file diff --git a/SQL/tgstation_schema.sql b/SQL/tgstation_schema.sql deleted file mode 100644 index b7e2e501ce1..00000000000 --- a/SQL/tgstation_schema.sql +++ /dev/null @@ -1,100 +0,0 @@ -SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0; -SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0; -SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL'; - -CREATE SCHEMA IF NOT EXISTS `mydb` DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci ; -CREATE SCHEMA IF NOT EXISTS `tgstation` DEFAULT CHARACTER SET latin1 ; -USE `mydb` ; -USE `tgstation` ; - --- ----------------------------------------------------- --- Table `tgstation`.`death` --- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `tgstation`.`death` ( - `id` INT(11) NOT NULL AUTO_INCREMENT , - `pod` TEXT NOT NULL COMMENT 'Place of death' , - `coord` TEXT NOT NULL COMMENT 'X, Y, Z POD' , - `tod` DATETIME NOT NULL COMMENT 'Time of death' , - `job` TEXT NOT NULL , - `special` TEXT NOT NULL , - `name` TEXT NOT NULL , - `byondkey` TEXT NOT NULL , - `laname` TEXT NOT NULL COMMENT 'Last attacker name' , - `lakey` TEXT NOT NULL COMMENT 'Last attacker key' , - `gender` TEXT NOT NULL , - `bruteloss` INT(11) NOT NULL , - `brainloss` INT(11) NOT NULL , - `fireloss` INT(11) NOT NULL , - `oxyloss` INT(11) NOT NULL , - PRIMARY KEY (`id`) ) -ENGINE = MyISAM -AUTO_INCREMENT = 3409 -DEFAULT CHARACTER SET = latin1; - - --- ----------------------------------------------------- --- Table `tgstation`.`karma` --- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `tgstation`.`karma` ( - `id` INT(11) NOT NULL AUTO_INCREMENT , - `spendername` TEXT NOT NULL , - `spenderkey` TEXT NOT NULL , - `receivername` TEXT NOT NULL , - `receiverkey` TEXT NOT NULL , - `receiverrole` TEXT NOT NULL , - `receiverspecial` TEXT NOT NULL , - `isnegative` TINYINT(1) NOT NULL , - `spenderip` TEXT NOT NULL , - `time` DATETIME NOT NULL , - PRIMARY KEY (`id`) ) -ENGINE = MyISAM -AUTO_INCREMENT = 943 -DEFAULT CHARACTER SET = latin1; - - --- ----------------------------------------------------- --- Table `tgstation`.`karmatotals` --- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `tgstation`.`karmatotals` ( - `id` INT(11) NOT NULL AUTO_INCREMENT , - `byondkey` TEXT NOT NULL , - `karma` INT(11) NOT NULL , - PRIMARY KEY (`id`) ) -ENGINE = MyISAM -AUTO_INCREMENT = 244 -DEFAULT CHARACTER SET = latin1; - - --- ----------------------------------------------------- --- Table `tgstation`.`library` --- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `tgstation`.`library` ( - `id` INT(11) NOT NULL AUTO_INCREMENT , - `author` TEXT NOT NULL , - `title` TEXT NOT NULL , - `content` TEXT NOT NULL , - `category` TEXT NOT NULL , - PRIMARY KEY (`id`) ) -ENGINE = MyISAM -AUTO_INCREMENT = 184 -DEFAULT CHARACTER SET = latin1; - - --- ----------------------------------------------------- --- Table `tgstation`.`population` --- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `tgstation`.`population` ( - `id` INT(11) NOT NULL AUTO_INCREMENT , - `playercount` INT(11) NULL DEFAULT NULL , - `admincount` INT(11) NULL DEFAULT NULL , - `time` DATETIME NOT NULL , - PRIMARY KEY (`id`) ) -ENGINE = MyISAM -AUTO_INCREMENT = 2544 -DEFAULT CHARACTER SET = latin1; - - - -SET SQL_MODE=@OLD_SQL_MODE; -SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS; -SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS; diff --git a/SQL/unified_schema.sql b/SQL/unified_schema.sql new file mode 100644 index 00000000000..32441c5e22e --- /dev/null +++ b/SQL/unified_schema.sql @@ -0,0 +1,17 @@ +/** + * make sure to bump schema version and mark changes in database_changelog.md! + * + * you MUST use unified_ as a prefix. + * + * unified schema for citadel, **sync changes to both servers.** + **/ + +-- +-- Table structure for table `schema_revision` +-- +CREATE TABLE IF NOT EXISTS `unified_schema_revision` ( + `major` TINYINT(3) unsigned NOT NULL, + `minor` TINYINT(3) unsigned NOT NULL, + `date` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`major`, `minor`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; diff --git a/citadel.dme b/citadel.dme index 203852991c9..f6391aab9e4 100644 --- a/citadel.dme +++ b/citadel.dme @@ -124,7 +124,9 @@ #include "code\__DEFINES\color\lum.dm" #include "code\__DEFINES\combat\clickcode.dm" #include "code\__DEFINES\controllers\_subsystems.dm" +#include "code\__DEFINES\controllers\dbcore.dm" #include "code\__DEFINES\controllers\ticker.dm" +#include "code\__DEFINES\controllers\timer.dm" #include "code\__DEFINES\dcs\flags.dm" #include "code\__DEFINES\dcs\helpers.dm" #include "code\__DEFINES\dcs\mobs\signals_mob_perspectiive.dm" @@ -399,6 +401,8 @@ #include "code\controllers\subsystem\vis_overlays.dm" #include "code\controllers\subsystem\vote.dm" #include "code\controllers\subsystem\xenoarch.dm" +#include "code\controllers\subsystem\dbcore\_dbcore.dm" +#include "code\controllers\subsystem\dbcore\query.dm" #include "code\controllers\subsystem\job\_job.dm" #include "code\controllers\subsystem\job\job_controller.dm" #include "code\controllers\subsystem\job\spawnpoints.dm" @@ -601,7 +605,6 @@ #include "code\defines\obj.dm" #include "code\defines\obj\weapon.dm" #include "code\defines\procs\AStar.dm" -#include "code\defines\procs\dbcore.dm" #include "code\defines\procs\radio.dm" #include "code\defines\procs\sd_Alert.dm" #include "code\defines\procs\statistics.dm" @@ -1683,6 +1686,7 @@ #include "code\modules\admin\verbs\ticklag.dm" #include "code\modules\admin\verbs\tripAI.dm" #include "code\modules\admin\verbs\debug\profiling.dm" +#include "code\modules\admin\verbs\debug\reestablish_db_connection.dm" #include "code\modules\admin\verbs\SDQL2\SDQL_2.dm" #include "code\modules\admin\verbs\SDQL2\SDQL_2_parser.dm" #include "code\modules\admin\verbs\SDQL2\SDQL_2_wrappers.dm" diff --git a/code/__DEFINES/_protect.dm b/code/__DEFINES/_protect.dm index 1750ff8b08d..c865bd363ed 100644 --- a/code/__DEFINES/_protect.dm +++ b/code/__DEFINES/_protect.dm @@ -7,4 +7,7 @@ }\ ##Path/CanProcCall(procname){\ return FALSE;\ +}\ +##Path/can_vv_mark(){\ + return FALSE;\ } diff --git a/code/__DEFINES/controllers/_subsystems.dm b/code/__DEFINES/controllers/_subsystems.dm index 1ffb0eac3a5..0f6974e74e0 100644 --- a/code/__DEFINES/controllers/_subsystems.dm +++ b/code/__DEFINES/controllers/_subsystems.dm @@ -4,75 +4,6 @@ //! Lots of important stuff in here, make sure you have your brain switched on //! when editing this file -//! ## DB defines -/** - * DB major schema version - * - * Update this whenever the db schema changes - * - * make sure you add an update to the schema_version stable in the db changelog - */ -//#define DB_MAJOR_VERSION 5 - -/** - * DB minor schema version - * - * Update this whenever the db schema changes - * - * make sure you add an update to the schema_version stable in the db changelog - */ -//#define DB_MINOR_VERSION 0 - -//! ## Timing subsystem -/** - * Don't run if there is an identical unique timer active - * - * if the arguments to addtimer are the same as an existing timer, it doesn't create a new timer, - * and returns the id of the existing timer - */ -#define TIMER_UNIQUE (1<<0) - -///For unique timers: Replace the old timer rather then not start this one -#define TIMER_OVERRIDE (1<<1) - -/** - * Timing should be based on how timing progresses on clients, not the server. - * - * Tracking this is more expensive, - * should only be used in conjuction with things that have to progress client side, such as - * animate() or sound() - */ -#define TIMER_CLIENT_TIME (1<<2) - -///Timer can be stopped using deltimer() -#define TIMER_STOPPABLE (1<<3) - -///prevents distinguishing identical timers with the wait variable -/// -///To be used with TIMER_UNIQUE -#define TIMER_NO_HASH_WAIT (1<<4) - -///Loops the timer repeatedly until qdeleted -/// -///In most cases you want a subsystem instead, so don't use this unless you have a good reason -#define TIMER_LOOP (1<<5) - -///Delete the timer on parent datum Destroy() and when deltimer'd -#define TIMER_DELETE_ME (1<<6) - -DEFINE_BITFIELD(timer_flags, list( - BITFIELD(TIMER_UNIQUE), - BITFIELD(TIMER_OVERRIDE), - BITFIELD(TIMER_CLIENT_TIME), - BITFIELD(TIMER_STOPPABLE), - BITFIELD(TIMER_NO_HASH_WAIT), - BITFIELD(TIMER_LOOP), - BITFIELD(TIMER_DELETE_ME), -)) - -///Empty ID define -#define TIMER_ID_NULL -1 - //! ## Initialization subsystem ///New should not call Initialize @@ -138,7 +69,8 @@ DEFINE_BITFIELD(runlevels, list( // Subsystems shutdown in the reverse of the order they initialize in // The numbers just define the ordering, they are meaningless otherwise. -#define INIT_ORDER_FAIL2TOPIC 101 +#define INIT_ORDER_FAIL2TOPIC 102 +#define INIT_ORDER_DBCORE 101 #define INIT_ORDER_INPUT 100 #define INIT_ORDER_SOUNDS 95 #define INIT_ORDER_JOBS 85 diff --git a/code/__DEFINES/controllers/dbcore.dm b/code/__DEFINES/controllers/dbcore.dm new file mode 100644 index 00000000000..7870e6d7905 --- /dev/null +++ b/code/__DEFINES/controllers/dbcore.dm @@ -0,0 +1,18 @@ +//! ## DB defines +/** + * DB major schema version + * + * Update this whenever the db schema changes + * + * make sure you add an update to the schema_version stable in the db changelog + */ +#define DB_MAJOR_VERSION 1 + +/** + * DB minor schema version + * + * Update this whenever the db schema changes + * + * make sure you add an update to the schema_version stable in the db changelog + */ +#define DB_MINOR_VERSION 1 diff --git a/code/__DEFINES/controllers/timer.dm b/code/__DEFINES/controllers/timer.dm new file mode 100644 index 00000000000..50001908061 --- /dev/null +++ b/code/__DEFINES/controllers/timer.dm @@ -0,0 +1,49 @@ +//! ## Timing subsystem +/** + * Don't run if there is an identical unique timer active + * + * if the arguments to addtimer are the same as an existing timer, it doesn't create a new timer, + * and returns the id of the existing timer + */ +#define TIMER_UNIQUE (1<<0) + +///For unique timers: Replace the old timer rather then not start this one +#define TIMER_OVERRIDE (1<<1) + +/** + * Timing should be based on how timing progresses on clients, not the server. + * + * Tracking this is more expensive, + * should only be used in conjuction with things that have to progress client side, such as + * animate() or sound() + */ +#define TIMER_CLIENT_TIME (1<<2) + +///Timer can be stopped using deltimer() +#define TIMER_STOPPABLE (1<<3) + +///prevents distinguishing identical timers with the wait variable +/// +///To be used with TIMER_UNIQUE +#define TIMER_NO_HASH_WAIT (1<<4) + +///Loops the timer repeatedly until qdeleted +/// +///In most cases you want a subsystem instead, so don't use this unless you have a good reason +#define TIMER_LOOP (1<<5) + +///Delete the timer on parent datum Destroy() and when deltimer'd +#define TIMER_DELETE_ME (1<<6) + +DEFINE_BITFIELD(timer_flags, list( + BITFIELD(TIMER_UNIQUE), + BITFIELD(TIMER_OVERRIDE), + BITFIELD(TIMER_CLIENT_TIME), + BITFIELD(TIMER_STOPPABLE), + BITFIELD(TIMER_NO_HASH_WAIT), + BITFIELD(TIMER_LOOP), + BITFIELD(TIMER_DELETE_ME), +)) + +///Empty ID define +#define TIMER_ID_NULL -1 diff --git a/code/__HELPERS/_logging.dm b/code/__HELPERS/_logging.dm index d079ff8d1e1..677f77de336 100644 --- a/code/__HELPERS/_logging.dm +++ b/code/__HELPERS/_logging.dm @@ -194,6 +194,12 @@ GLOBAL_LIST_INIT(testing_global_profiler, list("_PROFILE_NAME" = "Global")) /proc/log_href(text) WRITE_LOG(GLOB.world_href_log, "HREF: [text]") +/proc/log_sql(text) + WRITE_LOG(GLOB.sql_error_log, "SQL: [text]") + +/proc/log_query_debug(text) + // does nothing right now, sorry + /proc/log_qdel(text) WRITE_LOG(GLOB.world_qdel_log, "QDEL: [text]") diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm index 3ee28dee2f0..498da5df55f 100644 --- a/code/__HELPERS/text.dm +++ b/code/__HELPERS/text.dm @@ -8,15 +8,15 @@ * Misc */ - /* * SQL sanitization */ -// Run all strings to be used in an SQL query through this proc first to properly escape out injection attempts. -/proc/sanitizeSQL(var/t as text) - var/sqltext = dbcon.Quote(t); - return copytext(sqltext, 2, length(sqltext));//Quote() adds quotes around input, we already do that +/proc/format_table_name(table) + return CONFIG_GET(string/sql_server_prefix) + table + +/proc/format_unified_table_name(table) + return CONFIG_GET(string/sql_unified_prefix) + table /* * Text sanitization diff --git a/code/_globalvars/logging.dm b/code/_globalvars/logging.dm index cb2e1a80826..6988c15e6f1 100644 --- a/code/_globalvars/logging.dm +++ b/code/_globalvars/logging.dm @@ -24,6 +24,9 @@ GLOBAL_PROTECT(round_id) /// Config loading error/config validation errors GLOBAL_VAR(config_error_log) GLOBAL_PROTECT(config_error_log) +/// logs sql events +GLOBAL_VAR(sql_error_log) +GLOBAL_PROTECT(sql_error_log) /// Map error logging GLOBAL_VAR(world_map_error_log) GLOBAL_PROTECT(world_map_error_log) @@ -46,3 +49,4 @@ GLOBAL_PROTECT(picture_logging_prefix) /// Intended to hold all logins that failed due to suspicious circumstances such as ban detection, CID randomisation etc. GLOBAL_VAR(world_suspicious_login_log) GLOBAL_PROTECT(world_suspicious_login_log) + diff --git a/code/controllers/configuration/entries/dbconfig.dm b/code/controllers/configuration/entries/dbconfig.dm index f4f90a8bef1..3fbc32d38fe 100644 --- a/code/controllers/configuration/entries/dbconfig.dm +++ b/code/controllers/configuration/entries/dbconfig.dm @@ -1,2 +1,48 @@ /datum/config_entry/flag/sql_enabled // for sql switching protection = CONFIG_ENTRY_LOCKED + +/datum/config_entry/string/sql_server_prefix + protection = CONFIG_ENTRY_LOCKED + +/datum/config_entry/string/sql_unified_prefix + protection = CONFIG_ENTRY_LOCKED + +/datum/config_entry/string/sql_address + protection = CONFIG_ENTRY_HIDDEN | CONFIG_ENTRY_LOCKED + config_entry_value = "localhost" + +/datum/config_entry/number/sql_port + protection = CONFIG_ENTRY_HIDDEN | CONFIG_ENTRY_LOCKED + config_entry_value = 3306 + +/datum/config_entry/string/sql_user + protection = CONFIG_ENTRY_HIDDEN | CONFIG_ENTRY_LOCKED + +/datum/config_entry/string/sql_password + protection = CONFIG_ENTRY_HIDDEN | CONFIG_ENTRY_LOCKED + +/datum/config_entry/string/sql_database + protection = CONFIG_ENTRY_HIDDEN | CONFIG_ENTRY_LOCKED + +/datum/config_entry/number/query_debug_log_timeout + config_entry_value = 70 + min_val = 1 + protection = CONFIG_ENTRY_LOCKED + deprecated_by = /datum/config_entry/number/blocking_query_timeout + +/datum/config_entry/number/query_debug_log_timeout/DeprecationUpdate(value) + return value + +/datum/config_entry/number/async_query_timeout + config_entry_value = 10 + min_val = 0 + protection = CONFIG_ENTRY_LOCKED + +/datum/config_entry/number/blocking_query_timeout + config_entry_value = 5 + min_val = 0 + protection = CONFIG_ENTRY_LOCKED + +/datum/config_entry/number/bsql_thread_limit + config_entry_value = 50 + min_val = 1 diff --git a/code/controllers/subsystem/dbcore/_dbcore.dm b/code/controllers/subsystem/dbcore/_dbcore.dm new file mode 100644 index 00000000000..3abfc87bbe0 --- /dev/null +++ b/code/controllers/subsystem/dbcore/_dbcore.dm @@ -0,0 +1,335 @@ +SUBSYSTEM_DEF(dbcore) + name = "Database" + subsystem_flags = SS_BACKGROUND + wait = 1 MINUTES + init_order = INIT_ORDER_DBCORE + var/failed_connection_timeout = 0 + + var/schema_mismatch = 0 + var/db_minor = 0 + var/db_major = 0 + var/failed_connections = 0 + + var/last_error + var/list/active_queries = list() + + var/connection // Arbitrary handle returned from rust_g. + +/datum/controller/subsystem/dbcore/Initialize() + //We send warnings to the admins during subsystem init, as the clients will be New'd and messages + //will queue properly with goonchat + switch(schema_mismatch) + if(1) + message_admins("Database schema ([db_major].[db_minor]) doesn't match the latest schema version ([DB_MAJOR_VERSION].[DB_MINOR_VERSION]), this may lead to undefined behaviour or errors") + if(2) + message_admins("Could not get schema version from database") + + return ..() + +/datum/controller/subsystem/dbcore/fire() + for(var/I in active_queries) + var/datum/db_query/Q = I + if(world.time - Q.last_activity_time > (5 MINUTES)) + message_admins("Found undeleted query, please check the server logs and notify coders.") + log_sql("Undeleted query: \"[Q.sql]\" LA: [Q.last_activity] LAT: [Q.last_activity_time]") + qdel(Q) + if(MC_TICK_CHECK) + return + +/datum/controller/subsystem/dbcore/Recover() + connection = SSdbcore.connection + +/datum/controller/subsystem/dbcore/Shutdown() + //This is as close as we can get to the true round end before Disconnect() without changing where it's called, defeating the reason this is a subsystem + if(SSdbcore.Connect()) + var/datum/db_query/query_round_shutdown = SSdbcore.NewQuery( + "UPDATE [format_table_name("round")] SET shutdown_datetime = Now(), end_state = :end_state WHERE id = :round_id", + list("end_state" = "Unknown", "round_id" = GLOB.round_id) + ) + query_round_shutdown.Execute() + qdel(query_round_shutdown) + if(IsConnected()) + Disconnect() + +//nu +/datum/controller/subsystem/dbcore/can_vv_get(var_name) + return var_name != NAMEOF(src, connection) && var_name != NAMEOF(src, active_queries) && ..() + +/datum/controller/subsystem/dbcore/vv_edit_var(var_name, var_value) + if(var_name == NAMEOF(src, connection)) + return FALSE + return ..() + +/datum/controller/subsystem/dbcore/proc/Connect() + if(IsConnected()) + return TRUE + + if(failed_connection_timeout <= world.time) //it's been more than 5 seconds since we failed to connect, reset the counter + failed_connections = 0 + + if(failed_connections > 5) //If it failed to establish a connection more than 5 times in a row, don't bother attempting to connect for 5 seconds. + failed_connection_timeout = world.time + 50 + return FALSE + + if(!CONFIG_GET(flag/sql_enabled)) + return FALSE + + var/user = CONFIG_GET(string/sql_user) + var/pass = CONFIG_GET(string/sql_password) + var/db = CONFIG_GET(string/sql_database) + var/address = CONFIG_GET(string/sql_address) + var/port = CONFIG_GET(number/sql_port) + var/timeout = max(CONFIG_GET(number/async_query_timeout), CONFIG_GET(number/blocking_query_timeout)) + var/thread_limit = CONFIG_GET(number/bsql_thread_limit) + + var/result = json_decode(rustg_sql_connect_pool(json_encode(list( + "host" = address, + "port" = port, + "user" = user, + "pass" = pass, + "db_name" = db, + "read_timeout" = timeout, + "write_timeout" = timeout, + "max_threads" = thread_limit, + )))) + . = (result["status"] == "ok") + if (.) + connection = result["handle"] + else + connection = null + last_error = result["data"] + log_sql("Connect() failed | [last_error]") + ++failed_connections + +/datum/controller/subsystem/dbcore/proc/CheckSchemaVersion() + if(CONFIG_GET(flag/sql_enabled)) + if(Connect()) + log_world("Database connection established.") + var/datum/db_query/query_db_version = NewQuery("SELECT major, minor FROM [format_table_name("schema_revision")] ORDER BY date DESC LIMIT 1") + query_db_version.Execute() + if(query_db_version.NextRow()) + db_major = text2num(query_db_version.item[1]) + db_minor = text2num(query_db_version.item[2]) + if(db_major != DB_MAJOR_VERSION || db_minor != DB_MINOR_VERSION) + schema_mismatch = 1 // flag admin message about mismatch + log_sql("Database schema ([db_major].[db_minor]) doesn't match the latest schema version ([DB_MAJOR_VERSION].[DB_MINOR_VERSION]), this may lead to undefined behaviour or errors") + else + schema_mismatch = 2 //flag admin message about no schema version + log_sql("Could not get schema version from database") + qdel(query_db_version) + else + log_sql("Your server failed to establish a connection with the database.") + else + log_sql("Database is not enabled in configuration.") + +/datum/controller/subsystem/dbcore/proc/SetRoundID() + if(!Connect()) + return + var/datum/db_query/query_round_initialize = SSdbcore.NewQuery( + "INSERT INTO [format_table_name("round")] (initialize_datetime, server_ip, server_port) VALUES (Now(), INET_ATON(:internet_address), :port)", + list("internet_address" = world.internet_address || "0", "port" = "[world.port]") + ) + query_round_initialize.Execute(async = FALSE) + GLOB.round_id = "[query_round_initialize.last_insert_id]" + qdel(query_round_initialize) + +/datum/controller/subsystem/dbcore/proc/SetRoundStart() + if(!Connect()) + return + var/datum/db_query/query_round_start = SSdbcore.NewQuery( + "UPDATE [format_table_name("round")] SET start_datetime = Now() WHERE id = :round_id", + list("round_id" = GLOB.round_id) + ) + query_round_start.Execute() + qdel(query_round_start) + +/datum/controller/subsystem/dbcore/proc/SetRoundEnd() + if(!Connect()) + return + var/datum/db_query/query_round_end = SSdbcore.NewQuery( + "UPDATE [format_table_name("round")] SET end_datetime = Now() WHERE id = :round_id", + list("round_id" = GLOB.round_id) + ) + query_round_end.Execute() + qdel(query_round_end) + +/datum/controller/subsystem/dbcore/proc/Disconnect() + failed_connections = 0 + if (connection) + rustg_sql_disconnect_pool(connection) + connection = null + +/datum/controller/subsystem/dbcore/proc/IsConnected() + if (!CONFIG_GET(flag/sql_enabled)) + return FALSE + if (!connection) + return FALSE + return json_decode(rustg_sql_connected(connection))["status"] == "online" + +/datum/controller/subsystem/dbcore/proc/ErrorMsg() + if(!CONFIG_GET(flag/sql_enabled)) + return "Database disabled by configuration" + return last_error + +/datum/controller/subsystem/dbcore/proc/ReportError(error) + last_error = error + +/** + * makes a query + * + * @params + * - sql_query - the query. use :arg for arguments + * - arguments - keyed list + */ +/datum/controller/subsystem/dbcore/proc/NewQuery(sql_query, arguments) + RETURN_TYPE(/datum/db_query) + if(IsAdminAdvancedProcCall()) + log_admin_private("ERROR: Advanced admin proc call led to sql query: [sql_query]. Query has been blocked") + message_admins("ERROR: Advanced admin proc call led to sql query. Query has been blocked") + return FALSE + return new /datum/db_query(connection, sql_query, arguments) + +/** + * makes, and runs a query + * + * @params + * - sql_query - the query. use :arg for arguments + * - arguments - keyed list + */ +/datum/controller/subsystem/dbcore/proc/ExecuteQuery(sql_query, arguments) + RETURN_TYPE(/datum/db_query) + var/datum/db_query/query = NewQuery(sql_query, arguments) + . = query + query.Execute() + +/** + * immediately runs a sql query with selected arguments + * always uses async queries + * will block the caller. + * + * ! do not use this proc for new things, this proc is bad practice. + * + * **warning**: will delete the query right after the current set of procs run. USE NewQuery IF YOU WANT TO MANAGE THIS YOURSELF. + */ +/datum/controller/subsystem/dbcore/proc/RunQuery(sql_query, arguments) + RETURN_TYPE(/datum/db_query) + var/datum/db_query/query = NewQuery(sql_query, arguments) + . = query + query.Execute(TRUE, TRUE) + QDEL_IN(query, 0) + +/datum/controller/subsystem/dbcore/proc/QuerySelect(list/querys, warn = FALSE, qdel = FALSE) + if (!islist(querys)) + if (!istype(querys, /datum/db_query)) + CRASH("Invalid query passed to QuerySelect: [querys]") + querys = list(querys) + + for (var/thing in querys) + var/datum/db_query/query = thing + if (warn) + INVOKE_ASYNC(query, /datum/db_query.proc/warn_execute) + else + INVOKE_ASYNC(query, /datum/db_query.proc/Execute) + + for (var/thing in querys) + var/datum/db_query/query = thing + UNTIL(!query.in_progress) + if (qdel) + qdel(query) + + + +/* +Takes a list of rows (each row being an associated list of column => value) and inserts them via a single mass query. +Rows missing columns present in other rows will resolve to SQL NULL +You are expected to do your own escaping of the data, and expected to provide your own quotes for strings. +The duplicate_key arg can be true to automatically generate this part of the query + or set to a string that is appended to the end of the query +Ignore_errors instructes mysql to continue inserting rows if some of them have errors. + the erroneous row(s) aren't inserted and there isn't really any way to know why or why errored +Delayed insert mode was removed in mysql 7 and only works with MyISAM type tables, + It was included because it is still supported in mariadb. + It does not work with duplicate_key and the mysql server ignores it in those cases +*/ +/datum/controller/subsystem/dbcore/proc/MassInsert(table, list/rows, duplicate_key = FALSE, ignore_errors = FALSE, delayed = FALSE, warn = FALSE, async = TRUE, special_columns = null) + if (!table || !rows || !istype(rows)) + return + + // Prepare column list + var/list/columns = list() + var/list/has_question_mark = list() + for (var/list/row in rows) + for (var/column in row) + columns[column] = "?" + has_question_mark[column] = TRUE + for (var/column in special_columns) + columns[column] = special_columns[column] + has_question_mark[column] = findtext(special_columns[column], "?") + + // Prepare SQL query full of placeholders + var/list/query_parts = list("INSERT") + if (delayed) + query_parts += " DELAYED" + if (ignore_errors) + query_parts += " IGNORE" + query_parts += " INTO " + query_parts += table + query_parts += "\n([columns.Join(", ")])\nVALUES" + + var/list/arguments = list() + var/has_row = FALSE + for (var/list/row in rows) + if (has_row) + query_parts += "," + query_parts += "\n (" + var/has_col = FALSE + for (var/column in columns) + if (has_col) + query_parts += ", " + if (has_question_mark[column]) + var/name = "p[arguments.len]" + query_parts += replacetext(columns[column], "?", ":[name]") + arguments[name] = row[column] + else + query_parts += columns[column] + has_col = TRUE + query_parts += ")" + has_row = TRUE + + if (duplicate_key == TRUE) + var/list/column_list = list() + for (var/column in columns) + column_list += "[column] = VALUES([column])" + query_parts += "\nON DUPLICATE KEY UPDATE [column_list.Join(", ")]" + else if (duplicate_key != FALSE) + query_parts += duplicate_key + + var/datum/db_query/Query = NewQuery(query_parts.Join(), arguments) + if (warn) + . = Query.warn_execute(async) + else + . = Query.Execute(async) + qdel(Query) + +/** + * WARNING: This proc currently does nothing. + * why??? Because rust_g already sanitizes strings. + * This proc simply flattens a string. + */ +/proc/sanitizeSQL(t) + return "[t]" + +/** + * LISTEN UP MOTHERFUCKERS + * This is NOT LIKE THAT PROC ABOVE THAT. + * + * DO NOT REPLACE THIS BLINDLY, LEST YOU OPEN US UP TO SQL INJECTION ATTACKS + * We'll phase both of these out eventually(tm) + * DON'T BLINDLY TOUCH IT. + */ +// Sanitize inputs to avoid SQL injection attacks +/proc/sql_sanitize_text(var/text) + text = replacetext(text, "'", "''") + text = replacetext(text, ";", "") + text = replacetext(text, "&", "") + return text diff --git a/code/controllers/subsystem/dbcore/query.dm b/code/controllers/subsystem/dbcore/query.dm new file mode 100644 index 00000000000..4913b5138b7 --- /dev/null +++ b/code/controllers/subsystem/dbcore/query.dm @@ -0,0 +1,130 @@ +/datum/db_query + // Inputs + var/connection + var/sql + var/arguments + + // Status information + var/in_progress + var/last_error + var/last_activity + var/last_activity_time + + // Output + var/list/list/rows + var/next_row_to_take = 1 + var/affected + var/last_insert_id + + var/list/item //list of data values populated by NextRow() + +/datum/db_query/New(connection, sql, arguments) + SSdbcore.active_queries[src] = TRUE + Activity("Created") + item = list() + + src.connection = connection + src.sql = sql + src.arguments = arguments + +/datum/db_query/Destroy() + Close() + SSdbcore.active_queries -= src + return ..() + +/datum/db_query/proc/Activity(activity) + last_activity = activity + last_activity_time = world.time + +/datum/db_query/proc/warn_execute(async = TRUE) + . = Execute(async) + if(!.) + to_chat(usr, "A SQL error occurred during this operation, check the server logs.") + +/datum/db_query/proc/Execute(async = TRUE, log_error = TRUE) + Activity("Execute") + if(in_progress) + CRASH("Attempted to start a new query while waiting on the old one") + + if(!SSdbcore.IsConnected()) + last_error = "No connection!" + return FALSE + + var/start_time + if(!async) + start_time = REALTIMEOFDAY + Close() + . = run_query(async) + var/timed_out = !. && findtext(last_error, "Operation timed out") + if(!. && log_error) + log_sql("[last_error] | Query used: [sql] | Arguments: [json_encode(arguments)]") + if(!async && timed_out) + log_query_debug("Query execution started at [start_time]") + log_query_debug("Query execution ended at [REALTIMEOFDAY]") + log_query_debug("Slow query timeout detected.") + log_query_debug("Query used: [sql]") + slow_query_check() + +/datum/db_query/proc/run_query(async) + var/job_result_str + + if (async) + var/job_id = rustg_sql_query_async(connection, sql, json_encode(arguments)) + in_progress = TRUE + UNTIL((job_result_str = rustg_sql_check_query(job_id)) != RUSTG_JOB_NO_RESULTS_YET) + in_progress = FALSE + + if (job_result_str == RUSTG_JOB_ERROR) + last_error = job_result_str + return FALSE + else + job_result_str = rustg_sql_query_blocking(connection, sql, json_encode(arguments)) + + var/result = json_decode(job_result_str) + switch (result["status"]) + if ("ok") + rows = result["rows"] + affected = result["affected"] + last_insert_id = result["last_insert_id"] + return TRUE + if ("err") + last_error = result["data"] + return FALSE + if ("offline") + last_error = "offline" + return FALSE + +/datum/db_query/proc/slow_query_check() + message_admins("HEY! A database query timed out. Did the server just hang? \[YES\]|\[NO\]") + +/datum/db_query/proc/NextRow(async = TRUE) + Activity("NextRow") + + if (rows && next_row_to_take <= rows.len) + item = rows[next_row_to_take] + next_row_to_take++ + return !!item + else + return FALSE + +/datum/db_query/proc/ErrorMsg() + return last_error + +/datum/db_query/proc/Close() + rows = null + item = null + +//! protect +/datum/db_query/can_vv_get(var_name) + switch(var_name) + if(NAMEOF(src, connection)) + return FALSE + return ..() + +/datum/db_query/vv_edit_var(var_name, var_value) + // nah + return FALSE + +/datum/db_query/CanProcCall(proc_name) + //fuck off kevinz + return FALSE diff --git a/code/controllers/subsystem/persist_vr.dm b/code/controllers/subsystem/persist_vr.dm index e706846bb2c..b3cde7aaa1c 100644 --- a/code/controllers/subsystem/persist_vr.dm +++ b/code/controllers/subsystem/persist_vr.dm @@ -19,8 +19,7 @@ SUBSYSTEM_DEF(persist) if(!config_legacy.time_off) return - establish_db_connection() - if(!dbcon.IsConnected()) + if(!SSdbcore.Connect()) src.currentrun.Cut() return if(!resumed) @@ -75,8 +74,14 @@ SUBSYSTEM_DEF(persist) var/sql_ckey = sql_sanitize_text(C.ckey) var/sql_dpt = sql_sanitize_text(department_earning) var/sql_bal = text2num("[C.department_hours[department_earning]]") - var/DBQuery/query = dbcon.NewQuery("INSERT INTO vr_player_hours (ckey, department, hours) VALUES ('[sql_ckey]', '[sql_dpt]', [sql_bal]) ON DUPLICATE KEY UPDATE hours = VALUES(hours)") - query.Execute() + SSdbcore.RunQuery( + "INSERT INTO [format_table_name("vr_player_hours")] (ckey, department, hours) VALUES (:ckey, :dept, :hours) ON DUPLICATE KEY UPDATE hours = VALUES(hours)", + list( + "ckey" = sql_ckey, + "dept" = sql_dpt, + "hours" = sql_bal + ) + ) if (MC_TICK_CHECK) return diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index a2e6f9a141c..d529fed35f8 100644 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -166,6 +166,9 @@ SUBSYSTEM_DEF(ticker) timeLeft = newtime /datum/controller/subsystem/ticker/proc/setup() + to_chat(world, "Starting game...") + var/init_start = world.timeofday + //Create and announce mode if(master_mode=="secret") src.hide_mode = 1 @@ -236,7 +239,9 @@ SUBSYSTEM_DEF(ticker) // type filtered, we cannot risk runtimes L.OnRoundstart() + log_world("Game start took [(world.timeofday - init_start)/10]s") round_start_time = world.time + SSdbcore.SetRoundStart() // TODO Dear God Fix This. Fix all of this. Not just this line, this entire proc. This entire file! spawn(0)//Forking here so we dont have to wait for this to finish @@ -461,6 +466,7 @@ SUBSYSTEM_DEF(ticker) broadcastmessage += "\n\n<@&[CONFIG_GET(string/chat_reboot_role)]>, the server will reboot shortly!" send2chat(broadcastmessage, CONFIG_GET(string/chat_roundend_notice_tag)) + SSdbcore.SetRoundEnd() SSpersistence.SavePersistence() ready_for_reboot = TRUE standard_reboot() diff --git a/code/defines/procs/dbcore.dm b/code/defines/procs/dbcore.dm deleted file mode 100644 index a6f75ce5679..00000000000 --- a/code/defines/procs/dbcore.dm +++ /dev/null @@ -1,240 +0,0 @@ - -//cursors -#define Default_Cursor 0 -#define Client_Cursor 1 -#define Server_Cursor 2 -//conversions -#define TEXT_CONV 1 -#define RSC_FILE_CONV 2 -#define NUMBER_CONV 3 -//column flag values: -#define IS_NUMERIC 1 -#define IS_BINARY 2 -#define IS_NOT_NULL 4 -#define IS_PRIMARY_KEY 8 -#define IS_UNSIGNED 16 -//types -#define TINYINT 1 -#define SMALLINT 2 -#define MEDIUMINT 3 -#define INTEGER 4 -#define BIGINT 5 -#define DECIMAL 6 -#define FLOAT 7 -#define DOUBLE 8 -#define DATE 9 -#define DATETIME 10 -#define TIMESTAMP 11 -#define TIME 12 -#define STRING 13 -#define BLOB 14 -// TODO: Investigate more recent type additions and see if I can handle them. - Nadrew - - -// Deprecated! See global.dm for new configuration vars -/* -var/DB_SERVER = "" // This is the location of your MySQL server (localhost is USUALLY fine) -var/DB_PORT = 3306 // This is the port your MySQL server is running on (3306 is the default) -*/ - -/DBConnection - var/_db_con // This variable contains a reference to the actual database connection. - var/dbi // This variable is a string containing the DBI MySQL requires. - var/user // This variable contains the username data. - var/password // This variable contains the password data. - var/default_cursor // This contains the default database cursor data. - // - var/server = "" - var/port = 3306 - -/DBConnection/New(dbi_handler,username,password_handler,cursor_handler) - src.dbi = dbi_handler - src.user = username - src.password = password_handler - src.default_cursor = cursor_handler - _db_con = _dm_db_new_con() - -/DBConnection/proc/Connect(dbi_handler=src.dbi,user_handler=src.user,password_handler=src.password,cursor_handler) - if(!sqllogging) - return 0 - if(!src) - return 0 - cursor_handler = src.default_cursor - if(!cursor_handler) cursor_handler = Default_Cursor - return _dm_db_connect(_db_con,dbi_handler,user_handler,password_handler,cursor_handler,null) - -/DBConnection/proc/Disconnect() - return _dm_db_close(_db_con) - -/DBConnection/proc/IsConnected() - if(!sqllogging) - return 0 - var/success = _dm_db_is_connected(_db_con) - return success - -/DBConnection/proc/Quote(str) - return _dm_db_quote(_db_con,str) - -/DBConnection/proc/ErrorMsg() - return _dm_db_error_msg(_db_con) - -/DBConnection/proc/SelectDB(database_name,dbi) - if(IsConnected()) Disconnect() - //return Connect("[dbi?"[dbi]":"dbi:mysql:[database_name]:[DB_SERVER]:[DB_PORT]"]",user,password) - return Connect("[dbi?"[dbi]":"dbi:mysql:[database_name]:[sqladdress]:[sqlport]"]",user,password) -/DBConnection/proc/NewQuery(sql_query,cursor_handler=src.default_cursor) - return new/DBQuery(sql_query,src,cursor_handler) - - -/DBQuery/New(sql_query,DBConnection/connection_handler,cursor_handler) - if(sql_query) - src.sql = sql_query - if(connection_handler) - src.db_connection = connection_handler - if(cursor_handler) - src.default_cursor = cursor_handler - _db_query = _dm_db_new_query() - return ..() - - -/DBQuery - /// The sql query being executed. - var/sql - var/default_cursor - /// List of DB Columns populated by Columns() - var/list/columns - var/list/conversions - /// List of data values populated by NextRow() - var/list/item[0] - - var/DBConnection/db_connection - var/_db_query - -/DBQuery/proc/Connect(DBConnection/connection_handler) src.db_connection = connection_handler - -/DBQuery/proc/Execute(sql_query=src.sql,cursor_handler=default_cursor) - Close() - return _dm_db_execute(_db_query,sql_query,db_connection._db_con,cursor_handler,null) - -/DBQuery/proc/NextRow() - return _dm_db_next_row(_db_query,item,conversions) - -/DBQuery/proc/RowsAffected() - return _dm_db_rows_affected(_db_query) - -/DBQuery/proc/RowCount() - return _dm_db_row_count(_db_query) - -/DBQuery/proc/ErrorMsg() - return _dm_db_error_msg(_db_query) - -/DBQuery/proc/Columns() - if(!columns) - columns = _dm_db_columns(_db_query,/DBColumn) - return columns - -/DBQuery/proc/GetRowData() - var/list/columns = Columns() - var/list/results - if(columns.len) - results = list() - for(var/C in columns) - results+=C - var/DBColumn/cur_col = columns[C] - results[C] = src.item[(cur_col.position+1)] - return results - -/DBQuery/proc/Close() - item.len = 0 - columns = null - conversions = null - return _dm_db_close(_db_query) - -/DBQuery/proc/Quote(str) - return db_connection.Quote(str) - -/DBQuery/proc/SetConversion(column,conversion) - if(istext(column)) - column = columns.Find(column) - if(!conversions) - conversions = new/list(column) - else if(conversions.len < column) - conversions.len = column - conversions[column] = conversion - - -/DBColumn - var/name - var/table - var/position //1-based index into item data - var/sql_type - var/flags - var/length - var/max_length - -/DBColumn/New(name_handler,table_handler,position_handler,type_handler,flag_handler,length_handler,max_length_handler) - src.name = name_handler - src.table = table_handler - src.position = position_handler - src.sql_type = type_handler - src.flags = flag_handler - src.length = length_handler - src.max_length = max_length_handler - return ..() - - -/DBColumn/proc/SqlTypeName(type_handler=src.sql_type) - switch(type_handler) - if(TINYINT) - return "TINYINT" - if(SMALLINT) - return "SMALLINT" - if(MEDIUMINT) - return "MEDIUMINT" - if(INTEGER) - return "INTEGER" - if(BIGINT) - return "BIGINT" - if(FLOAT) - return "FLOAT" - if(DOUBLE) - return "DOUBLE" - if(DATE) - return "DATE" - if(DATETIME) - return "DATETIME" - if(TIMESTAMP) - return "TIMESTAMP" - if(TIME) - return "TIME" - if(STRING) - return "STRING" - if(BLOB) - return "BLOB" - - -#undef Default_Cursor -#undef Client_Cursor -#undef Server_Cursor -#undef TEXT_CONV -#undef RSC_FILE_CONV -#undef NUMBER_CONV -#undef IS_NUMERIC -#undef IS_BINARY -#undef IS_NOT_NULL -#undef IS_PRIMARY_KEY -#undef IS_UNSIGNED -#undef TINYINT -#undef SMALLINT -#undef MEDIUMINT -#undef INTEGER -#undef BIGINT -#undef DECIMAL -#undef FLOAT -#undef DOUBLE -#undef DATE -#undef DATETIME -#undef TIMESTAMP -#undef TIME -#undef STRING -#undef BLOB diff --git a/code/defines/procs/statistics.dm b/code/defines/procs/statistics.dm index 8a75164d7a1..5509efb2444 100644 --- a/code/defines/procs/statistics.dm +++ b/code/defines/procs/statistics.dm @@ -6,15 +6,21 @@ proc/sql_poll_population() for(var/mob/M in player_list) if(M.client) playercount += 1 - establish_db_connection() - if(!dbcon.IsConnected()) + + if(!SSdbcore.Connect()) log_game("SQL ERROR during population polling. Failed to connect.") else - var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") - var/DBQuery/query = dbcon_old.NewQuery("INSERT INTO `tgstation`.`population` (`playercount`, `admincount`, `time`) VALUES ([playercount], [admincount], '[sqltime]')") + var/datum/db_query/query = SSdbcore.NewQuery( + "INSERT INTO [format_table_name("population")] (playercount, admincount, time) VALUES (:pc, :ac, NOW())", + list( + "pc" = sanitizeSQL(playercount), + "ac" = sanitizeSQL(admincount) + ) + ) if(!query.Execute()) var/err = query.ErrorMsg() log_game("SQL ERROR during population polling. Error : \[[err]\]\n") + qdel(query) proc/sql_report_round_start() // TODO @@ -41,23 +47,43 @@ proc/sql_report_death(var/mob/living/carbon/human/H) var/sqlpod = sanitizeSQL(podname) var/sqlspecial = sanitizeSQL(H.mind.special_role) var/sqljob = sanitizeSQL(H.mind.assigned_role) + var/laname var/lakey if(H.lastattacker) laname = sanitizeSQL(H.lastattacker:real_name) lakey = sanitizeSQL(H.lastattacker:key) + var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") var/coord = "[H.x], [H.y], [H.z]" - //to_chat(world, "INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.bruteloss], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()])") - establish_db_connection() - if(!dbcon.IsConnected()) + + if(!SSdbcore.Connect()) log_game("SQL ERROR during death reporting. Failed to connect.") else - var/DBQuery/query = dbcon.NewQuery("INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, coord) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.getBruteLoss()], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()], '[coord]')") + var/datum/db_query/query = SSdbcore.NewQuery( + "INSERT INTO [format_table_name("death")] (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, coord) VALUES \ + (:name, :key, :job, :special, :pod, :time, :laname, :lakey, :geender, :bruteloss, :fireloss, :brainloss, :oxyloss, :coord)", + list( + "name" = sqlname, + "key" = sqlkey, + "job" = sqljob, + "special" = sqlspecial, + "pod" = sqlpod, + "time" = sqltime, + "laname" = laname, + "lakey" = lakey,, + "gender" = H.gender, + "bruteloss" = H.getBruteLoss(), + "fireloss" = H.getFireLoss(), + "brainloss" = H.getBrainLoss(), + "oxyloss" = H.getOxyLoss(), + "coord" = coord + ) + ) if(!query.Execute()) var/err = query.ErrorMsg() log_game("SQL ERROR during death reporting. Error : \[[err]\]\n") - + qdel(query) proc/sql_report_cyborg_death(var/mob/living/silicon/robot/H) if(!sqllogging) @@ -82,15 +108,34 @@ proc/sql_report_cyborg_death(var/mob/living/silicon/robot/H) lakey = sanitizeSQL(H.lastattacker:key) var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") var/coord = "[H.x], [H.y], [H.z]" - //to_chat(world, "INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.bruteloss], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()])") - establish_db_connection() - if(!dbcon.IsConnected()) + + if(!SSdbcore.Connect()) log_game("SQL ERROR during death reporting. Failed to connect.") else - var/DBQuery/query = dbcon.NewQuery("INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, coord) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.getBruteLoss()], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()], '[coord]')") + var/datum/db_query/query = SSdbcore.NewQuery( + "INSERT INTO [format_table_name("death")] (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, coord) VALUES \ + (:name, :key, :job, :special, :pod, :time, :laname, :lakey, :geender, :bruteloss, :fireloss, :brainloss, :oxyloss, :coord)", + list( + "name" = sqlname, + "key" = sqlkey, + "job" = sqljob, + "special" = sqlspecial, + "pod" = sqlpod, + "time" = sqltime, + "laname" = laname, + "lakey" = lakey,, + "gender" = H.gender, + "bruteloss" = H.getBruteLoss(), + "fireloss" = H.getFireLoss(), + "brainloss" = H.getBrainLoss(), + "oxyloss" = H.getOxyLoss(), + "coord" = coord + ) + ) if(!query.Execute()) var/err = query.ErrorMsg() log_game("SQL ERROR during death reporting. Error : \[[err]\]\n") + qdel(query) proc/statistic_cycle() @@ -113,13 +158,14 @@ proc/sql_commit_feedback() log_game("Round ended without any feedback being generated. No feedback was sent to the database.") return - establish_db_connection() - if(!dbcon.IsConnected()) + if(!SSdbcore.Connect()) log_game("SQL ERROR during feedback reporting. Failed to connect.") else - var/DBQuery/max_query = dbcon.NewQuery("SELECT MAX(roundid) AS max_round_id FROM erro_feedback") - max_query.Execute() + var/datum/db_query/max_query = SSdbcore.RunQuery( + "SELECT MAX(roundid) AS max_round_id FROM [format_table_name("feedback")]", + list() + ) var/newroundid @@ -138,7 +184,15 @@ proc/sql_commit_feedback() var/variable = item.get_variable() var/value = item.get_value() - var/DBQuery/query = dbcon.NewQuery("INSERT INTO erro_feedback (id, roundid, time, variable, value) VALUES (null, [newroundid], Now(), '[variable]', '[value]')") + var/datum/db_query/query = SSdbcore.NewQuery( + "INSERT INTO [format_table_name("feedback")] (id, roundid, time, variable, value) VALUES (null, :rid, Now(), :var, :val)", + list( + "rid" = newroundid, + "var" = sanitizeSQL(variable), + "val" = sanitizeSQL(value) + ) + ) if(!query.Execute()) var/err = query.ErrorMsg() log_game("SQL ERROR during death reporting. Error : \[[err]\]\n") + qdel(query) diff --git a/code/game/machinery/records_scanner.dm b/code/game/machinery/records_scanner.dm index a331dadae3f..345f1236e28 100644 --- a/code/game/machinery/records_scanner.dm +++ b/code/game/machinery/records_scanner.dm @@ -52,7 +52,7 @@ obj/machinery/scanner/attack_hand(mob/living/carbon/human/user) var/age = user.age var/gender = user.gender /* no dbstuff yet - var/DBQuery/cquery = dbcon.NewQuery("SELECT * from jobban WHERE ckey='[user.ckey]'") + var/datum/db_query/cquery = dbcon.NewQuery("SELECT * from jobban WHERE ckey='[user.ckey]'") if(!cquery.Execute()) return else while(cquery.NextRow()) diff --git a/code/game/magic/archived_book.dm b/code/game/magic/archived_book.dm index 3be1d44cd7c..a4548738fea 100644 --- a/code/game/magic/archived_book.dm +++ b/code/game/magic/archived_book.dm @@ -47,10 +47,10 @@ datum/book_manager/proc/freeid() if(BOOKS_USE_SQL && CONFIG_GET(flag/sql_enabled)) var/DBConnection/dbcon = new() dbcon.Connect("dbi:mysql:[sqldb]:[sqladdress]:[sqlport]","[sqllogin]","[sqlpass]") - if(!dbcon.IsConnected()) + if(!SSdbcore.Connect()) alert("Connection to Archive has been severed. Aborting.") else - var/DBQuery/query = dbcon.NewQuery("DELETE FROM library WHERE id=[isbn]") + var/datum/db_query/query = dbcon.NewQuery("DELETE FROM library WHERE id=[isbn]") if(!query.Execute()) usr << query.ErrorMsg() dbcon.Disconnect() diff --git a/code/game/world.dm b/code/game/world.dm index ffa5b9f9438..bdc374b70d2 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -39,6 +39,10 @@ GLOBAL_LIST(topic_status_cache) config.Load(params[OVERRIDE_CONFIG_DIRECTORY_PARAMETER]) + //SetupLogs depends on the RoundID, so lets check + //DB schema and set RoundID if we can + SSdbcore.CheckSchemaVersion() + SSdbcore.SetRoundID() SetupLogs() // #ifndef USE_CUSTOM_ERROR_HANDLER @@ -145,6 +149,7 @@ GLOBAL_LIST(topic_status_cache) GLOB.world_attack_log = "[GLOB.log_directory]/attack.log" GLOB.world_href_log = "[GLOB.log_directory]/hrefs.log" GLOB.world_qdel_log = "[GLOB.log_directory]/qdel.log" + GLOB.sql_error_log = "[GLOB.log_directory]/sql.log" GLOB.world_map_error_log = "[GLOB.log_directory]/map_errors.log" GLOB.world_runtime_log = "[GLOB.log_directory]/runtime.log" GLOB.tgui_log = "[GLOB.log_directory]/tgui.log" @@ -158,6 +163,7 @@ GLOBAL_LIST(topic_status_cache) start_log(GLOB.world_game_log) start_log(GLOB.world_attack_log) start_log(GLOB.world_href_log) + start_log(GLOB.sql_error_log) start_log(GLOB.world_qdel_log) start_log(GLOB.world_map_error_log) start_log(GLOB.world_runtime_log) @@ -385,100 +391,6 @@ GLOBAL_LIST(topic_status_cache) status = . -#define FAILED_DB_CONNECTION_CUTOFF 5 -var/failed_db_connections = 0 -var/failed_old_db_connections = 0 - -/hook/startup/proc/connectDB() - if(!CONFIG_GET(flag/sql_enabled)) - log_world("SQL connection disabled in config_legacy.") - else if(!setup_database_connection()) - log_world("Your server failed to establish a connection with the feedback database.") - else - log_world("Feedback database connection established.") - return 1 - -proc/setup_database_connection() - - if(failed_db_connections > FAILED_DB_CONNECTION_CUTOFF) //If it failed to establish a connection more than 5 times in a row, don't bother attempting to conenct anymore. - return 0 - - if(!dbcon) - dbcon = new() - - var/user = sqlfdbklogin - var/pass = sqlfdbkpass - var/db = sqlfdbkdb - var/address = sqladdress - var/port = sqlport - - dbcon.Connect("dbi:mysql:[db]:[address]:[port]","[user]","[pass]") - . = dbcon.IsConnected() - if ( . ) - failed_db_connections = 0 //If this connection succeeded, reset the failed connections counter. - else - failed_db_connections++ //If it failed, increase the failed connections counter. - world.log << dbcon.ErrorMsg() - - return . - -//This proc ensures that the connection to the feedback database (global variable dbcon) is established -proc/establish_db_connection() - if(failed_db_connections > FAILED_DB_CONNECTION_CUTOFF) - return 0 - - if(!dbcon || !dbcon.IsConnected()) - return setup_database_connection() - else - return 1 - - -/hook/startup/proc/connectOldDB() - if(!CONFIG_GET(flag/sql_enabled)) - log_world("SQL connection disabled in config_legacy.") - else if(!setup_old_database_connection()) - log_world("Your server failed to establish a connection with the SQL database.") - else - log_world("SQL database connection established.") - return 1 - -//These two procs are for the old database, while it's being phased out. See the tgstation.sql file in the SQL folder for more information. -proc/setup_old_database_connection() - - if(failed_old_db_connections > FAILED_DB_CONNECTION_CUTOFF) //If it failed to establish a connection more than 5 times in a row, don't bother attempting to conenct anymore. - return 0 - - if(!dbcon_old) - dbcon_old = new() - - var/user = sqllogin - var/pass = sqlpass - var/db = sqldb - var/address = sqladdress - var/port = sqlport - - dbcon_old.Connect("dbi:mysql:[db]:[address]:[port]","[user]","[pass]") - . = dbcon_old.IsConnected() - if ( . ) - failed_old_db_connections = 0 //If this connection succeeded, reset the failed connections counter. - else - failed_old_db_connections++ //If it failed, increase the failed connections counter. - world.log << dbcon.ErrorMsg() - - return . - -//This proc ensures that the connection to the feedback database (global variable dbcon) is established -proc/establish_old_db_connection() - if(failed_old_db_connections > FAILED_DB_CONNECTION_CUTOFF) - return 0 - - if(!dbcon_old || !dbcon_old.IsConnected()) - return setup_old_database_connection() - else - return 1 - -#undef FAILED_DB_CONNECTION_CUTOFF - /world/proc/update_hub_visibility(new_value) //CITADEL PROC: TG's method of changing visibility if(new_value) //I'm lazy so this is how I wrap it to a bool number new_value = TRUE diff --git a/code/global.dm b/code/global.dm index a9de47eda5c..99645338c40 100644 --- a/code/global.dm +++ b/code/global.dm @@ -99,11 +99,6 @@ var/forum_authenticated_group = "10" var/fileaccess_timer = 0 var/custom_event_msg = null -// Database connections. A connection is established on world creation. -// Ideally, the connection dies when the server restarts (After feedback logging.). -var/DBConnection/dbcon = new() // Feedback database (New database) -var/DBConnection/dbcon_old = new() // /tg/station database (Old database) -- see the files in the SQL folder for information on what goes where. - // Added for Xenoarchaeology, might be useful for other stuff. var/global/list/alphabet_uppercase = list("A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z") diff --git a/code/modules/admin/DB ban/functions.dm b/code/modules/admin/DB ban/functions.dm index 1213f4b9e6c..0b6400320ce 100644 --- a/code/modules/admin/DB ban/functions.dm +++ b/code/modules/admin/DB ban/functions.dm @@ -4,8 +4,7 @@ datum/admins/proc/DB_ban_record(var/bantype, var/mob/banned_mob, var/duration = if(!check_rights(R_MOD,0) && !check_rights(R_BAN)) return - establish_db_connection() - if(!dbcon.IsConnected()) + if(!SSdbcore.Connect()) return var/serverip = "[world.internet_address]:[world.port]" @@ -69,9 +68,28 @@ datum/admins/proc/DB_ban_record(var/bantype, var/mob/banned_mob, var/duration = reason = sql_sanitize_text(reason) - var/sql = "INSERT INTO erro_ban (`id`,`bantime`,`serverip`,`bantype`,`reason`,`job`,`duration`,`rounds`,`expiration_time`,`ckey`,`computerid`,`ip`,`a_ckey`,`a_computerid`,`a_ip`,`who`,`adminwho`,`edits`,`unbanned`,`unbanned_datetime`,`unbanned_ckey`,`unbanned_computerid`,`unbanned_ip`) VALUES (null, Now(), '[serverip]', '[bantype_str]', '[reason]', '[job]', [(duration)?"[duration]":"0"], [(rounds)?"[rounds]":"0"], Now() + INTERVAL [(duration>0) ? duration : 0] MINUTE, '[ckey]', '[computerid]', '[ip]', '[a_ckey]', '[a_computerid]', '[a_ip]', '[who]', '[adminwho]', '', null, null, null, null, null)" - var/DBQuery/query_insert = dbcon.NewQuery(sql) - query_insert.Execute() + var/sql = "INSERT INTO [format_table_name("ban")] \ + (`id`,`bantime`,`serverip`,`bantype`,`reason`,`job`,`duration`,`rounds`,`expiration_time`,`ckey`,`computerid`,`ip`,`a_ckey`,`a_computerid`,`a_ip`,`who`,`adminwho`,`edits`,`unbanned`,`unbanned_datetime`,`unbanned_ckey`,`unbanned_computerid`,`unbanned_ip`) \ + VALUES (null, Now(), :ip, :type, :reason, :job, :duration, :rounds, Now() + INTERVAL :duration MINUTE, :ckey, :cid, :ip, :a_ckey, :a_cid, :a_ip, :who, :adminwho, '', null, null, null, null, null)" + SSdbcore.RunQuery( + sql, + list( + "ip" = serverip, + "type" = bantype_str, + "reason" = reason, + "job" = job, + "duration" = duration? duration : 0, + "rounds" = rounds? rounds : 0, + "ckey" = ckey, + "cid" = computerid, + "ip" = ip, + "a_ckey" = a_ckey, + "a_cid" = a_computerid, + "a_ip" = a_ip, + "who" = who, + "adminwho" = adminwho + ) + ) to_chat(usr, "Ban saved to database.") message_admins("[key_name_admin(usr)] has added a [bantype_str] for [ckey] [(job)?"([job])":""] [(duration > 0)?"([duration] minutes)":""] with the reason: \"[reason]\" to the ban database.",1) @@ -108,19 +126,23 @@ datum/admins/proc/DB_ban_unban(var/ckey, var/bantype, var/job = "") else bantype_sql = "bantype = '[bantype_str]'" - var/sql = "SELECT id FROM erro_ban WHERE ckey = '[ckey]' AND [bantype_sql] AND (unbanned is null OR unbanned = false)" + var/sql = "SELECT id FROM [format_table_name("ban")] WHERE ckey = :ckey AND [bantype_sql] AND (unbanned is null OR unbanned = false)" if(job) - sql += " AND job = '[job]'" + sql += " AND job = :job" - establish_db_connection() - if(!dbcon.IsConnected()) + if(!SSdbcore.Connect()) return var/ban_id var/ban_number = 0 //failsafe - var/DBQuery/query = dbcon.NewQuery(sql) - query.Execute() + var/datum/db_query/query = SSdbcore.RunQuery( + sql, + list( + "ckey" = ckey, + "job" = job + ) + ) while(query.NextRow()) ban_id = query.item[1] ban_number++; @@ -149,8 +171,12 @@ datum/admins/proc/DB_ban_edit(var/banid = null, var/param = null) to_chat(usr, "Cancelled") return - var/DBQuery/query = dbcon.NewQuery("SELECT ckey, duration, reason FROM erro_ban WHERE id = [banid]") - query.Execute() + var/datum/db_query/query = SSdbcore.RunQuery( + "SELECT ckey, duration, reason FROM [format_table_name("ban")] WHERE id = :id", + list( + "id" = banid + ) + ) var/eckey = usr.ckey //Editing admin ckey var/pckey //(banned) Player ckey @@ -177,8 +203,17 @@ datum/admins/proc/DB_ban_edit(var/banid = null, var/param = null) to_chat(usr, "Cancelled") return - var/DBQuery/update_query = dbcon.NewQuery("UPDATE erro_ban SET reason = '[value]', edits = CONCAT(edits,'- [eckey] changed ban reason from \\\"[reason]\\\" to \\\"[value]\\\"
') WHERE id = [banid]") - update_query.Execute() + SSdbcore.RunQuery( + "UPDATE [format_table_name("ban")] SET reason = :reason, \ + edits = CONCAT(edits, '- :ckey changed ban reason from \\\":oldreason\\\" to \\\":reason\\\"
') \ + WHERE id = :id", + list( + "reason" = value, + "oldreason" = reason, + "id" = banid, + "ckey" = eckey + ) + ) message_admins("[key_name_admin(usr)] has edited a ban for [pckey]'s reason from [reason] to [value]",1) if("duration") if(!value) @@ -186,10 +221,16 @@ datum/admins/proc/DB_ban_edit(var/banid = null, var/param = null) if(!isnum(value) || !value) to_chat(usr, "Cancelled") return - - var/DBQuery/update_query = dbcon.NewQuery("UPDATE erro_ban SET duration = [value], edits = CONCAT(edits,'- [eckey] changed ban duration from [duration] to [value]
'), expiration_time = DATE_ADD(bantime, INTERVAL [value] MINUTE) WHERE id = [banid]") - message_admins("[key_name_admin(usr)] has edited a ban for [pckey]'s duration from [duration] to [value]",1) - update_query.Execute() + SSdbcore.RunQuery( + "UPDATE [format_table_name("ban")] SET duration = :duration, \ + edits = CONCAT(edits, '- :ckey changed ban duration from :oldduration to :duration
'), expiration_time = DATE_ADD(bantime, INTERVAL :duration MINUTE) \ + WHERE id = :id", + list( + "duration" = value, + "oldduration" = duration, + "id" = banid + ) + ) if("unban") if(alert("Unban [pckey]?", "Unban?", "Yes", "No") == "Yes") DB_ban_unban_by_id(banid) @@ -201,21 +242,23 @@ datum/admins/proc/DB_ban_edit(var/banid = null, var/param = null) to_chat(usr, "Cancelled") return -datum/admins/proc/DB_ban_unban_by_id(var/id) +/datum/admins/proc/DB_ban_unban_by_id(var/id) + if(!check_rights(R_BAN)) + return - if(!check_rights(R_BAN)) return - - var/sql = "SELECT ckey FROM erro_ban WHERE id = [id]" - - establish_db_connection() - if(!dbcon.IsConnected()) + if(!SSdbcore.Connect()) return var/ban_number = 0 //failsafe - var/pckey - var/DBQuery/query = dbcon.NewQuery(sql) - query.Execute() + + var/datum/db_query/query = SSdbcore.RunQuery( + "SEELECT ckey FROM [format_table_name("ban")] WHERE id = :id", + list( + "id" = id + ) + ) + while(query.NextRow()) pckey = query.item[1] ban_number++; @@ -235,12 +278,17 @@ datum/admins/proc/DB_ban_unban_by_id(var/id) var/unban_computerid = src.owner:computer_id var/unban_ip = src.owner:address - var/sql_update = "UPDATE erro_ban SET unbanned = 1, unbanned_datetime = Now(), unbanned_ckey = '[unban_ckey]', unbanned_computerid = '[unban_computerid]', unbanned_ip = '[unban_ip]' WHERE id = [id]" message_admins("[key_name_admin(usr)] has lifted [pckey]'s ban.",1) - var/DBQuery/query_update = dbcon.NewQuery(sql_update) - query_update.Execute() - + SSdbcore.RunQuery( + "UPDATE [format_table_name("ban")] SET unbanned = 1, unbanned_datetime = Now(), unbanned_ckey = :ckey, unbanned_computerid = :cid, unbanned_ip = :ip WHERE id = :id", + list( + "ckey" = unban_ckey, + "cid" = unban_computerid, + "ip" = unban_ip, + "id" = id + ) + ) /client/proc/DB_ban_panel() set category = "Admin" @@ -259,8 +307,7 @@ datum/admins/proc/DB_ban_unban_by_id(var/id) if(!check_rights(R_BAN)) return - establish_db_connection() - if(!dbcon.IsConnected()) + if(!SSdbcore.Connect()) to_chat(usr, "Failed to establish database connection") return @@ -359,22 +406,22 @@ datum/admins/proc/DB_ban_unban_by_id(var/id) if(!match) if(adminckey) - adminsearch = "AND a_ckey = '[adminckey]' " + adminsearch = "AND a_ckey = :a_ckey " if(playerckey) - playersearch = "AND ckey = '[playerckey]' " + playersearch = "AND ckey = :ckey " if(playerip) - ipsearch = "AND ip = '[playerip]' " + ipsearch = "AND ip = :ip " if(playercid) - cidsearch = "AND computerid = '[playercid]' " + cidsearch = "AND computerid = :cid " else if(adminckey && length(adminckey) >= 3) - adminsearch = "AND a_ckey LIKE '[adminckey]%' " + adminsearch = "AND a_ckey LIKE ':a_ckey%' " if(playerckey && length(playerckey) >= 3) - playersearch = "AND ckey LIKE '[playerckey]%' " + playersearch = "AND ckey LIKE ':ckey%' " if(playerip && length(playerip) >= 3) - ipsearch = "AND ip LIKE '[playerip]%' " + ipsearch = "AND ip LIKE ':ip%' " if(playercid && length(playercid) >= 7) - cidsearch = "AND computerid LIKE '[playercid]%' " + cidsearch = "AND computerid LIKE ':cid%' " if(dbbantype) bantypesearch = "AND bantype = " @@ -389,8 +436,17 @@ datum/admins/proc/DB_ban_unban_by_id(var/id) else bantypesearch += "'PERMABAN' " - var/DBQuery/select_query = dbcon.NewQuery("SELECT id, bantime, bantype, reason, job, duration, expiration_time, ckey, a_ckey, unbanned, unbanned_ckey, unbanned_datetime, edits, ip, computerid FROM erro_ban WHERE 1 [playersearch] [adminsearch] [ipsearch] [cidsearch] [bantypesearch] ORDER BY bantime DESC LIMIT 100") - select_query.Execute() + var/datum/db_query/select_query = SSdbcore.RunQuery( + "SELECT id, bantime, bantype, reason, job, duration, expiration_time, ckey, a_ckey, unbanned, unbanned_ckey, unbanned_datetime, edits, ip, computerid \ + FROM [format_table_name("ban")] \ + WHERE 1 [playersearch] [adminsearch] [ipsearch] [cidsearch] [bantypesearch] ORDER BY bantime DESC LIMIT 100", + list( + "a_ckey" = adminckey, + "ckey" = playerckey, + "ip" = playerip, + "cid" = playercid + ) + ) var/now = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") // MUST BE the same format as SQL gives us the dates in, and MUST be least to most specific (i.e. year, month, day not day, month, year) diff --git a/code/modules/admin/IsBanned.dm b/code/modules/admin/IsBanned.dm index cc0bee6b678..9da50ee1e0f 100644 --- a/code/modules/admin/IsBanned.dm +++ b/code/modules/admin/IsBanned.dm @@ -68,7 +68,7 @@ world/IsBanned(key,address,computer_id,type,real_bans_only=FALSE) var/ckeytext = ckey - if(!establish_db_connection()) + if(!SSdbcore.Connect()) log_world("Ban database connection failure. Key [ckeytext] not checked") log_misc("Ban database connection failure. Key [ckeytext] not checked") key_cache[key] = 0 @@ -81,15 +81,20 @@ world/IsBanned(key,address,computer_id,type,real_bans_only=FALSE) var/cidquery = "" if(address) failedip = 0 - ipquery = " OR ip = '[address]' " + ipquery = " OR ip = ':ip' " if(computer_id) failedcid = 0 - cidquery = " OR computerid = '[computer_id]' " + cidquery = " OR computerid = ':cid' " - var/DBQuery/query = dbcon.NewQuery("SELECT ckey, ip, computerid, a_ckey, reason, expiration_time, duration, bantime, bantype FROM erro_ban WHERE (ckey = '[ckeytext]' [ipquery] [cidquery]) AND (bantype = 'PERMABAN' OR (bantype = 'TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)") - - query.Execute() + var/datum/db_query/query = SSdbcore.RunQuery( + "SELECT ckey, ip, computerid, a_ckey, reason, expiration_time, duration, bantime, bantype FROM [format_table_name("ban")] WHERE (ckey = :ckey [ipquery] [cidquery]) AND (bantype = 'PERMABAN' OR (bantype = 'TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)", + list( + "ckey" = ckeytext, + "ip" = address, + "cid" = computer_id + ) + ) while(query.NextRow()) var/pckey = query.item[1] diff --git a/code/modules/admin/admin_ranks.dm b/code/modules/admin/admin_ranks.dm index 2787e2c2873..3d5056acc15 100644 --- a/code/modules/admin/admin_ranks.dm +++ b/code/modules/admin/admin_ranks.dm @@ -105,16 +105,18 @@ var/list/admin_ranks = list() //list of all ranks with associated rights else //The current admin system uses SQL - establish_db_connection() - if(!dbcon.IsConnected()) + if(!SSdbcore.Connect()) log_world("Failed to connect to database in load_admins(). Reverting to legacy system.") log_misc("Failed to connect to database in load_admins(). Reverting to legacy system.") config_legacy.admin_legacy_system = 1 load_admins() return - var/DBQuery/query = dbcon.NewQuery("SELECT ckey, rank, level, flags FROM erro_admin") - query.Execute() + var/datum/db_query/query = SSdbcore.RunQuery( + "SELECT ckey, rank, level, flags FROM [format_table_name("admin")]", + list() + ) + while(query.NextRow()) var/ckey = query.item[1] var/rank = query.item[2] diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index a844e34b650..8c0bb8e1d8f 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -191,7 +191,8 @@ var/list/admin_verbs_server = list( /client/proc/recipe_dump, /client/proc/panicbunker, /client/proc/ip_reputation, - /client/proc/paranoia_logging + /client/proc/paranoia_logging, + /client/proc/reestablish_db_connection ) var/list/admin_verbs_debug = list( diff --git a/code/modules/admin/banjob.dm b/code/modules/admin/banjob.dm index 434fe486b58..d0262c4f2d6 100644 --- a/code/modules/admin/banjob.dm +++ b/code/modules/admin/banjob.dm @@ -65,7 +65,7 @@ DEBUG jobban_keylist=list() log_admin("jobban_keylist was empty") else - if(!establish_db_connection()) + if(!SSdbcore.Connect()) log_world("Database connection failed. Reverting to the legacy ban system.") log_misc("Database connection failed. Reverting to the legacy ban system.") config_legacy.ban_legacy_system = 1 @@ -73,8 +73,10 @@ DEBUG return //Job permabans - var/DBQuery/query = dbcon.NewQuery("SELECT ckey, job FROM erro_ban WHERE bantype = 'JOB_PERMABAN' AND isnull(unbanned)") - query.Execute() + var/datum/db_query/query = SSdbcore.RunQuery( + "SELECT ckey, job FROM [format_table_name("ban")] WHERE bantype = 'JOB_PERMABAN' AND isnull(unbanned)", + list() + ) while(query.NextRow()) var/ckey = query.item[1] @@ -83,8 +85,10 @@ DEBUG jobban_keylist.Add("[ckey] - [job]") //Job tempbans - var/DBQuery/query1 = dbcon.NewQuery("SELECT ckey, job FROM erro_ban WHERE bantype = 'JOB_TEMPBAN' AND isnull(unbanned) AND expiration_time > Now()") - query1.Execute() + var/datum/db_query/query1 = SSdbcore.RunQuery( + "SELECT ckey, job FROM [format_table_name("ban")] WHERE bantype = 'JOB_TEMPBAN' AND isnull(unbanned) AND expiration_time > Now()", + list() + ) while(query1.NextRow()) var/ckey = query1.item[1] diff --git a/code/modules/admin/permissionverbs/permissionedit.dm b/code/modules/admin/permissionverbs/permissionedit.dm index e24e0942aa5..18b7e3aa902 100644 --- a/code/modules/admin/permissionverbs/permissionedit.dm +++ b/code/modules/admin/permissionverbs/permissionedit.dm @@ -58,9 +58,8 @@ to_chat(usr, "You do not have permission to do this!") return - establish_db_connection() - if(!dbcon.IsConnected()) + if(!SSdbcore.Connect()) to_chat(usr, "Failed to establish database connection") return @@ -75,8 +74,12 @@ if(!istext(adm_ckey) || !istext(new_rank)) return - var/DBQuery/select_query = dbcon.NewQuery("SELECT id FROM erro_admin WHERE ckey = '[adm_ckey]'") - select_query.Execute() + var/datum/db_query/select_query = SSdbcore.RunQuery( + "SELECT id FROM [format_table_name("admin")] WHERE ckey = :ckey", + list( + "ckey" = adm_ckey + ) + ) var/new_admin = 1 var/admin_id @@ -85,17 +88,39 @@ admin_id = text2num(select_query.item[1]) if(new_admin) - var/DBQuery/insert_query = dbcon.NewQuery("INSERT INTO `erro_admin` (`id`, `ckey`, `rank`, `level`, `flags`) VALUES (null, '[adm_ckey]', '[new_rank]', -1, 0)") - insert_query.Execute() - var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO `test`.`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Added new admin [adm_ckey] to rank [new_rank]');") - log_query.Execute() + SSdbcore.RunQuery( + "INSERT INTO [format_table_name("admin")] (id, ckey, rank, level, flags) VALUES (null, :ckey, :rank, -1, 0)", + list( + "ckey" = adm_ckey, + "rank" = new_rank + ) + ) + SSdbcore.RunQuery( + "INSERT INTO [format_table_name("admin_log")] (id, datetime, adminckey, adminip, log) VALUES (NULL, NOW(), :ckey, :ip, :logstr)", + list( + "ckey" = sanitizeSQL(usr.ckey), + "ip" = sanitizeSQL(usr.client.address), + "Added new admin [adm_ckey] to rank [new_rank]" + ) + ) to_chat(usr, "New admin added.") else if(!isnull(admin_id) && isnum(admin_id)) - var/DBQuery/insert_query = dbcon.NewQuery("UPDATE `erro_admin` SET rank = '[new_rank]' WHERE id = [admin_id]") - insert_query.Execute() - var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO `test`.`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Edited the rank of [adm_ckey] to [new_rank]');") - log_query.Execute() + SSdbcore.RunQuery( + "UPDATE [format_table_name("admin")] SET rank = :rank WHERE id = :id", + list( + "rank" = new_rank, + "id" = admin_id + ) + ) + SSdbcore.RunQuery( + "INSERT INTO [format_table_name("admin_log")] (id, datetime, adminckey, adminip, log) VALUES (NULL, Now(), :ckey, :addr, :log)", + list( + "ckey" = usr.ckey, + "addr" = usr.client.address, + "log" = "Edited the rank of [adm_ckey] to [new_rank]" + ) + ) to_chat(usr, "Admin rank changed.") /datum/admins/proc/log_admin_permission_modification(var/adm_ckey, var/new_permission) @@ -108,8 +133,7 @@ to_chat(usr, "You do not have permission to do this!") return - establish_db_connection() - if(!dbcon.IsConnected()) + if(!SSdbcore.Connect()) to_chat(usr, "Failed to establish database connection") return @@ -127,8 +151,12 @@ if(!istext(adm_ckey) || !isnum(new_permission)) return - var/DBQuery/select_query = dbcon.NewQuery("SELECT id, flags FROM erro_admin WHERE ckey = '[adm_ckey]'") - select_query.Execute() + var/datum/db_query/select_query = SSdbcore.RunQuery( + "SELECT id, flags FROM [format_table_name("admin")] WHERE ckey = :ckey", + list( + "ckey" = adm_ckey + ) + ) var/admin_id var/admin_rights @@ -140,14 +168,36 @@ return if(admin_rights & new_permission) //This admin already has this permission, so we are removing it. - var/DBQuery/insert_query = dbcon.NewQuery("UPDATE `erro_admin` SET flags = [admin_rights & ~new_permission] WHERE id = [admin_id]") - insert_query.Execute() - var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO `test`.`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Removed permission [rights2text(new_permission)] (flag = [new_permission]) to admin [adm_ckey]');") - log_query.Execute() + SSdbcore.RunQuery( + "UPDATE [format_table_name("admin")] SET flags = :flags WHERE id = :id", + list( + "flags" = admin_rights & ~new_permission, + "id" = admin_id + ) + ) + SSdbcore.RunQuery( + "INSERT INTO [format_table_name("admin_log")] (id, datetime, adminckey, adminip, log) VALUES (NULL, Now(), :ckey, :addr, :log)", + list( + "ckey" = usr.ckey, + "addr" = usr.client.address, + "log" = "Removed permission [rights2text(new_permission)] (flag = [new_permission]) to admin [adm_ckey]" + ) + ) to_chat(usr, "Permission removed.") else //This admin doesn't have this permission, so we are adding it. - var/DBQuery/insert_query = dbcon.NewQuery("UPDATE `erro_admin` SET flags = '[admin_rights | new_permission]' WHERE id = [admin_id]") - insert_query.Execute() - var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO `test`.`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Added permission [rights2text(new_permission)] (flag = [new_permission]) to admin [adm_ckey]')") - log_query.Execute() + SSdbcore.RunQuery( + "UPDATE [format_table_name("admin")] SET flags = :flags WHERE id = :id", + list( + "flags" = admin_rights | new_permission, + "id" = admin_id + ) + ) + SSdbcore.RunQuery( + "INSERT INTO [format_table_name("admin_log")] (id, datetime, adminckey, adminip, log) VALUES (NULL, Now(), :ckey, :addr, :log)", + list( + "ckey" = usr.ckey, + "addr" = usr.client.address, + "log" = "Added permission [rights2text(new_permission)] (flag = [new_permission]) to admin [adm_ckey]" + ) + ) to_chat(usr, "Permission added.") diff --git a/code/modules/admin/verbs/check_customitem_activity.dm b/code/modules/admin/verbs/check_customitem_activity.dm index 80bbfccc120..9092051ab98 100644 --- a/code/modules/admin/verbs/check_customitem_activity.dm +++ b/code/modules/admin/verbs/check_customitem_activity.dm @@ -29,8 +29,7 @@ var/inactive_keys = "None
" if(checked_for_inactives) return - establish_db_connection() - if(!dbcon.IsConnected()) + if(!SSdbcore.Connect()) return //grab all ckeys associated with custom items @@ -55,8 +54,10 @@ var/inactive_keys = "None
" //run a query to get all ckeys inactive for over 2 months var/list/inactive_ckeys = list() if(ckeys_with_customitems.len) - var/DBQuery/query_inactive = dbcon.NewQuery("SELECT ckey, lastseen FROM erro_player WHERE datediff(Now(), lastseen) > 60") - query_inactive.Execute() + var/datum/db_query/query_inactive = SSdbcore.RunQuery( + "SELECT ckey, lastseen FROM [format_table_name("player")] WHERE datediff(Now(), lastseen) > 60", + list() + ) while(query_inactive.NextRow()) var/cur_ckey = query_inactive.item[1] //if the ckey has a custom item attached, output it @@ -67,9 +68,13 @@ var/inactive_keys = "None
" //if there are ckeys left over, check whether they have a database entry at all if(ckeys_with_customitems.len) for(var/cur_ckey in ckeys_with_customitems) - var/DBQuery/query_inactive = dbcon.NewQuery("SELECT ckey FROM erro_player WHERE ckey = '[cur_ckey]'") - query_inactive.Execute() - if(!query_inactive.RowCount()) + var/datum/db_query/query_inactive = SSdbcore.RunQuery( + "SELECT ckey FROM [format_table_name("player")] WHERE ckey = :ckey", + list( + "ckey" = cur_ckey + ) + ) + if(!query_inactive.rows) inactive_ckeys += cur_ckey if(inactive_ckeys.len) diff --git a/code/modules/admin/verbs/debug/reestablish_db_connection.dm b/code/modules/admin/verbs/debug/reestablish_db_connection.dm new file mode 100644 index 00000000000..eda196614d9 --- /dev/null +++ b/code/modules/admin/verbs/debug/reestablish_db_connection.dm @@ -0,0 +1,30 @@ +/client/proc/reestablish_db_connection() + set category = "Special Verbs" + set name = "Reestablish DB Connection" + if (!CONFIG_GET(flag/sql_enabled)) + to_chat(usr, "The Database is not enabled!") + return + + if (SSdbcore.IsConnected()) + if (!check_rights(R_DEBUG,0)) + alert("The database is already connected! (Only those with +debug can force a reconnection)", "The database is already connected!") + return + + var/reconnect = alert("The database is already connected! If you *KNOW* that this is incorrect, you can force a reconnection", "The database is already connected!", "Force Reconnect", "Cancel") + if (reconnect != "Force Reconnect") + return + + SSdbcore.Disconnect() + log_admin("[key_name(usr)] has forced the database to disconnect") + message_admins("[key_name_admin(usr)] has forced the database to disconnect!") + // SSblackbox.record_feedback("tally", "admin_verb", 1, "Force Reestablished Database Connection") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + log_admin("[key_name(usr)] is attempting to re-established the DB Connection") + message_admins("[key_name_admin(usr)] is attempting to re-established the DB Connection") + // SSblackbox.record_feedback("tally", "admin_verb", 1, "Reestablished Database Connection") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + SSdbcore.failed_connections = 0 + if(!SSdbcore.Connect()) + message_admins("Database connection failed: " + SSdbcore.ErrorMsg()) + else + message_admins("Database connection re-established") diff --git a/code/modules/admin/verbs/panicbunker.dm b/code/modules/admin/verbs/panicbunker.dm index 6963769e058..0a5155f2a3b 100644 --- a/code/modules/admin/verbs/panicbunker.dm +++ b/code/modules/admin/verbs/panicbunker.dm @@ -14,7 +14,7 @@ GLOBAL_LIST_EMPTY(bunker_passthrough) config_legacy.panic_bunker = (!config_legacy.panic_bunker) log_and_message_admins("[key_name(usr)] has toggled the Panic Bunker, it is now [(config_legacy.panic_bunker?"on":"off")]") - if (config_legacy.panic_bunker && (!dbcon || !dbcon.IsConnected())) + if (config_legacy.panic_bunker && (!SSdbcore.Connect())) message_admins("The Database is not connected! Panic bunker will not work until the connection is reestablished.") feedback_add_details("admin_verb","PANIC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -57,7 +57,7 @@ GLOBAL_LIST_EMPTY(bunker_passthrough) config_legacy.paranoia_logging = (!config_legacy.paranoia_logging) log_and_message_admins("[key_name(usr)] has toggled Paranoia Logging, it is now [(config_legacy.paranoia_logging?"on":"off")]") - if (config_legacy.paranoia_logging && (!dbcon || !dbcon.IsConnected())) + if (config_legacy.paranoia_logging && (!SSdbcore.Connect())) message_admins("The Database is not connected! Paranoia logging will not be able to give 'player age' (time since first connection) warnings, only Byond account warnings.") feedback_add_details("admin_verb","PARLOG") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -71,6 +71,6 @@ GLOBAL_LIST_EMPTY(bunker_passthrough) config_legacy.ip_reputation = (!config_legacy.ip_reputation) log_and_message_admins("[key_name(usr)] has toggled IP reputation checks, it is now [(config_legacy.ip_reputation?"on":"off")].") - if (config_legacy.ip_reputation && (!dbcon || !dbcon.IsConnected())) + if (config_legacy.ip_reputation && (!SSdbcore.Connect())) message_admins("The database is not connected! IP reputation logging will not be able to allow existing players to bypass the reputation checks (if that is enabled).") feedback_add_details("admin_verb","IPREP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/awaymissions/corpse.dm b/code/modules/awaymissions/corpse.dm index cf709e15401..c6a75be8f06 100644 --- a/code/modules/awaymissions/corpse.dm +++ b/code/modules/awaymissions/corpse.dm @@ -33,7 +33,6 @@ . = M M.set_species(species) M.real_name = src.name - M.death(1) //Kills the new mob if(src.corpseuniform) M.equip_to_slot_or_del(new src.corpseuniform(M), SLOT_ID_UNIFORM) if(src.corpsesuit) @@ -77,6 +76,7 @@ W.assignment = corpseidjob M.set_id_info(W) M.equip_to_slot_or_del(W, SLOT_ID_WORN_ID) + INVOKE_ASYNC(M, /mob/proc/death) /atom/movable/spawner/corpse/syndicatesoldier name = "Mercenary" diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 23b71cea63a..afd9b0caa94 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -453,14 +453,17 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( // Returns null if no DB connection can be established, or -1 if the requested key was not found in the database /proc/get_player_age(key) - establish_db_connection() - if(!dbcon.IsConnected()) + if(!SSdbcore.Connect()) return null var/sql_ckey = sql_sanitize_text(ckey(key)) - var/DBQuery/query = dbcon.NewQuery("SELECT datediff(Now(),firstseen) as age FROM erro_player WHERE ckey = '[sql_ckey]'") - query.Execute() + var/datum/db_query/query = SSdbcore.RunQuery( + "SELECT datediff(Now(), firstseen) as age FROM [format_table_name("player")] WHERE ckey = :ckey", + list( + "ckey" = sql_ckey + ) + ) if(query.NextRow()) return text2num(query.item[1]) @@ -473,14 +476,17 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( if ( IsGuestKey(src.key) ) return - establish_db_connection() - if(!dbcon.IsConnected()) + if(!SSdbcore.Connect()) return var/sql_ckey = sql_sanitize_text(src.ckey) - var/DBQuery/query = dbcon.NewQuery("SELECT id, datediff(Now(),firstseen) as age FROM erro_player WHERE ckey = '[sql_ckey]'") - query.Execute() + var/datum/db_query/query = SSdbcore.RunQuery( + "SELECT id, datediff(Now(), firstseen) as age FROM [format_table_name("player")] WHERE ckey = :ckey", + list( + "ckey" = sql_ckey + ) + ) var/sql_id = 0 player_age = -1 // New players won't have an entry so knowing we have a connection we set this to zero to be updated if their is a record. while(query.NextRow()) @@ -489,20 +495,33 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( break account_join_date = sanitizeSQL(findJoinDate()) - if(account_join_date && dbcon.IsConnected()) - var/DBQuery/query_datediff = dbcon.NewQuery("SELECT DATEDIFF(Now(),'[account_join_date]')") - if(query_datediff.Execute() && query_datediff.NextRow()) + if(account_join_date && SSdbcore.Connect()) + var/datum/db_query/query_datediff = SSdbcore.RunQuery( + "SELECT DATEDIFF(Now(), :date)", + list( + "date" = account_join_date + ) + ) + if(query_datediff.NextRow()) account_age = text2num(query_datediff.item[1]) - var/DBQuery/query_ip = dbcon.NewQuery("SELECT ckey FROM erro_player WHERE ip = '[address]'") - query_ip.Execute() + var/datum/db_query/query_ip = SSdbcore.RunQuery( + "SELECT ckey FROM [format_table_name("player")] WHERE ip = :addr", + list( + "addr" = address + ) + ) related_accounts_ip = "" while(query_ip.NextRow()) related_accounts_ip += "[query_ip.item[1]], " break - var/DBQuery/query_cid = dbcon.NewQuery("SELECT ckey FROM erro_player WHERE computerid = '[computer_id]'") - query_cid.Execute() + var/datum/db_query/query_cid = SSdbcore.RunQuery( + "SELECT ckey FROM [format_table_name("player")] WHERE computerid = :cid", + list( + "cid" = sanitizeSQL(computer_id) + ) + ) related_accounts_cid = "" while(query_cid.NextRow()) related_accounts_cid += "[query_cid.item[1]], " @@ -556,25 +575,49 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( // Department Hours if(config_legacy.time_off) - var/DBQuery/query_hours = dbcon.NewQuery("SELECT department, hours FROM vr_player_hours WHERE ckey = '[sql_ckey]'") - query_hours.Execute() + var/datum/db_query/query_hours = SSdbcore.RunQuery( + "SELECT department, hours FROM [format_table_name("vr_player_hours")] WHERE ckey = :ckey", + list( + "ckey" = sql_ckey + ) + ) while(query_hours.NextRow()) LAZYINITLIST(department_hours) department_hours[query_hours.item[1]] = text2num(query_hours.item[2]) if(sql_id) - //Player already identified previously, we need to just update the 'lastseen', 'ip' and 'computer_id' variables - var/DBQuery/query_update = dbcon.NewQuery("UPDATE erro_player SET lastseen = Now(), ip = '[sql_ip]', computerid = '[sql_computerid]', lastadminrank = '[sql_admin_rank]' WHERE id = [sql_id]") - query_update.Execute() + SSdbcore.RunQuery( + "UPDATE [format_table_name("player")] SET lastseen = Now(), ip = :ip, computerid = :computerid, lastadminrank = :lastadminrank WHERE id = :id", + list( + "ip" = sql_ip, + "computerid" = sql_computerid, + "lastadminrank" = sql_admin_rank, + "id" = sql_id + ) + ) else //New player!! Need to insert all the stuff - var/DBQuery/query_insert = dbcon.NewQuery("INSERT INTO erro_player (id, ckey, firstseen, lastseen, ip, computerid, lastadminrank) VALUES (null, '[sql_ckey]', Now(), Now(), '[sql_ip]', '[sql_computerid]', '[sql_admin_rank]')") - query_insert.Execute() + SSdbcore.RunQuery( + "INSERT INTO [format_table_name("player")] (id, ckey, firstseen, lastseen, ip, computerid, lastadminrank) VALUES (null, :ckey, Now(), Now(), :ip, :cid, :rank)", + list( + "ckey" = sql_ckey, + "ip" = sql_ip, + "cid" = sql_computerid, + "rank" = sql_admin_rank + ) + ) //Logging player access var/serverip = "[world.internet_address]:[world.port]" - var/DBQuery/query_accesslog = dbcon.NewQuery("INSERT INTO `erro_connection_log`(`id`,`datetime`,`serverip`,`ckey`,`ip`,`computerid`) VALUES(null,Now(),'[serverip]','[sql_ckey]','[sql_ip]','[sql_computerid]');") - query_accesslog.Execute() + SSdbcore.RunQuery( + "INSERT INTO [format_table_name("connection_log")] (id, datetime, serverip, ckey, ip, computerid) VALUES (null, Now(), :serverip, :ckey, :ip, :computerid)", + list( + "serverip" = serverip, + "ckey" = sql_ckey, + "ip" = sql_ip, + "computerid" = sql_computerid + ) + ) #undef UPLOAD_LIMIT diff --git a/code/modules/library/lib_machines.dm b/code/modules/library/lib_machines.dm index f47de4a0f8d..dbb28aff068 100644 --- a/code/modules/library/lib_machines.dm +++ b/code/modules/library/lib_machines.dm @@ -30,7 +30,6 @@ datum/borrowbook // Datum used to keep track of who has borrowed what when and f var/title var/category = "Any" var/author - var/SQLquery /obj/machinery/librarypubliccomp/attack_hand(var/mob/user as mob) usr.set_machine(src) @@ -43,17 +42,20 @@ datum/borrowbook // Datum used to keep track of who has borrowed what when and f Filter by Author: [author]
\[Start Search\]
"} if(1) - establish_old_db_connection() - if(!dbcon_old.IsConnected()) + if(!SSdbcore.Connect()) dat += "ERROR: Unable to contact External Archive. Please contact your system administrator for assistance.
" - else if(!SQLquery) - dat += "ERROR: Malformed search request. Please contact your system administrator for assistance.
" else dat += {""} - var/DBQuery/query = dbcon_old.NewQuery(SQLquery) - query.Execute() + var/datum/db_query/query = SSdbcore.RunQuery( + "SELECT author, title, category, id FROM [format_table_name("library")] WHERE author LIKE '%:author%' AND title LIKE '%:title%'[category == "Any"? "" : " AND category = :category"]", + list( + "author" = author, + "title" = title, + "category" = category + ) + ) while(query.NextRow()) var/author = query.item[1] @@ -94,11 +96,6 @@ datum/borrowbook // Datum used to keep track of who has borrowed what when and f author = null author = sanitizeSQL(author) if(href_list["search"]) - SQLquery = "SELECT author, title, category, id FROM library WHERE " - if(category == "Any") - SQLquery += "author LIKE '%[author]%' AND title LIKE '%[title]%'" - else - SQLquery += "author LIKE '%[author]%' AND title LIKE '%[title]%' AND category='[category]'" screenstate = 1 if(href_list["back"]) @@ -226,18 +223,21 @@ datum/borrowbook // Datum used to keep track of who has borrowed what when and f (Return to main menu)
"} if(4) dat += "

External Archive

" - establish_old_db_connection() dat += "

Warning: System Administrator has slated this archive for removal. Personal uploads should be taken to the NT board of internal literature.

" - if(!dbcon_old.IsConnected()) + if(!SSdbcore.Connect()) dat += "ERROR: Unable to contact External Archive. Please contact your system administrator for assistance." else dat += {"(Order book by SS13BN)

AUTHORTITLECATEGORYSS13BN
TITLEThank you for your vote!") usr << browse(null,"window=privacypoll") diff --git a/code/modules/mob/new_player/poll.dm b/code/modules/mob/new_player/poll.dm index a3cf56bc201..f90ebdba3e3 100644 --- a/code/modules/mob/new_player/poll.dm +++ b/code/modules/mob/new_player/poll.dm @@ -1,12 +1,17 @@ /mob/new_player/proc/handle_privacy_poll() - establish_db_connection() - if(!dbcon.IsConnected()) + if(!SSdbcore.Connect()) return + var/voted = 0 - var/DBQuery/query = dbcon.NewQuery("SELECT * FROM erro_privacy WHERE ckey='[src.ckey]'") - query.Execute() + var/datum/db_query/query = SSdbcore.RunQuery( + "SELECT * FROM [format_table_name("privacy")] WHERE ckey = :ckey", + list( + "ckey" = ckey + ) + ) + while(query.NextRow()) voted = 1 break @@ -47,14 +52,16 @@ var/optiontext /mob/new_player/proc/handle_player_polling() - establish_db_connection() - if(dbcon.IsConnected()) + + if(SSdbcore.Connect()) var/isadmin = 0 if(src.client && src.client.holder) isadmin = 1 - var/DBQuery/select_query = dbcon.NewQuery("SELECT id, question FROM erro_poll_question WHERE [(isadmin ? "" : "adminonly = false AND")] Now() BETWEEN starttime AND endtime") - select_query.Execute() + var/datum/db_query/select_query = SSdbcore.RunQuery( + "SELECT id, question FROM [format_table_name("poll_question")] WHERE [(isadmin? "" : "adminonly = false AND")] Now() BETWEEN starttime AND endtime", + list() + ) var/output = "
Player polls" output +="
" @@ -81,11 +88,15 @@ /mob/new_player/proc/poll_player(var/pollid = -1) if(pollid == -1) return - establish_db_connection() - if(dbcon.IsConnected()) - var/DBQuery/select_query = dbcon.NewQuery("SELECT starttime, endtime, question, polltype, multiplechoiceoptions FROM erro_poll_question WHERE id = [pollid]") - select_query.Execute() + if(SSdbcore.Connect()) + + var/datum/db_query/select_query = SSdbcore.RunQuery( + "SELECT starttime, endtime, question, pollytype, multiplechoiceoptions FROM [format_table_name("poll_question")] WHERE id = :id", + list( + "id" = "[pollid]" + ) + ) var/pollstarttime = "" var/pollendtime = "" @@ -109,8 +120,13 @@ switch(polltype) //Polls that have enumerated options if("OPTION") - var/DBQuery/voted_query = dbcon.NewQuery("SELECT optionid FROM erro_poll_vote WHERE pollid = [pollid] AND ckey = '[usr.ckey]'") - voted_query.Execute() + var/datum/db_query/voted_query = SSdbcore.RunQuery( + "SELECT optionid FROM [format_table_name("poll_vote")] WHERE pollid = :id AND ckey = :ckey", + list( + "id" = "[pollid]", + "ckey" = usr.ckey + ) + ) var/voted = 0 var/votedoptionid = 0 @@ -121,8 +137,13 @@ var/list/datum/polloption/options = list() - var/DBQuery/options_query = dbcon.NewQuery("SELECT id, text FROM erro_poll_option WHERE pollid = [pollid]") - options_query.Execute() + var/datum/db_query/options_query = SSdbcore.RunQuery( + "SELECT id, text FROM [format_table_name("poll_option")] WHERE pollid = :id", + list( + "id" = pollid + ) + ) + while(options_query.NextRow()) var/datum/polloption/PO = new() PO.optionid = text2num(options_query.item[1]) @@ -162,8 +183,13 @@ //Polls with a text input if("TEXT") - var/DBQuery/voted_query = dbcon.NewQuery("SELECT replytext FROM erro_poll_textreply WHERE pollid = [pollid] AND ckey = '[usr.ckey]'") - voted_query.Execute() + var/datum/db_query/voted_query = SSdbcore.RunQuery( + "SELECT replytext FROM [format_table_name("poll_textreply")] WHERE pollid = :id AND ckey = :ckey", + list( + "id" = pollid, + "ckey" = usr.ckey + ) + ) var/voted = 0 var/vote_text = "" @@ -204,8 +230,13 @@ //Polls with a text input if("NUMVAL") - var/DBQuery/voted_query = dbcon.NewQuery("SELECT o.text, v.rating FROM erro_poll_option o, erro_poll_vote v WHERE o.pollid = [pollid] AND v.ckey = '[usr.ckey]' AND o.id = v.optionid") - voted_query.Execute() + var/datum/db_query/voted_query = SSdbcore.RunQuery( + "SELECT o.text, v.rating FROM [format_table_name("poll_option")] o, [format_table_name("poll_vote")] v WHERE o.pollid = :pid AND v.ckey = :ckey AND o.id = v.optionid", + list( + "pid" = pollid, + "ckey" = usr.ckey + ) + ) var/output = "
Player poll" output +="
" @@ -230,8 +261,12 @@ var/minid = 999999 var/maxid = 0 - var/DBQuery/option_query = dbcon.NewQuery("SELECT id, text, minval, maxval, descmin, descmid, descmax FROM erro_poll_option WHERE pollid = [pollid]") - option_query.Execute() + var/datum/db_query/option_query = SSdbcore.RunQuery( + "SELECT id, text, minval, maxval, descmin, descmid, descmax FROM [format_table_name("poll_option")] WHERE pollid = :id", + list( + "id" = pollid + ) + ) while(option_query.NextRow()) var/optionid = text2num(option_query.item[1]) var/optiontext = option_query.item[2] @@ -273,8 +308,13 @@ src << browse(output,"window=playerpoll;size=500x500") if("MULTICHOICE") - var/DBQuery/voted_query = dbcon.NewQuery("SELECT optionid FROM erro_poll_vote WHERE pollid = [pollid] AND ckey = '[usr.ckey]'") - voted_query.Execute() + var/datum/db_query/voted_query = SSdbcore.RunQuery( + "SELECT optionid FROM [format_table_name("poll_vote")] WHERE pollid = :id AND ckey = :ckey", + list( + "id" = pollid, + "ckey" = usr.ckey + ) + ) var/list/votedfor = list() var/voted = 0 @@ -286,8 +326,13 @@ var/maxoptionid = 0 var/minoptionid = 0 - var/DBQuery/options_query = dbcon.NewQuery("SELECT id, text FROM erro_poll_option WHERE pollid = [pollid]") - options_query.Execute() + var/datum/db_query/options_query = SSdbcore.RunQuery( + "SELECT id, text FROM [format_table_name("poll_option")] WHERE pollid = :id", + list( + "id" = pollid + ) + ) + while(options_query.NextRow()) var/datum/polloption/PO = new() PO.optionid = text2num(options_query.item[1]) @@ -342,11 +387,15 @@ if(!isnum(pollid) || !isnum(optionid)) return - establish_db_connection() - if(dbcon.IsConnected()) - var/DBQuery/select_query = dbcon.NewQuery("SELECT starttime, endtime, question, polltype, multiplechoiceoptions FROM erro_poll_question WHERE id = [pollid] AND Now() BETWEEN starttime AND endtime") - select_query.Execute() + if(SSdbcore.Connect()) + + var/datum/db_query/select_query = SSdbcore.RunQuery( + "SELECT starttime, endtime, question, polltype, multiplechoiceoptions FROM [format_table_name("poll_question")] WHERE id = :id AND Now() BETWEEN starttime AND endtime", + list( + "id" = pollid + ) + ) var/validpoll = 0 var/multiplechoiceoptions = 0 @@ -363,8 +412,13 @@ to_chat(usr, "Poll is not valid.") return - var/DBQuery/select_query2 = dbcon.NewQuery("SELECT id FROM erro_poll_option WHERE id = [optionid] AND pollid = [pollid]") - select_query2.Execute() + var/datum/db_query/select_query2 = SSdbcore.RunQuery( + "SELECT id FROM [format_table_name("poll_option")] WHERE id = :id AND pollid = :pollid", + list( + "id" = optionid, + "pollid" = pollid + ) + ) var/validoption = 0 @@ -378,8 +432,13 @@ var/alreadyvoted = 0 - var/DBQuery/voted_query = dbcon.NewQuery("SELECT id FROM erro_poll_vote WHERE pollid = [pollid] AND ckey = '[usr.ckey]'") - voted_query.Execute() + var/datum/db_query/voted_query = SSdbcore.RunQuery( + "SELECT id FROM [format_table_name("poll_vote")] WHERE pollid = :id AND ckey = :ckey", + list( + "id" = pollid, + "ckey" = usr.ckey + ) + ) while(voted_query.NextRow()) alreadyvoted += 1 @@ -399,8 +458,16 @@ adminrank = usr.client.holder.rank - var/DBQuery/insert_query = dbcon.NewQuery("INSERT INTO erro_poll_vote (id ,datetime ,pollid ,optionid ,ckey ,ip ,adminrank) VALUES (null, Now(), [pollid], [optionid], '[usr.ckey]', '[usr.client.address]', '[adminrank]')") - insert_query.Execute() + SSdbcore.RunQuery( + "INSERT INTO [format_table_name("poll_vote")] (id, datetime, pollid, optionid, ckey, ip, adminrank) VALUES (null, Now(), :poll, :option, :ckey, :addr, :rank)", + list( + "poll" = pollid, + "option" = optionid, + "ckey" = usr.ckey, + "addr" = usr.client.address, + "rank" = adminrank + ) + ) to_chat(usr, "Vote successful.") usr << browse(null,"window=playerpoll") @@ -412,11 +479,15 @@ if(!isnum(pollid) || !istext(replytext)) return - establish_db_connection() - if(dbcon.IsConnected()) - var/DBQuery/select_query = dbcon.NewQuery("SELECT starttime, endtime, question, polltype FROM erro_poll_question WHERE id = [pollid] AND Now() BETWEEN starttime AND endtime") - select_query.Execute() + if(SSdbcore.Connect()) + + var/datum/db_query/select_query = SSdbcore.RunQuery( + "SELECT starttime, endtime, question, polltype FROM [format_table_name("poll_question")] WHERE id = :id AND Now() BETWEEN starttime AND endtime", + list( + "id" = pollid + ) + ) var/validpoll = 0 @@ -432,8 +503,13 @@ var/alreadyvoted = 0 - var/DBQuery/voted_query = dbcon.NewQuery("SELECT id FROM erro_poll_textreply WHERE pollid = [pollid] AND ckey = '[usr.ckey]'") - voted_query.Execute() + var/datum/db_query/voted_query = SSdbcore.RunQuery( + "SELECT id FROM [format_table_name("poll_textreply")] WHERE pollid = :id AND ckey = :ckey", + list( + "id" = pollid, + "ckey" = usr.ckey + ) + ) while(voted_query.NextRow()) alreadyvoted = 1 @@ -457,8 +533,16 @@ to_chat(usr, "The text you entered was blank, contained illegal characters or was too long. Please correct the text and submit again.") return - var/DBQuery/insert_query = dbcon.NewQuery("INSERT INTO erro_poll_textreply (id ,datetime ,pollid ,ckey ,ip ,replytext ,adminrank) VALUES (null, Now(), [pollid], '[usr.ckey]', '[usr.client.address]', '[replytext]', '[adminrank]')") - insert_query.Execute() + SSdbcore.RunQuery( + "INSERT INTO [format_table_name("poll_textreply")] (id, datetime, pollid, ckey, ip, replytext, adminrank) VALUES (null, Now(), :pollid, :ckey, :addr, :reply, :rank)", + list( + "pollid" = pollid, + "ckey" = usr.ckey, + "addr" = usr.client.address, + "reply" = replytext, + "rank" = adminrank + ) + ) to_chat(usr, "Feedback logging successful.") usr << browse(null,"window=playerpoll") @@ -470,10 +554,12 @@ if(!isnum(pollid) || !isnum(optionid)) return - establish_db_connection() - if(dbcon.IsConnected()) - var/DBQuery/select_query = dbcon.NewQuery("SELECT starttime, endtime, question, polltype FROM erro_poll_question WHERE id = [pollid] AND Now() BETWEEN starttime AND endtime") + if(SSdbcore.Connect()) + var/datum/db_query/select_query = SSdbcore.NewQuery( + "SELECT starttime, endtime, question, polltype FROM [format_table_name("poll_question")] WHERE id = :pollid AND Now() BETWEEN starttime AND endtime", + list("pollid" = pollid) + ) select_query.Execute() var/validpoll = 0 @@ -484,12 +570,19 @@ validpoll = 1 break + qdel(select_query) + if(!validpoll) to_chat(usr, "Poll is not valid.") return - var/DBQuery/select_query2 = dbcon.NewQuery("SELECT id FROM erro_poll_option WHERE id = [optionid] AND pollid = [pollid]") - select_query2.Execute() + var/datum/db_query/select_query2 = SSdbcore.RunQuery( + "SELECT id FROM [format_table_name("poll_option")] WHERE id = :optionid AND pollid = :pollid", + list( + "optionid" = optionid, + "pollid" = pollid + ) + ) var/validoption = 0 @@ -497,14 +590,21 @@ validoption = 1 break + qdel(select_query2) + if(!validoption) to_chat(usr, "Poll option is not valid.") return var/alreadyvoted = 0 - var/DBQuery/voted_query = dbcon.NewQuery("SELECT id FROM erro_poll_vote WHERE optionid = [optionid] AND ckey = '[usr.ckey]'") - voted_query.Execute() + var/datum/db_query/voted_query = SSdbcore.RunQuery( + "SELECT id FROM [format_table_name("poll_vote")] WHERE optionid = :optionid AND ckey = :ckey", + list( + "optionid" = sanitizeSQL(optionid), + "ckey" = usr.ckey + ) + ) while(voted_query.NextRow()) alreadyvoted = 1 @@ -518,9 +618,17 @@ if(usr && usr.client && usr.client.holder) adminrank = usr.client.holder.rank - - var/DBQuery/insert_query = dbcon.NewQuery("INSERT INTO erro_poll_vote (id ,datetime ,pollid ,optionid ,ckey ,ip ,adminrank, rating) VALUES (null, Now(), [pollid], [optionid], '[usr.ckey]', '[usr.client.address]', '[adminrank]', [(isnull(rating)) ? "null" : rating])") - insert_query.Execute() + SSdbcore.RunQuery( + "INSERT INTO [format_table_name("poll_vote")] (id, datetime, pollid, optionid, ckey, ip, adminrank, rating) VALUES (null, Now(), :pollid, :optionid, :ckey, :address, :rank, :rating)", + list( + "pollid" = sanitizeSQL(pollid), + "optionid" = sanitizeSQL(optionid), + "ckey" = usr.ckey, + "address" = usr.client.address, + "rank" = adminrank, + "rating" = isnull(rating)? "null" : sanitizeSQL(rating) + ) + ) to_chat(usr, "Vote successful.") usr << browse(null,"window=playerpoll") diff --git a/code/modules/research/message_server.dm b/code/modules/research/message_server.dm index f4350f44411..10ec30e0dbf 100644 --- a/code/modules/research/message_server.dm +++ b/code/modules/research/message_server.dm @@ -336,12 +336,17 @@ var/obj/machinery/blackbox_recorder/blackbox if(!feedback) return round_end_data_gathering() //round_end time logging and some other data processing - establish_db_connection() - if(!dbcon.IsConnected()) return + + if(!SSdbcore.Connect()) + return + var/round_id - var/DBQuery/query = dbcon.NewQuery("SELECT MAX(round_id) AS round_id FROM erro_feedback") - query.Execute() + var/datum/db_query/query = SSdbcore.RunQuery( + "SELECT MAX(ronud_id) AS round_id FROM [format_table_name("feedback")]", + list() + ) + while(query.NextRow()) round_id = query.item[1] @@ -350,16 +355,15 @@ var/obj/machinery/blackbox_recorder/blackbox round_id++ for(var/datum/feedback_variable/FV in feedback) - var/sql = "INSERT INTO erro_feedback VALUES (null, Now(), [round_id], \"[FV.get_variable()]\", [FV.get_value()], \"[FV.get_details()]\")" - var/DBQuery/query_insert = dbcon.NewQuery(sql) - query_insert.Execute() - -// Sanitize inputs to avoid SQL injection attacks -proc/sql_sanitize_text(var/text) - text = replacetext(text, "'", "''") - text = replacetext(text, ";", "") - text = replacetext(text, "&", "") - return text + SSdbcore.RunQuery( + "INSERT INTO [format_table_name("feedback")] VALUES (null, Now(), :round_id, :variable, :value, :details)", + list( + "round_id" = "[round_id]", + "variable" = "[FV.get_variable()]", + "value" = "[FV.get_value()]", + "details" = "[FV.get_details()]" + ) + ) proc/feedback_set(var/variable,var/value) if(!blackbox) return diff --git a/code/modules/species/station/protean/protean_blob.dm b/code/modules/species/station/protean/protean_blob.dm index 348a0c1f795..66c2ad631ee 100644 --- a/code/modules/species/station/protean/protean_blob.dm +++ b/code/modules/species/station/protean/protean_blob.dm @@ -67,13 +67,13 @@ access_card = new(src) if(H) humanform = H - updatehealth() refactory = locate() in humanform.internal_organs verbs |= /mob/living/proc/hide verbs |= /mob/living/simple_mob/protean_blob/proc/useradio verbs |= /mob/living/simple_mob/protean_blob/proc/appearanceswitch verbs |= /mob/living/simple_mob/protean_blob/proc/rig_transform verbs |= /mob/living/proc/usehardsuit + INVOKE_ASYNC(src, /mob/living/proc/updatehealth) else update_icon() diff --git a/config/config.txt b/config/config.txt index 9b388d65843..e0707b68827 100644 --- a/config/config.txt +++ b/config/config.txt @@ -5,6 +5,7 @@ #$include antag_rep.txt $include resources.txt $include logging.txt +$include entries/dbconfig.txt $include entries/fail2topic.txt $include entries/game_options.txt $include entries/lobby.txt diff --git a/config/entries/dbconfig.txt b/config/entries/dbconfig.txt new file mode 100644 index 00000000000..daf292cf2e3 --- /dev/null +++ b/config/entries/dbconfig.txt @@ -0,0 +1,41 @@ +## Enables SQL/database usage +SQL_ENABLED + +## SQL address +SQL_ADDRESS localhost + +## SQL port +SQL_PORT 3306 + +## SQL user +SQL_USER root + +## SQL password +SQL_PASSWORD password + +## SQL database +SQL_DATABASE ss13 + +## Table prefix to use for server-specific stuff +SQL_SERVER_PREFIX rp_ + +## Table prefix to use for unified tables +SQL_UNIFIED_PREFIX citadel_ + +## Time in seconds for asynchronous queries to timeout +## Set to 0 for infinite +ASYNC_QUERY_TIMEOUT 10 + +## Time in seconds for blocking queries to execute before slow query timeout +## Set to 0 for infinite +## Must be less than or equal to ASYNC_QUERY_TIMEOUT +BLOCKING_QUERY_TIMEOUT 5 + +## The maximum number of additional threads BSQL is allowed to run at once +BSQL_THREAD_LIMIT 50 + +## Uncomment to enable verbose BSQL communication logs +#BSQL_DEBUG + +## Time to wait before considering a query as lingering too long +@QUERY_DEBUG_LOG_TIMEOUT 70 diff --git a/config/legacy/dbconfig.txt b/config/legacy/dbconfig.txt deleted file mode 100644 index c5fd887d017..00000000000 --- a/config/legacy/dbconfig.txt +++ /dev/null @@ -1,31 +0,0 @@ -## MySQL Connection Configuration -## This is used for stats, feedback gathering, -## administration, and the in game library. - -## Enable/disable SQL connection (comment out to disable) -#SQL_ENABLED - -# Server the MySQL database can be found at -# Examples: localhost, 200.135.5.43, www.mysqldb.com, etc. -ADDRESS localhost - -# MySQL server port (default is 3306) -PORT 3306 - -# Database the population, death, karma, etc. tables may be found in -DATABASE CitadelRP - -# Username/Login used to access the database -LOGIN mylogin - -# Password used to access the database -PASSWORD mypassword - -# The following information is for feedback tracking via the blackbox server -FEEDBACK_DATABASE test -FEEDBACK_LOGIN mylogin -FEEDBACK_PASSWORD mypassword - -# Track population and death statistics -# Comment this out to disable -#ENABLE_STAT_TRACKING diff --git a/tools/ci/ci_config.txt b/tools/ci/ci_config.txt index 4925d781bbb..e7e05a8fe21 100644 --- a/tools/ci/ci_config.txt +++ b/tools/ci/ci_config.txt @@ -1,9 +1,9 @@ SQL_ENABLED -ADDRESS 127.0.0.1 -PORT 3306 -FEEDBACK_DATABASE tg_ci -FEEDBACK_TABLEPREFIX -FEEDBACK_LOGIN root -FEEDBACK_PASSWORD -LAVALAND_BUDGET 0 -SPACE_BUDGET 0 +SQL_ADDRESS 127.0.0.1 +SQL_USER root +SQL_PORT 3306 +SQL_PASSWORD + +SQL_DATABASE ss13_ci +SQL_SERVER_PREFIX rp_ +SQL_UNIFIED_PREFIX citadel_