diff --git a/database/2025_02_07_215843_make_column_names_consistent.php b/database/2025_02_07_215843_make_column_names_consistent.php
new file mode 100644
index 00000000..d84cad0b
--- /dev/null
+++ b/database/2025_02_07_215843_make_column_names_consistent.php
@@ -0,0 +1,127 @@
+<?php
+use Index\Db\DbConnection;
+use Index\Db\Migration\DbMigration;
+
+final class MakeColumnNamesConsistent_20250207_215843 implements DbMigration {
+    public function migrate(DbConnection $conn): void {
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_audit_log
+                CHANGE COLUMN log_ip log_remote_addr VARBINARY(16) NULL DEFAULT NULL AFTER log_created;
+        SQL);
+
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_comments_categories
+                DROP FOREIGN KEY comments_categories_owner_foreign;
+        SQL);
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_comments_categories
+                CHANGE COLUMN owner_id user_id INT(10) UNSIGNED NULL DEFAULT NULL AFTER category_name,
+                DROP INDEX comments_categories_owner_foreign,
+                ADD INDEX comments_categories_user_foreign (user_id),
+                ADD CONSTRAINT comments_categories_user_foreign
+                    FOREIGN KEY (user_id)
+                    REFERENCES msz_users (user_id)
+                    ON UPDATE CASCADE
+                    ON DELETE SET NULL;
+        SQL);
+
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_emoticons
+                CHANGE COLUMN emote_hierarchy emote_rank INT(11) NOT NULL DEFAULT '0' AFTER emote_order,
+                DROP INDEX emotes_hierarchy,
+                ADD INDEX emotes_rank (emote_rank);
+        SQL);
+
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_forum_posts
+                CHANGE COLUMN post_ip post_remote_addr VARBINARY(16) NOT NULL AFTER user_id,
+                DROP INDEX posts_ip_index,
+                ADD INDEX posts_remote_addr_index (post_remote_addr);
+        SQL);
+
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_forum_topics_redirects
+                CHANGE COLUMN topic_redir_url redir_url VARCHAR(255) NOT NULL COLLATE 'ascii_bin' AFTER user_id,
+                CHANGE COLUMN topic_redir_created redir_created TIMESTAMP NOT NULL DEFAULT current_timestamp() AFTER redir_url;
+        SQL);
+
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_login_attempts
+                ADD COLUMN attempt_id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT FIRST,
+                CHANGE COLUMN attempt_ip attempt_remote_addr VARBINARY(16) NOT NULL AFTER attempt_success,
+                DROP INDEX login_attempts_ip_index,
+                ADD INDEX login_attempts_remote_addr_index (attempt_remote_addr),
+                ADD PRIMARY KEY (attempt_id);
+        SQL);
+
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_news_categories
+                CHANGE COLUMN category_is_hidden category_hidden TINYINT(1) NOT NULL DEFAULT '0' AFTER category_description,
+                DROP INDEX news_categories_is_hidden_index,
+                ADD INDEX news_categories_hidden_index (category_hidden);
+        SQL);
+
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_news_posts
+                CHANGE COLUMN post_is_featured post_featured TINYINT(1) NOT NULL DEFAULT '0' AFTER comment_section_id,
+                DROP INDEX news_posts_featured_index,
+                ADD INDEX news_posts_featured_index (post_featured);
+        SQL);
+
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_roles
+                CHANGE COLUMN role_hierarchy role_rank INT(11) NOT NULL DEFAULT '1' AFTER role_string,
+                DROP INDEX roles_hierarchy_index,
+                ADD INDEX roles_rank_index (role_rank);
+        SQL);
+
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_sessions
+                CHANGE COLUMN session_ip session_remote_addr_first VARBINARY(16) NOT NULL AFTER session_key,
+                CHANGE COLUMN session_ip_last session_remote_addr_last VARBINARY(16) NULL DEFAULT NULL AFTER session_remote_addr_first;
+        SQL);
+
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_users
+                CHANGE COLUMN username user_name VARCHAR(255) NOT NULL COLLATE 'utf8mb4_unicode_520_ci' AFTER user_id,
+                CHANGE COLUMN password user_password VARCHAR(255) NULL DEFAULT NULL COLLATE 'ascii_bin' AFTER user_name,
+                CHANGE COLUMN email user_email VARCHAR(255) NOT NULL COLLATE 'utf8mb4_unicode_520_ci' AFTER user_password,
+                CHANGE COLUMN register_ip user_remote_addr_first VARBINARY(16) NOT NULL AFTER user_email,
+                CHANGE COLUMN last_ip user_remote_addr_last VARBINARY(16) NOT NULL AFTER user_remote_addr_first,
+                CHANGE COLUMN display_role user_display_role_id INT(10) UNSIGNED NULL DEFAULT NULL AFTER user_deleted,
+                DROP INDEX users_indices,
+                DROP INDEX users_username_unique,
+                ADD UNIQUE INDEX users_name_unique (user_name),
+                DROP INDEX users_email_unique,
+                ADD UNIQUE INDEX users_email_unique (user_email),
+                DROP INDEX users_display_role_foreign,
+                ADD INDEX users_display_role_foreign (user_display_role_id),
+                ADD INDEX users_created_index (user_created),
+                ADD INDEX users_country_index (user_country),
+                ADD INDEX users_active_index (user_active),
+                ADD INDEX users_deleted_index (user_deleted),
+                ADD INDEX users_birthdate_index (user_birthdate);
+        SQL);
+
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_users_password_resets
+                DROP FOREIGN KEY msz_users_password_resets_user_id_foreign;
+        SQL);
+        $conn->execute(<<<SQL
+            ALTER TABLE msz_users_password_resets
+                ADD COLUMN reset_id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT FIRST,
+                CHANGE COLUMN reset_ip reset_remote_addr VARBINARY(16) NOT NULL AFTER user_id,
+                CHANGE COLUMN verification_code reset_code CHAR(12) NULL DEFAULT NULL COLLATE 'ascii_general_ci' AFTER reset_requested,
+                DROP INDEX users_password_resets_user_unique,
+                ADD UNIQUE INDEX users_password_resets_unique (user_id, reset_remote_addr),
+                DROP INDEX users_password_resets_token_unique,
+                ADD UNIQUE INDEX users_password_resets_code_unique (reset_code),
+                ADD PRIMARY KEY (reset_id),
+                ADD CONSTRAINT msz_users_password_resets_users_foreign
+                    FOREIGN KEY (user_id)
+                    REFERENCES msz_users (user_id)
+                    ON UPDATE CASCADE
+                    ON DELETE CASCADE;
+        SQL);
+    }
+}
diff --git a/public-legacy/settings/data.php b/public-legacy/settings/data.php
index 98e248cb..1f269067 100644
--- a/public-legacy/settings/data.php
+++ b/public-legacy/settings/data.php
@@ -126,26 +126,26 @@ if(isset($_POST['action']) && is_string($_POST['action'])) {
                         $tmpFiles = [];
 
                         try {
-                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'audit_log',              ['user_id:s:n', 'log_action:s', 'log_params:j', 'log_created:t', 'log_ip:a:n', 'log_country:s']);
+                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'audit_log',              ['user_id:s:n', 'log_action:s', 'log_params:j', 'log_created:t', 'log_remote_addr:a:n', 'log_country:s']);
                             $tmpFiles[] = db_to_zip($archive, $userInfo, 'auth_tfa',               ['user_id:s', 'tfa_token:n', 'tfa_created:t']);
                             $tmpFiles[] = db_to_zip($archive, $userInfo, 'changelog_changes',      ['change_id:s', 'user_id:s:n', 'change_action:s:n', 'change_created:t', 'change_log:s', 'change_text:s:n']);
-                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'comments_categories',    ['category_id:s', 'category_name:s', 'owner_id:s:n', 'category_created:t', 'category_locked:t:n'], 'owner_id');
+                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'comments_categories',    ['category_id:s', 'category_name:s', 'user_id:s:n', 'category_created:t', 'category_locked:t:n'], 'user_id');
                             $tmpFiles[] = db_to_zip($archive, $userInfo, 'comments_posts',         ['comment_id:s', 'category_id:s', 'user_id:s:n', 'comment_reply_to:s:n', 'comment_text:s', 'comment_created:t', 'comment_pinned:t:n', 'comment_edited:t:n', 'comment_deleted:t:n']);
                             $tmpFiles[] = db_to_zip($archive, $userInfo, 'comments_votes',         ['comment_id:s', 'user_id:s', 'comment_vote:i']);
