installation
This guide will walk you through installing hsJobQuest V2 on your FiveM server.
Prerequisites
Before installing, ensure you have the following:
Required Dependencies
ox_lib
https://github.com/overextended/ox_lib
UI framework
oxmysql
https://github.com/overextended/oxmysql
Database driver
Framework Support
hsJobQuest V2 supports the following frameworks:
QBCore
QBOX
ESX
Step 2: Database Setup
Import the SQL File
Open your database management tool (HeidiSQL, phpMyAdmin, etc.)
Select your FiveM database
Import the
install.sqlfile located in the resource folder
-- Create main tables
CREATE TABLE IF NOT EXISTS `hsjobquest_players` (
`citizenid` VARCHAR(50) PRIMARY KEY,
`name` VARCHAR(100) DEFAULT 'Unknown',
`xp` INT DEFAULT 0,
`rank` INT DEFAULT 1,
`money` INT DEFAULT 0,
`prestige` INT DEFAULT 0,
`career_path` INT DEFAULT 0,
`skill_points` INT DEFAULT 0,
`total_deliveries` INT DEFAULT 0,
`perfect_deliveries` INT DEFAULT 0,
`total_earnings` BIGINT DEFAULT 0,
`total_distance` FLOAT DEFAULT 0,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`last_played` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `hsjobquest_skills` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`citizenid` VARCHAR(50) NOT NULL,
`category` VARCHAR(50) NOT NULL,
`skill_name` VARCHAR(50) NOT NULL,
`level` INT DEFAULT 0,
UNIQUE KEY `unique_skill` (`citizenid`, `category`, `skill_name`),
FOREIGN KEY (`citizenid`) REFERENCES `hsjobquest_players`(`citizenid`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `hsjobquest_factions` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`citizenid` VARCHAR(50) NOT NULL,
`faction_id` VARCHAR(50) NOT NULL,
`reputation` INT DEFAULT 0,
`missions_completed` INT DEFAULT 0,
UNIQUE KEY `unique_faction` (`citizenid`, `faction_id`),
FOREIGN KEY (`citizenid`) REFERENCES `hsjobquest_players`(`citizenid`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `hsjobquest_achievements` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`citizenid` VARCHAR(50) NOT NULL,
`achievement_id` VARCHAR(100) NOT NULL,
`unlocked_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`claimed` TINYINT(1) DEFAULT 0,
UNIQUE KEY `unique_achievement` (`citizenid`, `achievement_id`),
FOREIGN KEY (`citizenid`) REFERENCES `hsjobquest_players`(`citizenid`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `hsjobquest_statistics` (
`citizenid` VARCHAR(50) PRIMARY KEY,
`fast_deliveries` INT DEFAULT 0,
`session_deliveries` INT DEFAULT 0,
`survived_robberies` INT DEFAULT 0,
`bad_weather_deliveries` INT DEFAULT 0,
`night_deliveries` INT DEFAULT 0,
`coop_deliveries` INT DEFAULT 0,
`bonus_objectives_completed` INT DEFAULT 0,
`perfect_streak_current` INT DEFAULT 0,
`perfect_streak_best` INT DEFAULT 0,
`unique_job_types_completed` TEXT,
`stealth_perfect` INT DEFAULT 0,
FOREIGN KEY (`citizenid`) REFERENCES `hsjobquest_players`(`citizenid`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `hsjobquest_heat` (
`citizenid` VARCHAR(50) PRIMARY KEY,
`heat_level` INT DEFAULT 0,
`last_decay` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (`citizenid`) REFERENCES `hsjobquest_players`(`citizenid`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `hsjobquest_mission_history` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`citizenid` VARCHAR(50) NOT NULL,
`mission_type` VARCHAR(50) NOT NULL,
`job_name` VARCHAR(100) NOT NULL,
`faction_id` VARCHAR(50),
`xp_earned` INT DEFAULT 0,
`money_earned` INT DEFAULT 0,
`completion_time` INT DEFAULT 0,
`damage_taken` FLOAT DEFAULT 0,
`bonus_completed` TINYINT(1) DEFAULT 0,
`was_coop` TINYINT(1) DEFAULT 0,
`completed_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (`citizenid`) REFERENCES `hsjobquest_players`(`citizenid`) ON DELETE CASCADE,
INDEX `idx_citizen_missions` (`citizenid`, `completed_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `hsjobquest_coop_groups` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`leader_citizenid` VARCHAR(50) NOT NULL,
`mission_id` VARCHAR(100),
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`expires_at` TIMESTAMP NULL,
FOREIGN KEY (`leader_citizenid`) REFERENCES `hsjobquest_players`(`citizenid`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `hsjobquest_coop_members` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`group_id` INT NOT NULL,
`citizenid` VARCHAR(50) NOT NULL,
`joined_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY `unique_member_group` (`group_id`, `citizenid`),
FOREIGN KEY (`group_id`) REFERENCES `hsjobquest_coop_groups`(`id`) ON DELETE CASCADE,
FOREIGN KEY (`citizenid`) REFERENCES `hsjobquest_players`(`citizenid`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `hsjobquest_economy` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`job_type` VARCHAR(50) UNIQUE NOT NULL,
`current_multiplier` FLOAT DEFAULT 1.0,
`demand_level` INT DEFAULT 50,
`last_updated` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `hsjobquest_economy` (`job_type`, `current_multiplier`, `demand_level`) VALUES
('food_delivery', 1.0, 50),
('parcel_delivery', 1.0, 50),
('medical_supply_delivery', 1.0, 50),
('luxury_goods_delivery', 1.0, 50),
('construction_material_delivery', 1.0, 50),
('mail_delivery', 1.0, 50),
('alcohol_delivery', 1.0, 50),
('pharmaceutical_delivery', 1.0, 50),
('grocery_delivery', 1.0, 50),
('auto_parts_delivery', 1.0, 50)
ON DUPLICATE KEY UPDATE `job_type` = `job_type`;
CREATE TABLE IF NOT EXISTS `hsjobquest_transactions` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`citizenid` VARCHAR(50) NOT NULL,
`type` ENUM('deposit', 'withdrawal') NOT NULL,
`amount` INT NOT NULL,
`description` VARCHAR(255),
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (`citizenid`) REFERENCES `hsjobquest_players`(`citizenid`) ON DELETE CASCADE,
INDEX `idx_citizen_trans` (`citizenid`, `created_at` DESC)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- ===== Index creation with checks (safe) =====
-- We'll create indexes only if they don't exist already.
DELIMITER $$
CREATE PROCEDURE create_index_if_missing()
BEGIN
DECLARE idxcount INT DEFAULT 0;
-- idx_player_xp
SELECT COUNT(*) INTO idxcount
FROM information_schema.statistics
WHERE table_schema = DATABASE() AND table_name = 'hsjobquest_players' AND index_name = 'idx_player_xp';
IF idxcount = 0 THEN
ALTER TABLE `hsjobquest_players` ADD INDEX `idx_player_xp` (`xp`);
END IF;
-- idx_player_prestige
SELECT COUNT(*) INTO idxcount
FROM information_schema.statistics
WHERE table_schema = DATABASE() AND table_name = 'hsjobquest_players' AND index_name = 'idx_player_prestige';
IF idxcount = 0 THEN
ALTER TABLE `hsjobquest_players` ADD INDEX `idx_player_prestige` (`prestige`, `xp`);
END IF;
-- idx_faction_rep
SELECT COUNT(*) INTO idxcount
FROM information_schema.statistics
WHERE table_schema = DATABASE() AND table_name = 'hsjobquest_factions' AND index_name = 'idx_faction_rep';
IF idxcount = 0 THEN
ALTER TABLE `hsjobquest_factions` ADD INDEX `idx_faction_rep` (`faction_id`, `reputation`);
END IF;
-- idx_achievement_unlock
SELECT COUNT(*) INTO idxcount
FROM information_schema.statistics
WHERE table_schema = DATABASE() AND table_name = 'hsjobquest_achievements' AND index_name = 'idx_achievement_unlock';
IF idxcount = 0 THEN
ALTER TABLE `hsjobquest_achievements` ADD INDEX `idx_achievement_unlock` (`unlocked_at`);
END IF;
END$$
DELIMITER ;
CALL create_index_if_missing();
DROP PROCEDURE IF EXISTS create_index_if_missing;
-- ===== Safe migration from V1 to V2: only run if legacy table exists =====
DELIMITER $$
CREATE PROCEDURE migrate_playerData_if_exists()
BEGIN
DECLARE tbl_count INT DEFAULT 0;
SELECT COUNT(*) INTO tbl_count
FROM information_schema.tables
WHERE table_schema = DATABASE()
AND table_name = 'playerData';
IF tbl_count > 0 THEN
INSERT INTO `hsjobquest_players` (`citizenid`, `name`, `xp`, `rank`, `money`)
SELECT `citizenid`, `name`, `xp`, `rank`, `money`
FROM `playerData`
ON DUPLICATE KEY UPDATE `name` = VALUES(`name`);
END IF;
END$$
DELIMITER ;
CALL migrate_playerData_if_exists();
DROP PROCEDURE IF EXISTS migrate_playerData_if_exists;
DROP TRIGGER IF EXISTS `init_player_stats`;
DROP TRIGGER IF EXISTS `update_session_deliveries`;
DELIMITER $$
CREATE TRIGGER `init_player_stats`
AFTER INSERT ON `hsjobquest_players`
FOR EACH ROW
BEGIN
INSERT INTO `hsjobquest_statistics` (`citizenid`) VALUES (NEW.citizenid);
END$$
CREATE TRIGGER `update_session_deliveries`
AFTER INSERT ON `hsjobquest_mission_history`
FOR EACH ROW
BEGIN
UPDATE `hsjobquest_statistics`
SET `session_deliveries` = `session_deliveries` + 1
WHERE `citizenid` = NEW.citizenid;
UPDATE `hsjobquest_players`
SET `total_deliveries` = `total_deliveries` + 1,
`total_earnings` = `total_earnings` + NEW.money_earned
WHERE `citizenid` = NEW.citizenid;
END$$
DELIMITER ;
-- Final checks/status
SELECT 'hsJobQuest V2 Database Installation Complete!' AS Status;
SELECT COUNT(*) AS 'Total Players' FROM hsjobquest_players;
Make sure your database user has CREATE TABLE permissions.
Step 3: Configure server.cfg
Add the following to your server.cfg:
# Dependencies (must be before hsJobQuestV2)
ensure ox_lib
ensure oxmysql
# Your framework
ensure qb-core # or es_extended for ESX
# hsJobQuest V2
ensure hsJobQuestV2Load Order is Important! Dependencies must be loaded BEFORE hsJobQuestV2.
Step 4: Basic Configuration
Open shared/config.lua and configure your framework:
-- Framework Selection
Config.Framework = 'QBCORE' -- Options: 'QBCORE', 'QBOX', 'ESX'
-- Target System
Config.TargetSystem = 'qb-target' -- Options: 'qb-target', 'ox_target'
-- Inventory System
Config.InventorySystem = 'qb-inventory' -- Options: 'qb-inventory', 'ox_inventory', etc.
-- Notification System
Config.NotifySystem = 'ox_lib' -- Options: 'ox_lib', 'qb-core', 'esx'Step 5: Verify Installation
Start your server
Join and go to the job hub location:
Default:
vec4(154.17, -3075.09, 5.9, 86.59)(near the docks)
Interact with the postal worker NPC
You should see the job selection menu
Check Console
If everything is working, you'll see:
[hsJobQuest] Initializing hsJobQuest V2...
[hsJobQuest] Database initialized
[hsJobQuest] Economy system initialized
[hsJobQuest] Mission generator initialized
[hsJobQuest] hsJobQuest V2 initialized successfully!Optional: Enable Debug Mode
For troubleshooting, enable debug mode in shared/config.lua:
Config.EnableDebug = trueThis will print detailed information to your server console.
Next Steps
Quick Start Guide
Learn how to use the system
Configuration
Customize all settings
Last updated