-                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'forum_posts',            ['post_id:s', 'topic_id:s', 'forum_id:s', 'user_id:s:n', 'post_ip:a', 'post_text:s', 'post_parse:i', 'post_display_signature:b', 'post_created:t', 'post_edited:t:n', 'post_deleted:t:n']);
+                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'forum_posts',            ['post_id:s', 'topic_id:s', 'forum_id:s', 'user_id:s:n', 'post_remote_addr:a', 'post_text:s', 'post_parse:i', 'post_display_signature:b', 'post_created:t', 'post_edited:t:n', 'post_deleted:t:n']);
                             $tmpFiles[] = db_to_zip($archive, $userInfo, 'forum_topics',           ['topic_id:s', 'forum_id:s', 'user_id:s:n', 'topic_type:i', 'topic_title:s', 'topic_count_views:i', 'topic_created:t', 'topic_bumped:t', 'topic_deleted:t:n', 'topic_locked:t:n']);
-                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'forum_topics_redirects', ['topic_id:s', 'user_id:s:n', 'topic_redir_url:s', 'topic_redir_created:t']);
+                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'forum_topics_redirects', ['topic_id:s', 'user_id:s:n', 'redir_url:s', 'redir_created:t']);
                             $tmpFiles[] = db_to_zip($archive, $userInfo, 'forum_topics_track',     ['user_id:s', 'topic_id:s', 'forum_id:s', 'track_last_read:t']);
-                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'login_attempts',         ['user_id:s:n', 'attempt_success:b', 'attempt_ip:a', 'attempt_country:s', 'attempt_created:t', 'attempt_user_agent:s']);
+                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'login_attempts',         ['attempt_id:s', 'user_id:s:n', 'attempt_success:b', 'attempt_remote_addr:a', 'attempt_country:s', 'attempt_created:t', 'attempt_user_agent:s']);
                             $tmpFiles[] = db_to_zip($archive, $userInfo, 'messages',               ['msg_id:s', 'msg_owner_id:s', 'msg_author_id:s:n', 'msg_recipient_id:s:n', 'msg_reply_to:s:n', 'msg_title:s', 'msg_body:s', 'msg_parser:i', 'msg_created:t', 'msg_sent:t:n', 'msg_read:t:n', 'msg_deleted:t:n'], 'msg_owner_id');
-                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'news_posts',             ['post_id:s', 'category_id:s', 'user_id:s:n', 'comment_section_id:s:n', 'post_is_featured:b', 'post_title:s', 'post_text:s', 'post_scheduled:t', 'post_created:t', 'post_updated:t', 'post_deleted:t:n']);
+                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'news_posts',             ['post_id:s', 'category_id:s', 'user_id:s:n', 'comment_section_id:s:n', 'post_featured:b', 'post_title:s', 'post_text:s', 'post_scheduled:t', 'post_created:t', 'post_updated:t', 'post_deleted:t:n']);
                             $tmpFiles[] = db_to_zip($archive, $userInfo, 'perms',                  ['user_id:s:n', 'role_id:s:n', 'forum_id:s:n', 'perms_category:s', 'perms_allow:i', 'perms_deny:i']);
                             $tmpFiles[] = db_to_zip($archive, $userInfo, 'perms_calculated',       ['user_id:s:n', 'forum_id:s:n', 'perms_category:s', 'perms_calculated:i']);
                             $tmpFiles[] = db_to_zip($archive, $userInfo, 'profile_fields_values',  ['field_id:s', 'user_id:s', 'format_id:s', 'field_value:s']);
-                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'sessions',               ['session_id:s', 'user_id:s', 'session_key:n', 'session_ip:a', 'session_ip_last:a:n', 'session_user_agent:s', 'session_country:s', 'session_expires:t', 'session_expires_bump:b', 'session_created:t', 'session_active:t:n']);
-                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'users',                  ['user_id:s', 'username:s', 'password:n', 'email:s', 'register_ip:a', 'last_ip:a', 'user_super:b', 'user_country:s', 'user_colour:i:n', 'user_created:t', 'user_active:t:n', 'user_deleted:t:n', 'display_role:s:n', 'user_totp_key:n', 'user_about_content:s:n', 'user_about_parser:i', 'user_signature_content:s:n', 'user_signature_parser:i', 'user_birthdate:s:n', 'user_background_settings:i:n', 'user_title:s:n']);
+                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'sessions',               ['session_id:s', 'user_id:s', 'session_key:n', 'session_remote_addr_first:a', 'session_remote_addr_last:a:n', 'session_user_agent:s', 'session_country:s', 'session_expires:t', 'session_expires_bump:b', 'session_created:t', 'session_active:t:n']);
+                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'users',                  ['user_id:s', 'user_name:s', 'user_password:n', 'user_email:s', 'user_remote_addr_first:a', 'user_remote_addr_last:a', 'user_super:b', 'user_country:s', 'user_colour:i:n', 'user_created:t', 'user_active:t:n', 'user_deleted:t:n', 'user_display_role_id:s:n', 'user_totp_key:n', 'user_about_content:s:n', 'user_about_parser:i', 'user_signature_content:s:n', 'user_signature_parser:i', 'user_birthdate:s:n', 'user_background_settings:i:n', 'user_title:s:n']);
                             $tmpFiles[] = db_to_zip($archive, $userInfo, 'users_bans',             ['ban_id:s', 'user_id:s', 'mod_id:n', 'ban_severity:i', 'ban_reason_public:s', 'ban_reason_private:s', 'ban_created:t', 'ban_expires:t:n']);
-                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'users_password_resets',  ['user_id:s', 'reset_ip:a', 'reset_requested:t', 'verification_code:n']);
+                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'users_password_resets',  ['reset_id:s', 'user_id:s', 'reset_remote_addr:a', 'reset_requested:t', 'reset_code:n']);
                             $tmpFiles[] = db_to_zip($archive, $userInfo, 'users_warnings',         ['warn_id:s', 'user_id:s', 'mod_id:n', 'warn_body:s', 'warn_created:t']);
                             $tmpFiles[] = db_to_zip($archive, $userInfo, 'users_roles',            ['user_id:s', 'role_id:s']);
 
diff --git a/src/AuditLog/AuditLogData.php b/src/AuditLog/AuditLogData.php
index 59ae72a4..c1441e6c 100644
--- a/src/AuditLog/AuditLogData.php
+++ b/src/AuditLog/AuditLogData.php
@@ -31,7 +31,7 @@ class AuditLogData {
         }
         if($hasRemoteAddr) {
             $query .= (++$args > 1 ? ' AND' : ' WHERE');
-            $query .= ' log_ip = INET6_ATON(?)';
+            $query .= ' log_remote_addr = INET6_ATON(?)';
         }
 
         $stmt = $this->cache->get($query);
@@ -65,14 +65,14 @@ class AuditLogData {
         $hasPagination = $pagination !== null;
 
         $args = 0;
-        $query = 'SELECT user_id, log_action, log_params, UNIX_TIMESTAMP(log_created), INET6_NTOA(log_ip), log_country FROM msz_audit_log';
+        $query = 'SELECT user_id, log_action, log_params, UNIX_TIMESTAMP(log_created), INET6_NTOA(log_remote_addr), log_country FROM msz_audit_log';
         if($hasUserInfo) {
             ++$args;
             $query .= ' WHERE user_id = ?';
         }
         if($hasRemoteAddr) {
             $query .= (++$args > 1 ? ' AND' : ' WHERE');
-            $query .= ' log_ip = INET6_ATON(?)';
+            $query .= ' log_remote_addr = INET6_ATON(?)';
         }
         $query .= ' ORDER BY log_created DESC';
         if($hasPagination)
@@ -121,7 +121,7 @@ class AuditLogData {
 
         $params = json_encode($params);
 
-        $stmt = $this->cache->get('INSERT INTO msz_audit_log (user_id, log_action, log_params, log_ip, log_country) VALUES (?, ?, ?, INET6_ATON(?), UPPER(?))');
+        $stmt = $this->cache->get('INSERT INTO msz_audit_log (user_id, log_action, log_params, log_remote_addr, log_country) VALUES (?, ?, ?, INET6_ATON(?), UPPER(?))');
         $stmt->nextParameter($userInfo);
         $stmt->nextParameter($action);
         $stmt->nextParameter($params);
diff --git a/src/Auth/LoginAttemptInfo.php b/src/Auth/LoginAttemptInfo.php
index 52828229..c31a1f95 100644
--- a/src/Auth/LoginAttemptInfo.php
+++ b/src/Auth/LoginAttemptInfo.php
@@ -7,6 +7,7 @@ use Index\Db\DbResult;
 
 class LoginAttemptInfo {
     public function __construct(
+        public private(set) string $id,
         public private(set) ?string $userId,
         public private(set) bool $success,
         public private(set) string $remoteAddress,
@@ -18,13 +19,14 @@ class LoginAttemptInfo {
 
     public static function fromResult(DbResult $result): LoginAttemptInfo {
         return new LoginAttemptInfo(
-            userId: $result->getStringOrNull(0),
-            success: $result->getBoolean(1),
-            remoteAddress: $result->getString(2),
-            countryCode: $result->getString(3),
-            createdTime: $result->getInteger(4),
-            userAgentString: $result->getString(5),
-            clientInfoJson: $result->getString(6),
+            id: $result->getString(0),
+            userId: $result->getStringOrNull(1),
+            success: $result->getBoolean(2),
+            remoteAddress: $result->getString(3),
+            countryCode: $result->getString(4),
+            createdTime: $result->getInteger(5),
+            userAgentString: $result->getString(6),
+            clientInfoJson: $result->getString(7),
         );
     }
 
diff --git a/src/Auth/LoginAttemptsData.php b/src/Auth/LoginAttemptsData.php
index ce7a00f2..4b81e229 100644
--- a/src/Auth/LoginAttemptsData.php
+++ b/src/Auth/LoginAttemptsData.php
@@ -39,7 +39,7 @@ class LoginAttemptsData {
         if($hasUserInfo)
             $query .= sprintf(' %s user_id = ?', ++$args > 1 ? 'AND' : 'WHERE');
         if($hasRemoteAddr)
-            $query .= sprintf(' %s attempt_ip = INET6_ATON(?)', ++$args > 1 ? 'AND' : 'WHERE');
+            $query .= sprintf(' %s attempt_remote_addr = INET6_ATON(?)', ++$args > 1 ? 'AND' : 'WHERE');
         if($hasTimeRange)
             $query .= sprintf(' %s attempt_created > NOW() - INTERVAL ? SECOND', ++$args > 1 ? 'AND' : 'WHERE');
 
@@ -87,7 +87,7 @@ class LoginAttemptsData {
         $hasPagination = $pagination !== null;
 
         $args = 0;
-        $query = 'SELECT user_id, attempt_success, INET6_NTOA(attempt_ip), attempt_country, UNIX_TIMESTAMP(attempt_created), attempt_user_agent, attempt_client_info FROM msz_login_attempts';
+        $query = 'SELECT attempt_id, user_id, attempt_success, INET6_NTOA(attempt_remote_addr), attempt_country, UNIX_TIMESTAMP(attempt_created), attempt_user_agent, attempt_client_info FROM msz_login_attempts';
         if($hasSuccess) {
             ++$args;
             $query .= sprintf(' WHERE attempt_success %s 0', $success ? '<>' : '=');
@@ -95,7 +95,7 @@ class LoginAttemptsData {
         if($hasUserInfo)
             $query .= sprintf(' %s user_id = ?', ++$args > 1 ? 'AND' : 'WHERE');
         if($hasRemoteAddr)
-            $query .= sprintf(' %s attempt_ip = INET6_ATON(?)', ++$args > 1 ? 'AND' : 'WHERE');
+            $query .= sprintf(' %s attempt_remote_addr = INET6_ATON(?)', ++$args > 1 ? 'AND' : 'WHERE');
         if($hasTimeRange)
             $query .= sprintf(' %s attempt_created > NOW() - INTERVAL ? SECOND', ++$args > 1 ? 'AND' : 'WHERE');
         $query .= ' ORDER BY attempt_created DESC';
@@ -130,7 +130,7 @@ class LoginAttemptsData {
         $hasUserInfo = $userInfo !== null;
         $clientInfo = json_encode($clientInfo ?? ClientInfo::parse($userAgentString));
 
-        $stmt = $this->cache->get('INSERT INTO msz_login_attempts (user_id, attempt_success, attempt_ip, attempt_country, attempt_user_agent, attempt_client_info) VALUES (?, ?, INET6_ATON(?), ?, ?, ?)');
+        $stmt = $this->cache->get('INSERT INTO msz_login_attempts (user_id, attempt_success, attempt_remote_addr, attempt_country, attempt_user_agent, attempt_client_info) VALUES (?, ?, INET6_ATON(?), ?, ?, ?)');
         $stmt->nextParameter($userInfo);
         $stmt->nextParameter($success ? 1 : 0);
         $stmt->nextParameter($remoteAddr);
diff --git a/src/Auth/RecoveryTokenInfo.php b/src/Auth/RecoveryTokenInfo.php
index 890a0879..53de9e60 100644
--- a/src/Auth/RecoveryTokenInfo.php
+++ b/src/Auth/RecoveryTokenInfo.php
@@ -8,6 +8,7 @@ class RecoveryTokenInfo {
     public const LIFETIME = 60 * 60;
 
     public function __construct(
+        public private(set) string $id,
         public private(set) string $userId,
         public private(set) string $remoteAddress,
         public private(set) int $createdTime,
@@ -16,10 +17,11 @@ class RecoveryTokenInfo {
 
     public static function fromResult(DbResult $result): RecoveryTokenInfo {
         return new RecoveryTokenInfo(
-            userId: $result->getString(0),
-            remoteAddress: $result->getString(1),
-            createdTime: $result->getInteger(2),
-            code: $result->getStringOrNull(3),
+            id: $result->getString(0),
+            userId: $result->getString(1),
+            remoteAddress: $result->getString(2),
+            createdTime: $result->getInteger(3),
+            code: $result->getStringOrNull(4),
         );
     }
 
diff --git a/src/Auth/RecoveryTokensData.php b/src/Auth/RecoveryTokensData.php
index e23e77a2..b183e9a2 100644
--- a/src/Auth/RecoveryTokensData.php
+++ b/src/Auth/RecoveryTokensData.php
@@ -41,17 +41,17 @@ class RecoveryTokensData {
             throw new InvalidArgumentException('You may not specify $isUnused and $verifyCode at the same time.');
 
         $args = 0;
-        $query = 'SELECT user_id, INET6_NTOA(reset_ip), UNIX_TIMESTAMP(reset_requested), verification_code FROM msz_users_password_resets';
+        $query = 'SELECT reset_id, user_id, INET6_NTOA(reset_remote_addr), UNIX_TIMESTAMP(reset_requested), reset_code FROM msz_users_password_resets';
         if($hasUserInfo) {
             ++$args;
             $query .= ' WHERE user_id = ?';
         }
         if($hasRemoteAddr)
-            $query .= sprintf(' %s reset_ip = INET6_ATON(?)', ++$args > 1 ? 'AND' : 'WHERE');
+            $query .= sprintf(' %s reset_remote_addr = INET6_ATON(?)', ++$args > 1 ? 'AND' : 'WHERE');
         if($hasIsUnused)
-            $query .= sprintf(' %s verification_code %s NULL', ++$args > 1 ? 'AND' : 'WHERE', $isUnused ? 'IS NOT' : 'IS');
+            $query .= sprintf(' %s reset_code %s NULL', ++$args > 1 ? 'AND' : 'WHERE', $isUnused ? 'IS NOT' : 'IS');
         elseif($hasVerifyCode)
-            $query .= sprintf(' %s verification_code = ?', ++$args > 1 ? 'AND' : 'WHERE');
+            $query .= sprintf(' %s reset_code = ?', ++$args > 1 ? 'AND' : 'WHERE');
 
         $stmt = $this->cache->get($query);
         if($hasUserInfo)
@@ -78,7 +78,7 @@ class RecoveryTokensData {
 
         $verifyCode = self::generateCode();
 
-        $stmt = $this->cache->get('INSERT INTO msz_users_password_resets (user_id, reset_ip, verification_code) VALUES (?, INET6_ATON(?), ?)');
+        $stmt = $this->cache->get('INSERT INTO msz_users_password_resets (user_id, reset_remote_addr, reset_code) VALUES (?, INET6_ATON(?), ?)');
         $stmt->nextParameter($userInfo);
         $stmt->nextParameter($remoteAddr);
         $stmt->nextParameter($verifyCode);
@@ -91,7 +91,7 @@ class RecoveryTokensData {
         if(!$tokenInfo->hasCode)
             return;
 
-        $stmt = $this->cache->get('UPDATE msz_users_password_resets SET verification_code = NULL WHERE verification_code = ? AND user_id = ?');
+        $stmt = $this->cache->get('UPDATE msz_users_password_resets SET reset_code = NULL WHERE reset_code = ? AND user_id = ?');
         $stmt->nextParameter($tokenInfo->code);
         $stmt->nextParameter($tokenInfo->userId);
         $stmt->execute();
diff --git a/src/Auth/SessionsData.php b/src/Auth/SessionsData.php
index e3590b41..47793c6a 100644
--- a/src/Auth/SessionsData.php
+++ b/src/Auth/SessionsData.php
@@ -63,7 +63,7 @@ class SessionsData {
         $hasPagination = $pagination !== null;
 
         //$args = 0;
-        $query = 'SELECT session_id, user_id, session_key, INET6_NTOA(session_ip), INET6_NTOA(session_ip_last), session_user_agent, session_client_info, session_country, UNIX_TIMESTAMP(session_expires), session_expires_bump, UNIX_TIMESTAMP(session_created), UNIX_TIMESTAMP(session_active) FROM msz_sessions';
+        $query = 'SELECT session_id, user_id, session_key, INET6_NTOA(session_remote_addr_first), INET6_NTOA(session_remote_addr_last), session_user_agent, session_client_info, session_country, UNIX_TIMESTAMP(session_expires), session_expires_bump, UNIX_TIMESTAMP(session_created), UNIX_TIMESTAMP(session_active) FROM msz_sessions';
         if($hasUserInfo) {
             //++$args;
             $query .= ' WHERE user_id = ?';
@@ -95,7 +95,7 @@ class SessionsData {
         $hasSessionToken = $sessionToken !== null;
         $value = null;
 
-        $query = 'SELECT session_id, user_id, session_key, INET6_NTOA(session_ip), INET6_NTOA(session_ip_last), session_user_agent, session_client_info, session_country, UNIX_TIMESTAMP(session_expires), session_expires_bump, UNIX_TIMESTAMP(session_created), UNIX_TIMESTAMP(session_active) FROM msz_sessions';
+        $query = 'SELECT session_id, user_id, session_key, INET6_NTOA(session_remote_addr_first), INET6_NTOA(session_remote_addr_last), session_user_agent, session_client_info, session_country, UNIX_TIMESTAMP(session_expires), session_expires_bump, UNIX_TIMESTAMP(session_created), UNIX_TIMESTAMP(session_active) FROM msz_sessions';
         if($hasSessionId) {
             $query .= ' WHERE session_id = ?';
             $value = $sessionId;
@@ -128,7 +128,7 @@ class SessionsData {
         $sessionToken = self::generateToken();
         $clientInfo = json_encode($clientInfo ?? ClientInfo::parse($userAgentString));
 
-        $stmt = $this->cache->get('INSERT INTO msz_sessions (user_id, session_key, session_ip, session_user_agent, session_client_info, session_country, session_expires) VALUES (?, ?, INET6_ATON(?), ?, ?, ?, NOW() + INTERVAL 1 MONTH)');
+        $stmt = $this->cache->get('INSERT INTO msz_sessions (user_id, session_key, session_remote_addr_first, session_user_agent, session_client_info, session_country, session_expires) VALUES (?, ?, INET6_ATON(?), ?, ?, ?, NOW() + INTERVAL 1 MONTH)');
         $stmt->nextParameter($userInfo);
         $stmt->nextParameter($sessionToken);
         $stmt->nextParameter($remoteAddr);
@@ -252,7 +252,7 @@ class SessionsData {
         $hasSessionToken = $sessionToken !== null;
         $value = null;
 
-        $query = 'UPDATE msz_sessions SET session_ip_last = COALESCE(INET6_ATON(?), session_ip_last), session_active = NOW(), session_expires = IF(session_expires_bump, NOW() + INTERVAL 1 MONTH, session_expires)';
+        $query = 'UPDATE msz_sessions SET session_remote_addr_last = COALESCE(INET6_ATON(?), session_remote_addr_last), session_active = NOW(), session_expires = IF(session_expires_bump, NOW() + INTERVAL 1 MONTH, session_expires)';
         if($hasSessionInfo) {
             $query .= ' WHERE session_id = ?';
             $value = $sessionInfo;
diff --git a/src/Comments/CommentsData.php b/src/Comments/CommentsData.php
index a98fb6a6..68a6087f 100644
--- a/src/Comments/CommentsData.php
+++ b/src/Comments/CommentsData.php
@@ -24,7 +24,7 @@ class CommentsData {
 
         $query = 'SELECT COUNT(*) FROM msz_comments_categories';
         if($hasOwner)
-            $query .= ' WHERE owner_id = ?';
+            $query .= ' WHERE user_id = ?';
 
         $stmt = $this->cache->get($query);
         $stmt->nextParameter($owner);
@@ -50,9 +50,9 @@ class CommentsData {
         $hasOwner = $owner !== null;
         $hasPagination = $pagination !== null;
 
-        $query = 'SELECT category_id, category_name, owner_id, UNIX_TIMESTAMP(category_created), UNIX_TIMESTAMP(category_locked), (SELECT COUNT(*) FROM msz_comments_posts AS cp WHERE cp.category_id = cc.category_id AND comment_deleted IS NULL) AS `category_comments` FROM msz_comments_categories AS cc';
+        $query = 'SELECT category_id, category_name, user_id, UNIX_TIMESTAMP(category_created), UNIX_TIMESTAMP(category_locked), (SELECT COUNT(*) FROM msz_comments_posts AS cp WHERE cp.category_id = cc.category_id AND comment_deleted IS NULL) AS `category_comments` FROM msz_comments_categories AS cc';
         if($hasOwner)
-            $query .= ' WHERE owner_id = ?';
+            $query .= ' WHERE user_id = ?';
         $query .= ' ORDER BY category_id ASC'; // should order by date but no index on
         if($hasPagination)
             $query .= ' LIMIT ? RANGE ?';
@@ -84,7 +84,7 @@ class CommentsData {
         if(($hasCategoryId && ($hasName || $hasPostInfo)) || ($hasName && ($hasCategoryId || $hasPostInfo)) || ($hasPostInfo && ($hasCategoryId || $hasName)))
             throw new InvalidArgumentException('Only one of the arguments may be specified.');
 
-        $query = 'SELECT category_id, category_name, owner_id, UNIX_TIMESTAMP(category_created), UNIX_TIMESTAMP(category_locked), (SELECT COUNT(*) FROM msz_comments_posts AS cp WHERE cp.category_id = cc.category_id AND comment_deleted IS NULL) AS category_comments FROM msz_comments_categories AS cc';
+        $query = 'SELECT category_id, category_name, user_id, UNIX_TIMESTAMP(category_created), UNIX_TIMESTAMP(category_locked), (SELECT COUNT(*) FROM msz_comments_posts AS cp WHERE cp.category_id = cc.category_id AND comment_deleted IS NULL) AS category_comments FROM msz_comments_categories AS cc';
         $value = null;
         if($hasCategoryId) {
             $query .= ' WHERE category_id = ?';
@@ -143,7 +143,7 @@ class CommentsData {
         if(empty($name))
             throw new InvalidArgumentException('$name may not be empty.');
 
-        $stmt = $this->cache->get('INSERT INTO msz_comments_categories (category_name, owner_id) VALUES (?, ?)');
+        $stmt = $this->cache->get('INSERT INTO msz_comments_categories (category_name, user_id) VALUES (?, ?)');
         $stmt->nextParameter($name);
         $stmt->nextParameter($owner);
         $stmt->execute();
@@ -177,7 +177,7 @@ class CommentsData {
                 throw new InvalidArgumentException('$name may not be empty.');
         }
 
-        $stmt = $this->cache->get('UPDATE msz_comments_categories SET category_name = COALESCE(?, category_name), owner_id = IF(?, ?, owner_id) WHERE category_id = ?');
+        $stmt = $this->cache->get('UPDATE msz_comments_categories SET category_name = COALESCE(?, category_name), user_id = IF(?, ?, user_id) WHERE category_id = ?');
         $stmt->nextParameter($name);
         $stmt->nextParameter($updateOwner ? 1 : 0);
         $stmt->nextParameter($owner ? 1 : 0);
diff --git a/src/Emoticons/EmotesData.php b/src/Emoticons/EmotesData.php
index 7fa84ca2..276f38eb 100644
--- a/src/Emoticons/EmotesData.php
+++ b/src/Emoticons/EmotesData.php
@@ -9,7 +9,7 @@ class EmotesData {
     private const EMOTE_ORDER = [
         'order' => 'emote_order',
         'id' => 'emote_id',
-        'rank' => 'emote_hierarchy',
+        'rank' => 'emote_rank',
     ];
 
     private DbStatementCache $cache;
@@ -19,7 +19,7 @@ class EmotesData {
     }
 
     public function getEmote(string $emoteId): EmoteInfo {
-        $stmt = $this->cache->get('SELECT emote_id, emote_order, emote_hierarchy, emote_url FROM msz_emoticons WHERE emote_id = ?');
+        $stmt = $this->cache->get('SELECT emote_id, emote_order, emote_rank, emote_url FROM msz_emoticons WHERE emote_id = ?');
         $stmt->nextParameter($emoteId);
         $stmt->execute();
 
@@ -49,9 +49,9 @@ class EmotesData {
         $hasOrderBy = $orderBy !== null;
         $hasReverse = $reverse !== null;
 
-        $query = 'SELECT emote_id, emote_order, emote_hierarchy, emote_url FROM msz_emoticons';
+        $query = 'SELECT emote_id, emote_order, emote_rank, emote_url FROM msz_emoticons';
         if($hasMinRank)
-            $query .= ' WHERE emote_hierarchy <= ?';
+            $query .= ' WHERE emote_rank <= ?';
         if($hasOrderBy) {
             if(!array_key_exists($orderBy, self::EMOTE_ORDER))
                 throw new InvalidArgumentException('Invalid $orderBy specified.');
@@ -99,7 +99,7 @@ class EmotesData {
         if($check !== '')
             throw new InvalidArgumentException('$url is not correctly formatted: ' . $check);
 
-        $stmt = $this->cache->get('INSERT INTO msz_emoticons (emote_url, emote_hierarchy, emote_order) SELECT ?, ?, COALESCE(?, (SELECT FLOOR(MAX(emote_order) / 10) * 10 + 10 FROM msz_emoticons), 10)');
+        $stmt = $this->cache->get('INSERT INTO msz_emoticons (emote_url, emote_rank, emote_order) SELECT ?, ?, COALESCE(?, (SELECT FLOOR(MAX(emote_order) / 10) * 10 + 10 FROM msz_emoticons), 10)');
         $stmt->nextParameter($url);
         $stmt->nextParameter($minRank);
         $stmt->nextParameter($order);
@@ -132,7 +132,7 @@ class EmotesData {
         if($infoOrId instanceof EmoteInfo)
             $infoOrId = $infoOrId->id;
 
-        $stmt = $this->cache->get('UPDATE msz_emoticons SET emote_order = COALESCE(?, emote_order), emote_hierarchy = COALESCE(?, emote_hierarchy), emote_url = COALESCE(?, emote_url) WHERE emote_id = ?');
+        $stmt = $this->cache->get('UPDATE msz_emoticons SET emote_order = COALESCE(?, emote_order), emote_rank = COALESCE(?, emote_rank), emote_url = COALESCE(?, emote_url) WHERE emote_id = ?');
         $stmt->nextParameter($order);
         $stmt->nextParameter($minRank);
         $stmt->nextParameter($url);
diff --git a/src/Forum/ForumPostsData.php b/src/Forum/ForumPostsData.php
index af453f54..82b4285c 100644
--- a/src/Forum/ForumPostsData.php
+++ b/src/Forum/ForumPostsData.php
@@ -131,7 +131,7 @@ class ForumPostsData {
         $hasPagination = $pagination !== null;
 
         $args = 0;
-        $query = 'SELECT post_id, topic_id, forum_id, user_id, INET6_NTOA(post_ip), post_text, post_parse, post_display_signature, UNIX_TIMESTAMP(post_created), UNIX_TIMESTAMP(post_edited), UNIX_TIMESTAMP(post_deleted) FROM msz_forum_posts';
+        $query = 'SELECT post_id, topic_id, forum_id, user_id, INET6_NTOA(post_remote_addr), post_text, post_parse, post_display_signature, UNIX_TIMESTAMP(post_created), UNIX_TIMESTAMP(post_edited), UNIX_TIMESTAMP(post_deleted) FROM msz_forum_posts';
         if($hasCategoryInfo) {
             ++$args;
             if(is_array($categoryInfo))
@@ -207,7 +207,7 @@ class ForumPostsData {
             throw new InvalidArgumentException('At least one of the four first arguments must be specified.');
 
         $values = [];
-        $query = 'SELECT post_id, topic_id, forum_id, user_id, INET6_NTOA(post_ip), post_text, post_parse, post_display_signature, UNIX_TIMESTAMP(post_created), UNIX_TIMESTAMP(post_edited), UNIX_TIMESTAMP(post_deleted) FROM msz_forum_posts';
+        $query = 'SELECT post_id, topic_id, forum_id, user_id, INET6_NTOA(post_remote_addr), post_text, post_parse, post_display_signature, UNIX_TIMESTAMP(post_created), UNIX_TIMESTAMP(post_edited), UNIX_TIMESTAMP(post_deleted) FROM msz_forum_posts';
         if($hasPostId) {
             $query .= ' WHERE post_id = ?';
             $values[] = $postId;
@@ -281,7 +281,7 @@ class ForumPostsData {
         if($userInfo instanceof UserInfo)
             $userInfo = $userInfo->id;
 
-        $stmt = $this->cache->get('INSERT INTO msz_forum_posts (topic_id, forum_id, user_id, post_ip, post_text, post_parse, post_display_signature) VALUES (?, ?, ?, INET6_ATON(?), ?, ?, ?)');
+        $stmt = $this->cache->get('INSERT INTO msz_forum_posts (topic_id, forum_id, user_id, post_remote_addr, post_text, post_parse, post_display_signature) VALUES (?, ?, ?, INET6_ATON(?), ?, ?, ?)');
         $stmt->nextParameter($topicInfo);
         $stmt->nextParameter($categoryInfo);
         $stmt->nextParameter($userInfo);
@@ -309,7 +309,7 @@ class ForumPostsData {
         $values = [];
 
         if($remoteAddr !== null) {
-            $fields[] = 'post_ip = INET6_ATON(?)';
+            $fields[] = 'post_remote_addr = INET6_ATON(?)';
             $values[] = $remoteAddr;
         }
 
diff --git a/src/Forum/ForumTopicRedirectsData.php b/src/Forum/ForumTopicRedirectsData.php
index f9e28394..ab3d9186 100644
--- a/src/Forum/ForumTopicRedirectsData.php
+++ b/src/Forum/ForumTopicRedirectsData.php
@@ -45,7 +45,7 @@ class ForumTopicRedirectsData {
         $hasUserInfo = $userInfo !== null;
         $hasPagination = $pagination !== null;
 
-        $query = 'SELECT topic_id, user_id, topic_redir_url, UNIX_TIMESTAMP(topic_redir_created) FROM msz_forum_topics_redirects';
+        $query = 'SELECT topic_id, user_id, redir_url, UNIX_TIMESTAMP(redir_created) FROM msz_forum_topics_redirects';
         if($hasUserInfo)
             $query .= ' WHERE user_id = ?';
         if($hasPagination)
@@ -80,7 +80,7 @@ class ForumTopicRedirectsData {
         if($topicInfo instanceof ForumTopicInfo)
             $topicInfo = $topicInfo->id;
 
-        $stmt = $this->cache->get('SELECT topic_id, user_id, topic_redir_url, UNIX_TIMESTAMP(topic_redir_created) FROM msz_forum_topics_redirects WHERE topic_id = ?');
+        $stmt = $this->cache->get('SELECT topic_id, user_id, redir_url, UNIX_TIMESTAMP(redir_created) FROM msz_forum_topics_redirects WHERE topic_id = ?');
         $stmt->nextParameter($topicInfo);
         $stmt->execute();
 
@@ -101,7 +101,7 @@ class ForumTopicRedirectsData {
         if($userInfo instanceof UserInfo)
             $userInfo = $userInfo->id;
 
-        $stmt = $this->cache->get('INSERT INTO msz_forum_topics_redirects (topic_id, user_id, topic_redir_url) VALUES (?, ?, ?)');
+        $stmt = $this->cache->get('INSERT INTO msz_forum_topics_redirects (topic_id, user_id, redir_url) VALUES (?, ?, ?)');
         $stmt->nextParameter($topicInfo);
         $stmt->nextParameter($userInfo);
         $stmt->nextParameter($linkTarget);
diff --git a/src/News/NewsData.php b/src/News/NewsData.php
index 65e27df7..4294c7ab 100644
--- a/src/News/NewsData.php
+++ b/src/News/NewsData.php
@@ -23,7 +23,7 @@ class NewsData {
 
         $query = 'SELECT COUNT(*) FROM msz_news_categories';
         if($hasHidden)
-            $query .= sprintf(' WHERE category_is_hidden %s 0', $hidden ? '<>' : '=');
+            $query .= sprintf(' WHERE category_hidden %s 0', $hidden ? '<>' : '=');
 
         $stmt = $this->cache->get($query);
         $stmt->execute();
@@ -40,9 +40,9 @@ class NewsData {
         $hasHidden = $hidden !== null;
         $hasPagination = $pagination !== null;
 
-        $query = 'SELECT category_id, category_name, category_description, category_is_hidden, UNIX_TIMESTAMP(category_created), (SELECT COUNT(*) FROM msz_news_posts AS np WHERE np.category_id = nc.category_id) AS category_posts_count FROM msz_news_categories AS nc';
+        $query = 'SELECT category_id, category_name, category_description, category_hidden, UNIX_TIMESTAMP(category_created), (SELECT COUNT(*) FROM msz_news_posts AS np WHERE np.category_id = nc.category_id) AS category_posts_count FROM msz_news_categories AS nc';
         if($hasHidden)
-            $query .= sprintf(' WHERE category_is_hidden %s 0', $hidden ? '<>' : '=');
+            $query .= sprintf(' WHERE category_hidden %s 0', $hidden ? '<>' : '=');
         $query .= ' ORDER BY category_created ASC';
         if($hasPagination)
             $query .= ' LIMIT ? OFFSET ?';
@@ -69,7 +69,7 @@ class NewsData {
             throw new InvalidArgumentException('Only one argument may be specified.');
 
         $value = null;
-        $query = 'SELECT category_id, category_name, category_description, category_is_hidden, UNIX_TIMESTAMP(category_created) FROM msz_news_categories';
+        $query = 'SELECT category_id, category_name, category_description, category_hidden, UNIX_TIMESTAMP(category_created) FROM msz_news_categories';
 
         if($hasCategoryId) {
             $query .= ' WHERE category_id = ?';
@@ -109,7 +109,7 @@ class NewsData {
         if(empty($description))
             throw new InvalidArgumentException('$description may not be empty');
 
-        $stmt = $this->cache->get('INSERT INTO msz_news_categories (category_name, category_description, category_is_hidden) VALUES (?, ?, ?)');
+        $stmt = $this->cache->get('INSERT INTO msz_news_categories (category_name, category_description, category_hidden) VALUES (?, ?, ?)');
         $stmt->nextParameter($name);
         $stmt->nextParameter($description);
         $stmt->nextParameter($hidden ? 1 : 0);
@@ -150,7 +150,7 @@ class NewsData {
 
         $hasHidden = $hidden !== null;
 
-        $stmt = $this->cache->get('UPDATE msz_news_categories SET category_name = COALESCE(?, category_name), category_description = COALESCE(?, category_description), category_is_hidden = IF(?, ?, category_is_hidden) WHERE category_id = ?');
+        $stmt = $this->cache->get('UPDATE msz_news_categories SET category_name = COALESCE(?, category_name), category_description = COALESCE(?, category_description), category_hidden = IF(?, ?, category_hidden) WHERE category_id = ?');
         $stmt->nextParameter($name);
         $stmt->nextParameter($description);
         $stmt->nextParameter($hasHidden ? 1 : 0);
@@ -178,7 +178,7 @@ class NewsData {
         }
         if($onlyFeatured) {
             $query .= (++$args > 1 ? ' AND' : ' WHERE');
-            $query .= ' post_is_featured = 1';
+            $query .= ' post_featured = 1';
         }
         if(!$includeScheduled) {
             $query .= (++$args > 1 ? ' AND' : ' WHERE');
@@ -220,7 +220,7 @@ class NewsData {
         $hasPagination = $pagination !== null;
 
         $args = 0;
-        $query = 'SELECT post_id, category_id, user_id, comment_section_id, post_is_featured, post_title, post_text, UNIX_TIMESTAMP(post_scheduled), UNIX_TIMESTAMP(post_created), UNIX_TIMESTAMP(post_updated), UNIX_TIMESTAMP(post_deleted) FROM msz_news_posts';
+        $query = 'SELECT post_id, category_id, user_id, comment_section_id, post_featured, post_title, post_text, UNIX_TIMESTAMP(post_scheduled), UNIX_TIMESTAMP(post_created), UNIX_TIMESTAMP(post_updated), UNIX_TIMESTAMP(post_deleted) FROM msz_news_posts';
         if($hasCategoryInfo) {
             ++$args;
             $query .= ' WHERE category_id = ?';
@@ -231,7 +231,7 @@ class NewsData {
         }
         if($onlyFeatured) {
             $query .= (++$args > 1 ? ' AND' : ' WHERE');
-            $query .= ' post_is_featured = 1';
+            $query .= ' post_featured = 1';
         }
         if(!$includeScheduled) {
             $query .= (++$args > 1 ? ' AND' : ' WHERE');
@@ -259,7 +259,7 @@ class NewsData {
     }
 
     public function getPost(string $postId): NewsPostInfo {
-        $stmt = $this->cache->get('SELECT post_id, category_id, user_id, comment_section_id, post_is_featured, post_title, post_text, UNIX_TIMESTAMP(post_scheduled), UNIX_TIMESTAMP(post_created), UNIX_TIMESTAMP(post_updated), UNIX_TIMESTAMP(post_deleted) FROM msz_news_posts WHERE post_id = ?');
+        $stmt = $this->cache->get('SELECT post_id, category_id, user_id, comment_section_id, post_featured, post_title, post_text, UNIX_TIMESTAMP(post_scheduled), UNIX_TIMESTAMP(post_created), UNIX_TIMESTAMP(post_updated), UNIX_TIMESTAMP(post_deleted) FROM msz_news_posts WHERE post_id = ?');
         $stmt->nextParameter($postId);
         $stmt->execute();
 
@@ -293,7 +293,7 @@ class NewsData {
         if(empty($body))
             throw new InvalidArgumentException('$body may not be empty');
 
-        $stmt = $this->cache->get('INSERT INTO msz_news_posts (category_id, user_id, post_is_featured, post_title, post_text, post_scheduled) VALUES (?, ?, ?, ?, ?, ?)');
+        $stmt = $this->cache->get('INSERT INTO msz_news_posts (category_id, user_id, post_featured, post_title, post_text, post_scheduled) VALUES (?, ?, ?, ?, ?, ?)');
         $stmt->nextParameter($categoryInfo);
         $stmt->nextParameter($userInfo);
         $stmt->nextParameter($featured ? 1 : 0);
@@ -366,7 +366,7 @@ class NewsData {
 
         $hasFeatured = $featured !== null;
 
-        $stmt = $this->cache->get('UPDATE msz_news_posts SET category_id = COALESCE(?, category_id), user_id = IF(?, ?, user_id), post_is_featured = IF(?, ?, post_is_featured), post_title = COALESCE(?, post_title), post_text = COALESCE(?, post_text), post_scheduled = COALESCE(?, post_scheduled) WHERE post_id = ?');
+        $stmt = $this->cache->get('UPDATE msz_news_posts SET category_id = COALESCE(?, category_id), user_id = IF(?, ?, user_id), post_featured = IF(?, ?, post_featured), post_title = COALESCE(?, post_title), post_text = COALESCE(?, post_text), post_scheduled = COALESCE(?, post_scheduled) WHERE post_id = ?');
         $stmt->nextParameter($categoryInfo);
         $stmt->nextParameter($updateUserInfo ? 1 : 0);
         $stmt->nextParameter($userInfo);
diff --git a/src/Users/RolesData.php b/src/Users/RolesData.php
index d559aff4..2f693bdd 100644
--- a/src/Users/RolesData.php
+++ b/src/Users/RolesData.php
@@ -65,7 +65,7 @@ class RolesData {
         $hasPagination = $pagination !== null;
 
         $args = 0;
-        $query = 'SELECT role_id, role_string, role_hierarchy, role_name, role_title, role_description, role_hidden, role_can_leave, role_colour, UNIX_TIMESTAMP(role_created) FROM msz_roles';
+        $query = 'SELECT role_id, role_string, role_rank, role_name, role_title, role_description, role_hidden, role_can_leave, role_colour, UNIX_TIMESTAMP(role_created) FROM msz_roles';
         if($hasUserInfo) {
             ++$args;
             $query .= ' WHERE role_id IN (SELECT role_id FROM msz_users_roles WHERE user_id = ?)';
@@ -75,7 +75,7 @@ class RolesData {
         if($hasString !== null)
             $query .= sprintf(' %s role_string %s NULL', ++$args > 1 ? 'AND' : 'WHERE', $hasString ? 'IS NOT' : 'IS');
         if($orderByRank)
-            $query .= ' ORDER BY role_hierarchy DESC';
+            $query .= ' ORDER BY role_rank DESC';
         if($hasPagination)
             $query .= ' LIMIT ? OFFSET ?';
 
@@ -90,7 +90,7 @@ class RolesData {
     }
 
     public function getRole(string $roleId): RoleInfo {
-        $stmt = $this->cache->get('SELECT role_id, role_string, role_hierarchy, role_name, role_title, role_description, role_hidden, role_can_leave, role_colour, UNIX_TIMESTAMP(role_created) FROM msz_roles WHERE role_id = ?');
+        $stmt = $this->cache->get('SELECT role_id, role_string, role_rank, role_name, role_title, role_description, role_hidden, role_can_leave, role_colour, UNIX_TIMESTAMP(role_created) FROM msz_roles WHERE role_id = ?');
         $stmt->nextParameter($roleId);
         $stmt->execute();
 
@@ -120,7 +120,7 @@ class RolesData {
         if($title === '') $title = null;
         if($description === '') $description = null;
 
-        $stmt = $this->cache->get('INSERT INTO msz_roles (role_string, role_hierarchy, role_name, role_title, role_description, role_hidden, role_can_leave, role_colour) VALUES (?, ?, ?, ?, ?, ?, ?, ?)');
+        $stmt = $this->cache->get('INSERT INTO msz_roles (role_string, role_rank, role_name, role_title, role_description, role_hidden, role_can_leave, role_colour) VALUES (?, ?, ?, ?, ?, ?, ?, ?)');
         $stmt->nextParameter($string);
         $stmt->nextParameter($rank);
         $stmt->nextParameter($name);
@@ -189,7 +189,7 @@ class RolesData {
         if($title === '') $title = null;
         if($description === '') $description = null;
 
-        $stmt = $this->cache->get('UPDATE msz_roles SET role_string = IF(?, ?, role_string), role_hierarchy = COALESCE(?, role_hierarchy), role_name = COALESCE(?, role_name), role_title = IF(?, ?, role_title), role_description = IF(?, ?, role_description), role_hidden = IF(?, ?, role_hidden), role_can_leave = IF(?, ?, role_can_leave), role_colour = IF(?, ?, role_colour) WHERE role_id = ?');
+        $stmt = $this->cache->get('UPDATE msz_roles SET role_string = IF(?, ?, role_string), role_rank = COALESCE(?, role_rank), role_name = COALESCE(?, role_name), role_title = IF(?, ?, role_title), role_description = IF(?, ?, role_description), role_hidden = IF(?, ?, role_hidden), role_can_leave = IF(?, ?, role_can_leave), role_colour = IF(?, ?, role_colour) WHERE role_id = ?');
         $stmt->nextParameter($applyString ? 1 : 0);
         $stmt->nextParameter($string);
         $stmt->nextParameter($rank);
diff --git a/src/Users/UsersData.php b/src/Users/UsersData.php
index 0a1de565..b2badfb3 100644
--- a/src/Users/UsersData.php
+++ b/src/Users/UsersData.php
@@ -88,7 +88,7 @@ class UsersData {
 
     private const GET_USERS_SORT = [
         'id' => ['user_id', false],
-        'name' => ['username', false],
+        'name' => ['user_name', false],
         'country' => ['user_country', false],
         'created' => ['user_created', true],
         'active' => ['user_active', true],
@@ -162,7 +162,19 @@ class UsersData {
         }
 
         $args = 0;
-        $query = 'SELECT user_id, username, password, email, INET6_NTOA(register_ip), INET6_NTOA(last_ip), user_super, user_country, user_colour, UNIX_TIMESTAMP(user_created), UNIX_TIMESTAMP(user_active), UNIX_TIMESTAMP(user_deleted), display_role, user_totp_key, user_about_content, user_about_parser, user_signature_content, user_signature_parser, user_birthdate, user_background_settings, user_title FROM msz_users';
+        $query = <<<SQL
+            SELECT user_id, user_name, user_password, user_email,
+                INET6_NTOA(user_remote_addr_first), INET6_NTOA(user_remote_addr_last),
+                user_super, user_country, user_colour,
+                UNIX_TIMESTAMP(user_created),
+                UNIX_TIMESTAMP(user_active),
+                UNIX_TIMESTAMP(user_deleted),
+                user_display_role_id, user_totp_key, user_about_content,
+                user_about_parser, user_signature_content,
+                user_signature_parser, user_birthdate,
+                user_background_settings, user_title
+            FROM msz_users
+        SQL;
         if($hasRoleInfo) {
             ++$args;
             $query .= ' WHERE user_id IN (SELECT user_id FROM msz_users_roles WHERE role_id = ?)';
@@ -178,7 +190,7 @@ class UsersData {
         if($hasBirthdate)
             $query .= sprintf(' %s user_birthdate LIKE ?', ++$args > 1 ? 'AND' : 'WHERE');
         if($hasSearchQuery)
-            $query .= sprintf(' %s username LIKE CONCAT("%%", ?, "%%")', ++$args > 1 ? 'AND' : 'WHERE');
+            $query .= sprintf(' %s user_name LIKE CONCAT("%%", ?, "%%")', ++$args > 1 ? 'AND' : 'WHERE');
         if($hasOrderBy) {
             $query .= sprintf(' ORDER BY %s', $orderBy[0]);
             if($orderBy[1] !== null)
@@ -248,15 +260,27 @@ class UsersData {
             throw new InvalidArgumentException('$select flagset is invalid.');
 
         $args = 0;
-        $query = 'SELECT user_id, username, password, email, INET6_NTOA(register_ip), INET6_NTOA(last_ip), user_super, user_country, user_colour, UNIX_TIMESTAMP(user_created), UNIX_TIMESTAMP(user_active), UNIX_TIMESTAMP(user_deleted), display_role, user_totp_key, user_about_content, user_about_parser, user_signature_content, user_signature_parser, user_birthdate, user_background_settings, user_title FROM msz_users';
+        $query = <<<SQL
+            SELECT user_id, user_name, user_password, user_email,
+                INET6_NTOA(user_remote_addr_first), INET6_NTOA(user_remote_addr_last),
+                user_super, user_country, user_colour,
+                UNIX_TIMESTAMP(user_created),
+                UNIX_TIMESTAMP(user_active),
+                UNIX_TIMESTAMP(user_deleted),
+                user_display_role_id, user_totp_key, user_about_content,
+                user_about_parser, user_signature_content,
+                user_signature_parser, user_birthdate,
+                user_background_settings, user_title
+            FROM msz_users
+        SQL;
         if($selectId) {
             ++$args;
             $query .= ' WHERE user_id = ?';
         }
         if($selectName)
-            $query .= sprintf(' %s username = ?', ++$args > 1 ? 'OR' : 'WHERE');
+            $query .= sprintf(' %s user_name = ?', ++$args > 1 ? 'OR' : 'WHERE');
         if($selectMail)
-            $query .= sprintf(' %s email = ?', ++$args > 1 ? 'OR' : 'WHERE');
+            $query .= sprintf(' %s user_email = ?', ++$args > 1 ? 'OR' : 'WHERE');
 
         $stmt = $this->cache->get($query);
         if($selectId)
@@ -294,7 +318,7 @@ class UsersData {
         if(self::validateEMailAddress($email, true) !== '')
             throw new InvalidArgumentException('$email is not a valid e-mail address.');
 
-        $stmt = $this->cache->get('INSERT INTO msz_users (username, password, email, register_ip, last_ip, user_country, display_role) VALUES (?, ?, ?, INET6_ATON(?), INET6_ATON(?), ?, ?)');
+        $stmt = $this->cache->get('INSERT INTO msz_users (user_name, user_password, user_email, user_remote_addr_first, user_remote_addr_last, user_country, user_display_role_id) VALUES (?, ?, ?, INET6_ATON(?), INET6_ATON(?), ?, ?)');
         $stmt->nextParameter($name);
         $stmt->nextParameter($password);
         $stmt->nextParameter($email);
@@ -338,7 +362,7 @@ class UsersData {
             if(self::validateName($name, true) !== '')
                 throw new InvalidArgumentException('$name is not valid.');
 
-            $fields[] = 'username = ?';
+            $fields[] = 'user_name = ?';
             $values[] = $name;
         }
 
@@ -346,7 +370,7 @@ class UsersData {
             if(self::validateEMailAddress($emailAddr, true) !== '')
                 throw new InvalidArgumentException('$emailAddr is not valid.');
 
-            $fields[] = 'email = ?';
+            $fields[] = 'user_email = ?';
             $values[] = $emailAddr;
         }
 
@@ -366,7 +390,7 @@ class UsersData {
         }
 
         if($displayRoleInfo !== null) {
-            $fields[] = 'display_role = ?';
+            $fields[] = 'user_display_role_id = ?';
             $values[] = $displayRoleInfo;
         }
 
@@ -434,7 +458,7 @@ class UsersData {
         if($userInfo instanceof UserInfo)
             $userInfo = $userInfo->id;
 
-        $stmt = $this->cache->get('UPDATE msz_users SET user_active = NOW(), last_ip = INET6_ATON(?) WHERE user_id = ?');
+        $stmt = $this->cache->get('UPDATE msz_users SET user_active = NOW(), user_remote_addr_last = INET6_ATON(?) WHERE user_id = ?');
         $stmt->nextParameter($remoteAddr);
         $stmt->nextParameter($userInfo);
         $stmt->execute();
@@ -564,7 +588,7 @@ class UsersData {
             $query = '?';
             $value = $userInfo->displayRoleId;
         } else {
-            $query = '(SELECT display_role FROM msz_users WHERE user_id = ?)';
+            $query = '(SELECT user_display_role_id FROM msz_users WHERE user_id = ?)';
             $value = $userInfo;
         }
 
@@ -580,7 +604,7 @@ class UsersData {
         if($userInfo instanceof UserInfo)
             $userInfo = $userInfo->id;
 
-        $stmt = $this->cache->get('SELECT MAX(role_hierarchy) FROM msz_roles WHERE role_id IN (SELECT role_id FROM msz_users_roles WHERE user_id = ?)');
+        $stmt = $this->cache->get('SELECT MAX(role_rank) FROM msz_roles WHERE role_id IN (SELECT role_id FROM msz_users_roles WHERE user_id = ?)');
         $stmt->nextParameter($userInfo);
         $stmt->execute();
 
@@ -589,7 +613,7 @@ class UsersData {
     }
 
     public function checkNameInUse(string $name): bool {
-        $stmt = $this->cache->get('SELECT COUNT(*) FROM msz_users WHERE username = ?');
+        $stmt = $this->cache->get('SELECT COUNT(*) FROM msz_users WHERE user_name = ?');
         $stmt->nextParameter($name);
         $stmt->execute();
         $result = $stmt->getResult();
@@ -634,7 +658,7 @@ class UsersData {
     }
 
     public function checkEMailAddressInUse(string $address): bool {
-        $stmt = $this->cache->get('SELECT COUNT(*) FROM msz_users WHERE email = ?');
+        $stmt = $this->cache->get('SELECT COUNT(*) FROM msz_users WHERE user_email = ?');
         $stmt->nextParameter($address);
         $stmt->execute();
         $result = $stmt->getResult();
diff --git a/templates/user/macros.twig b/templates/user/macros.twig
index 032a84da..bfdf06f9 100644
--- a/templates/user/macros.twig
+++ b/templates/user/macros.twig
@@ -1,23 +1,9 @@
 {% macro user_card(user) %}
     {% from 'macros.twig' import avatar %}
-    {% if user.info is defined %}
-        {% set colour = user.colour %}
-        {% set info = user.info %}
-        {% set ftopics = user.ftopics %}
-        {% set fposts = user.fposts %}
-    {% else %}
-        {% set colour = user.user_colour %}
-        {% set info = {
-            'id': user.user_id,
-            'name': user.username,
-            'title': user.user_title|default(''),
-            'countryCode': user.user_country|default('XX'),
-            'lastActiveTime': user.user_active,
-            'createdTime': user.user_created
-        } %}
-        {% set ftopics = user.user_count_topics %}
-        {% set fposts = user.user_count_posts %}
-    {% endif %}
+    {% set colour = user.colour %}
+    {% set info = user.info %}
+    {% set ftopics = user.ftopics %}
+    {% set fposts = user.fposts %}
 
     <div class="usercard" style="--accent-colour: {{ colour }}">
         <div class="usercard__background"></div>
diff --git a/tools/cron b/tools/cron
index 171fa38f..e1983430 100755
--- a/tools/cron
+++ b/tools/cron
@@ -48,13 +48,13 @@ else
 echo PHP_EOL;
 
 msz_sched_task_sql('Ensure main role exists.', true,
-    'INSERT IGNORE INTO msz_roles (role_id, role_name, role_hierarchy, role_colour, role_description, role_created) VALUES (1, "Member", 1, 1073741824, NULL, NOW())');
+    'INSERT IGNORE INTO msz_roles (role_id, role_name, role_rank, role_colour, role_description, role_created) VALUES (1, "Member", 1, 1073741824, NULL, NOW())');
 
 msz_sched_task_sql('Ensure all users have the main role.', true,
     'INSERT INTO msz_users_roles (user_id, role_id) SELECT user_id, 1 FROM msz_users AS u WHERE NOT EXISTS (SELECT 1 FROM msz_users_roles AS ur WHERE role_id = 1 AND u.user_id = ur.user_id)');
 
-msz_sched_task_sql('Ensure all display_role field values are correct with msz_users_roles.', true,
-    'UPDATE msz_users AS u SET display_role = (SELECT ur.role_id FROM msz_users_roles AS ur LEFT JOIN msz_roles AS r ON r.role_id = ur.role_id WHERE ur.user_id = u.user_id ORDER BY role_hierarchy DESC LIMIT 1) WHERE NOT EXISTS (SELECT 1 FROM msz_users_roles AS ur WHERE ur.role_id = u.display_role AND ur.user_id = u.user_id)');
+msz_sched_task_sql('Ensure all user_display_role_id field values are correct with msz_users_roles.', true,
+    'UPDATE msz_users AS u SET user_display_role_id = (SELECT ur.role_id FROM msz_users_roles AS ur LEFT JOIN msz_roles AS r ON r.role_id = ur.role_id WHERE ur.user_id = u.user_id ORDER BY role_rank DESC LIMIT 1) WHERE NOT EXISTS (SELECT 1 FROM msz_users_roles AS ur WHERE ur.role_id = u.user_display_role_id AND ur.user_id = u.user_id)');
 
 msz_sched_task_sql('Remove expired sessions.', false,
     'DELETE FROM msz_sessions WHERE session_expires < NOW()');
diff --git a/tools/devel-insert-bogus b/tools/devel-insert-bogus
index 369627fe..72beaad8 100755
--- a/tools/devel-insert-bogus
+++ b/tools/devel-insert-bogus
@@ -34,7 +34,7 @@ mkv_log('Running slow cron to ensure main role exists...');
 echo shell_exec(MSZ_ROOT . '/tools/cron slow');
 
 mkv_log('Preparing role and permissions insert statements...');
-$cr = $msz->dbConn->prepare('INSERT INTO msz_roles (role_hierarchy, role_name, role_title, role_description, role_hidden, role_can_leave, role_colour) VALUES (?, ?, ?, ?, ?, ?, ?)');
+$cr = $msz->dbConn->prepare('INSERT INTO msz_roles (role_rank, role_name, role_title, role_description, role_hidden, role_can_leave, role_colour) VALUES (?, ?, ?, ?, ?, ?, ?)');
 $cp = $msz->dbConn->prepare('REPLACE INTO msz_permissions (role_id, general_perms_allow, user_perms_allow, changelog_perms_allow, news_perms_allow, forum_perms_allow, comments_perms_allow) VALUES (?, ?, ?, ?, ?, ?, ?)');
 
 mkv_log('Adding permissions for main role...');
@@ -216,7 +216,7 @@ $msz->dbConn->execute('DELETE FROM msz_users');
 $msz->dbConn->execute('ALTER TABLE msz_users AUTO_INCREMENT = 1');
 
 mkv_log('Preparing user insert statements...');
-$cu = $msz->dbConn->prepare('INSERT INTO msz_users (username, password, email, register_ip, last_ip, user_super, user_country, user_about_content, user_about_parser, user_signature_content, user_signature_parser, user_birthdate, user_title, display_role) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)');
+$cu = $msz->dbConn->prepare('INSERT INTO msz_users (user_name, user_password, user_email, user_remote_addr_first, user_remote_addr_last, user_super, user_country, user_about_content, user_about_parser, user_signature_content, user_signature_parser, user_birthdate, user_title, user_display_role_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)');
 $ur = $msz->dbConn->prepare('REPLACE INTO msz_users_roles (user_id, role_id) VALUES (?, ?)');
 
 mkv_log('Creating admin user...');
@@ -357,7 +357,7 @@ $msz->dbConn->execute('DELETE FROM msz_news_categories');
 $msz->dbConn->execute('ALTER TABLE msz_news_categories AUTO_INCREMENT = 1');
 
 mkv_log('Preparing news categories insert statements...');
-$nc = $msz->dbConn->prepare('INSERT INTO msz_news_categories (category_name, category_description, category_is_hidden) VALUES (?, ?, ?)');
+$nc = $msz->dbConn->prepare('INSERT INTO msz_news_categories (category_name, category_description, category_hidden) VALUES (?, ?, ?)');
 $ncIds = [];
 
 for($i = 0; $i < 10; ++$i) {
@@ -379,7 +379,7 @@ $msz->dbConn->execute('DELETE FROM msz_news_posts');
 $msz->dbConn->execute('ALTER TABLE msz_news_posts AUTO_INCREMENT = 1');
 
 mkv_log('Preparing news posts table...');
-$np = $msz->dbConn->prepare('INSERT INTO msz_news_posts (category_id, user_id, post_is_featured, post_title, post_text) VALUES (?, ?, ?, ?, ?)');
+$np = $msz->dbConn->prepare('INSERT INTO msz_news_posts (category_id, user_id, post_featured, post_title, post_text) VALUES (?, ?, ?, ?, ?)');
 
 for($i = 0; $i < 200; ++$i) {
     mkv_log('Creating bogus news post ' . $i . '...');
@@ -471,7 +471,7 @@ $msz->dbConn->execute('DELETE FROM msz_forum_posts');
 $msz->dbConn->execute('ALTER TABLE msz_forum_posts AUTO_INCREMENT = 1');
 
 mkv_log('Preparing forum post insertion statement...');
-$fp = $msz->dbConn->prepare('INSERT INTO msz_forum_posts (topic_id, forum_id, user_id, post_ip, post_text, post_parse, post_display_signature, post_created, post_edited, post_deleted) VALUES (?, 1, ?, ?, ?, ?, ?, FROM_UNIXTIME(?), FROM_UNIXTIME(?), FROM_UNIXTIME(?))');
+$fp = $msz->dbConn->prepare('INSERT INTO msz_forum_posts (topic_id, forum_id, user_id, post_remote_addr, post_text, post_parse, post_display_signature, post_created, post_edited, post_deleted) VALUES (?, 1, ?, ?, ?, ?, ?, FROM_UNIXTIME(?), FROM_UNIXTIME(?), FROM_UNIXTIME(?))');
 
 $topCount = count($topIds);
 for($t = 0; $t < $topCount; ++$t) {