From 12dda243e162ecef7ddc2d30aeffbd582c19448c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 8 Jan 2026 21:39:38 +0000 Subject: [PATCH 1/2] Initial plan From d12a6428598f4527adb17aefd720a97f2f62ff24 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 8 Jan 2026 21:44:56 +0000 Subject: [PATCH 2/2] Add public release preparation: Remove internal docs, add INSTALLATION.md, issue templates, CONTRIBUTING.md, and enhance README Co-authored-by: Twiitchter <52999643+Twiitchter@users.noreply.github.com> --- .github/ISSUE_TEMPLATE/bug_report.yml | 213 +++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 11 + CONTRIBUTING.md | 193 ++++++++++++ INSTALLATION.md | 424 ++++++++++++++++++++++++++ INTEGRATION.md | 203 ------------ OPTIMIZATION_SUMMARY.md | 122 -------- PERFORMANCE.md | 259 ---------------- README.md | 382 ++++++++++++++++++++--- SOLUTION.md | 134 -------- server.cfg.example | 119 ++++++++ 10 files changed, 1307 insertions(+), 753 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 CONTRIBUTING.md create mode 100644 INSTALLATION.md delete mode 100644 INTEGRATION.md delete mode 100644 OPTIMIZATION_SUMMARY.md delete mode 100644 PERFORMANCE.md delete mode 100644 SOLUTION.md create mode 100644 server.cfg.example diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..977a921 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,213 @@ +name: Bug Report +description: Report an issue with ingenium.sql +title: "[Bug]: " +labels: ["bug", "needs-triage"] +body: + - type: markdown + attributes: + value: | + ## Thank you for reporting a bug! + + Please fill out this form completely. Issues that don't follow this template may be closed without review. + + **Before submitting:** + - Make sure you've checked the [Installation Guide](https://github.com/Ingenium-Games/ingenium.sql/blob/main/INSTALLATION.md) + - Search existing issues to see if this has been reported before + - Try the troubleshooting steps in the documentation + + - type: input + id: resource-version + attributes: + label: Resource Version + description: What version of ingenium.sql are you using? (Check package.json or releases page) + placeholder: "e.g., 1.0.1" + validations: + required: true + + - type: input + id: fivem-version + attributes: + label: FiveM Server Version + description: What version of FiveM server are you running? (Check server console or txAdmin) + placeholder: "e.g., 6683 or latest" + validations: + required: true + + - type: dropdown + id: operating-system + attributes: + label: Operating System + description: What operating system is your FiveM server running on? + options: + - Windows 10 + - Windows 11 + - Windows Server 2016 + - Windows Server 2019 + - Windows Server 2022 + - Ubuntu 20.04 + - Ubuntu 22.04 + - Debian 10 + - Debian 11 + - CentOS 7 + - CentOS 8 + - Other Linux + - macOS + - Other (please specify in description) + validations: + required: true + + - type: input + id: nodejs-version + attributes: + label: Node.js Version + description: What version of Node.js are you using? Run `node --version` to find out + placeholder: "e.g., v18.17.0" + validations: + required: true + + - type: dropdown + id: database-type + attributes: + label: Database Type and Version + description: What database are you using? + options: + - MariaDB 10.6+ + - MariaDB 10.3-10.5 + - MySQL 8.0+ + - MySQL 5.7 + - Other (please specify in description) + validations: + required: true + + - type: textarea + id: description + attributes: + label: Description of the Problem + description: Please describe the issue you're experiencing in detail + placeholder: | + Example: When I start my FiveM server, the ingenium.sql resource starts but I get an "Access denied" error when other resources try to query the database. + validations: + required: true + + - type: textarea + id: steps-to-reproduce + attributes: + label: Steps to Reproduce + description: Clear steps to reproduce the bug + placeholder: | + 1. Install ingenium.sql following the guide + 2. Configure server.cfg with connection string + 3. Start FiveM server + 4. Attempt to execute a query from another resource + 5. See error in console + validations: + required: true + + - type: textarea + id: expected-behavior + attributes: + label: Expected Behavior + description: What did you expect to happen? + placeholder: "The query should execute successfully and return results" + validations: + required: true + + - type: textarea + id: actual-behavior + attributes: + label: Actual Behavior + description: What actually happened? + placeholder: "I received an 'Access denied for user' error" + validations: + required: true + + - type: textarea + id: error-logs + attributes: + label: Error Messages and Logs + description: | + Please paste any error messages or relevant logs here. + + **How to copy logs:** + - **From FiveM Console**: Select the error text, right-click, and choose Copy + - **From Server Console**: Scroll to the error, select text, and copy + - **From txAdmin**: Check the Live Console tab and copy relevant messages + + **Tip**: Include a few lines before and after the error for context + render: shell + placeholder: | + Example: + [ script:ingenium.sql] Error: Access denied for user 'fivem_user'@'localhost' (using password: YES) + [ script:ingenium.sql] at Packet.asError (node_modules/mysql2/lib/packets/packet.js:728:17) + [ script:ingenium.sql] at Connection.handlePacket (node_modules/mysql2/lib/connection.js:422:18) + validations: + required: true + + - type: textarea + id: screenshots + attributes: + label: Screenshots + description: | + If applicable, add screenshots to help explain your problem. + + **How to add screenshots:** + - Take a screenshot using your OS screenshot tool + - Drag and drop the image file here, or paste from clipboard + - Multiple screenshots are welcome! + placeholder: Drag and drop images here or paste from clipboard + + - type: textarea + id: configuration + attributes: + label: Configuration (server.cfg) + description: | + Please share your ingenium.sql configuration from server.cfg (REMOVE PASSWORDS!) + + **IMPORTANT**: Replace your actual password with "****" or "REDACTED" + render: cfg + placeholder: | + Example (with password removed): + set mysql_connection_string "mysql://fivem_user:****@localhost:3306/fivem_database" + set mysql_connection_limit "10" + set mysql_charset "utf8mb4" + ensure ingenium.sql + validations: + required: true + + - type: dropdown + id: troubleshooting-attempted + attributes: + label: Have you tried troubleshooting steps? + description: | + Have you checked the [Installation Guide](https://github.com/Ingenium-Games/ingenium.sql/blob/main/INSTALLATION.md) and tried the troubleshooting section? + options: + - "Yes, I've tried troubleshooting steps" + - "No, I haven't tried troubleshooting yet" + validations: + required: true + + - type: textarea + id: additional-context + attributes: + label: Additional Context + description: Any other information that might be helpful + placeholder: | + - Are you using txAdmin or running server manually? + - Are you using any other database resources? + - Did this work before and suddenly stop? + - Any recent changes to your server? + + - type: checkboxes + id: checklist + attributes: + label: Pre-submission Checklist + description: Please confirm the following before submitting + options: + - label: I have searched existing issues and this is not a duplicate + required: true + - label: I have checked the Installation Guide and tried troubleshooting steps + required: true + - label: I have removed any sensitive information (passwords, IPs, etc.) from my report + required: true + - label: I have provided all required information above + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..31e9be9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,11 @@ +blank_issues_enabled: false +contact_links: + - name: 💬 General Questions & Support + url: https://github.com/Ingenium-Games/ingenium.sql/discussions + about: Ask questions and discuss ingenium.sql with the community + - name: 📖 Installation Guide + url: https://github.com/Ingenium-Games/ingenium.sql/blob/main/INSTALLATION.md + about: Step-by-step installation instructions for beginners + - name: 📚 Documentation + url: https://github.com/Ingenium-Games/ingenium.sql/blob/main/README.md + about: Full documentation and API reference diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..a8c9c28 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,193 @@ +# Contributing to ingenium.sql + +Thank you for your interest in contributing to ingenium.sql! We welcome contributions from the community and appreciate your help in making this resource better. + +## 📋 Table of Contents + +- [How to Report Bugs](#how-to-report-bugs) +- [How to Suggest Features](#how-to-suggest-features) +- [How to Contribute Code](#how-to-contribute-code) +- [Pull Request Process](#pull-request-process) +- [Code Standards](#code-standards) +- [Community Guidelines](#community-guidelines) + +## 🐛 How to Report Bugs + +**Before reporting a bug:** +1. Check the [Installation Guide](INSTALLATION.md) and troubleshooting section +2. Search [existing issues](https://github.com/Ingenium-Games/ingenium.sql/issues) to avoid duplicates +3. Try to reproduce the issue with a fresh installation + +**When reporting a bug:** +- **ALWAYS use the [Bug Report template](https://github.com/Ingenium-Games/ingenium.sql/issues/new/choose)** +- Provide complete information (version numbers, error logs, configuration) +- Include steps to reproduce the issue +- Be as specific as possible +- Remove sensitive information (passwords, IPs) from your report + +**Issues that don't follow the template will be closed without review.** + +## 💡 How to Suggest Features + +We welcome feature suggestions! Here's how to propose a new feature: + +1. **Check existing issues** to see if it's already been suggested +2. **Open a new issue** with the title: `[Feature Request]: Your idea` +3. **Describe the feature**: + - What problem does it solve? + - How would it work? + - Are there any alternatives? + - Would it break existing functionality? +4. **Be open to discussion** - we may suggest modifications or alternatives + +Feature requests that align with the project goals and don't add excessive complexity are more likely to be implemented. + +## 💻 How to Contribute Code + +We appreciate code contributions! Here's the process: + +### Getting Started + +1. **Fork the repository** + - Click the "Fork" button on GitHub + - Clone your fork locally: `git clone https://github.com/YOUR-USERNAME/ingenium.sql.git` + +2. **Create a branch** + - Use a descriptive name: `git checkout -b feature/your-feature-name` + - Or for bugs: `git checkout -b fix/bug-description` + +3. **Set up your environment** + - Run `npm install` to install dependencies + - Make sure you have Node.js 16+ installed + - Test the resource on a local FiveM server + +### Making Changes + +1. **Keep changes focused** + - One feature or bug fix per pull request + - Don't mix unrelated changes + +2. **Write clean code** + - Follow existing code style + - Add comments for complex logic + - Keep functions small and focused + +3. **Test your changes** + - Test on a local FiveM server + - Verify existing functionality still works + - Test edge cases and error conditions + +4. **Update documentation** + - Update README.md if you change the API + - Update INSTALLATION.md if you change installation steps + - Add code comments for complex features + +## 🔄 Pull Request Process + +1. **Commit your changes** + ```bash + git add . + git commit -m "Description of your changes" + ``` + - Use clear, descriptive commit messages + - Reference issue numbers: "Fixes #123" or "Relates to #456" + +2. **Push to your fork** + ```bash + git push origin feature/your-feature-name + ``` + +3. **Open a Pull Request** + - Go to the original repository on GitHub + - Click "New Pull Request" + - Select your branch + - Fill out the PR template completely + +4. **PR Description should include:** + - What changes were made and why + - Issue number if fixing a bug + - Testing steps you performed + - Any breaking changes or migration notes + - Screenshots if applicable (for UI changes) + +5. **Wait for review** + - Maintainers will review your PR + - Be responsive to feedback and questions + - Make requested changes in new commits + - Don't force-push unless specifically asked + +6. **After approval** + - Your PR will be merged by a maintainer + - You can delete your branch + - Celebrate! 🎉 + +## 📝 Code Standards + +### JavaScript/Node.js + +- Use **ES6+ syntax** (const/let, arrow functions, async/await) +- **No unnecessary dependencies** - keep the package lean +- **Error handling** - all async operations should handle errors +- **Comments** - explain WHY, not just WHAT +- **Formatting** - consistent indentation (2 spaces) + +### Lua + +- Follow **FiveM Lua conventions** +- Use **PascalCase** for exported functions +- Use **camelCase** for local functions +- **Comments** for exported functions explaining parameters + +### Documentation + +- Use clear, beginner-friendly language +- Provide examples for new features +- Keep README.md up to date +- Include code comments for complex logic + +## 🤝 Community Guidelines + +- **Be respectful** - treat others with kindness and respect +- **Be patient** - maintainers are volunteers with limited time +- **Be constructive** - provide helpful feedback and suggestions +- **Be understanding** - not everyone has the same skill level +- **No harassment** - inappropriate behavior will not be tolerated + +### What We Look For + +**Good contributions:** +- ✅ Solve real problems users are experiencing +- ✅ Improve performance or reliability +- ✅ Make the resource easier to use +- ✅ Improve documentation +- ✅ Fix bugs with tests to prevent regression + +**What we generally don't accept:** +- ❌ Breaking changes without strong justification +- ❌ Features that add excessive complexity +- ❌ Code that duplicates existing functionality +- ❌ Changes that break compatibility with FiveM +- ❌ Unrelated refactoring in feature PRs + +## 🎯 Development Priorities + +Current focus areas (as of latest release): +1. **Stability** - fixing bugs and improving reliability +2. **Performance** - optimizing query execution +3. **Documentation** - making it easier for beginners +4. **Compatibility** - ensuring it works across different setups + +## 📞 Questions? + +If you have questions about contributing: +- Check existing [Discussions](https://github.com/Ingenium-Games/ingenium.sql/discussions) +- Read the [README.md](README.md) and [INSTALLATION.md](INSTALLATION.md) +- Open a new Discussion (not an issue) for general questions + +## 📜 License + +By contributing to ingenium.sql, you agree that your contributions will be licensed under the project's MIT License. + +--- + +Thank you for contributing to ingenium.sql! Your efforts help make this resource better for everyone. 🚀 diff --git a/INSTALLATION.md b/INSTALLATION.md new file mode 100644 index 0000000..09815aa --- /dev/null +++ b/INSTALLATION.md @@ -0,0 +1,424 @@ +# Installation Guide for ingenium.sql + +This guide will walk you through installing and configuring `ingenium.sql` for your FiveM server. Don't worry if you're new to this - we'll explain everything step by step! + +## 📋 Prerequisites + +Before you begin, make sure you have: + +- **Operating System**: Windows 10/11, Linux (Ubuntu 20.04+), or macOS 10.15+ +- **FiveM Server**: A working FiveM server installation +- **Administrator Access**: Ability to install software on your server +- **Internet Connection**: For downloading dependencies + +## 📦 Step 1: Installing Node.js + +Node.js is required to run this resource. Here's how to install it on your system: + +### Windows Installation + +1. **Download Node.js**: + - Visit [https://nodejs.org/](https://nodejs.org/) + - Download the **LTS (Long Term Support)** version - look for the big green button + - We recommend Node.js 16 or newer + +2. **Run the Installer**: + - Double-click the downloaded `.msi` file + - Follow the installation wizard + - Keep all default options selected (including "Automatically install necessary tools") + - Click "Install" and wait for completion + +3. **Verify Installation**: + - Open **Command Prompt** (press `Win + R`, type `cmd`, press Enter) + - Type: `node --version` + - You should see something like `v16.x.x` or `v18.x.x` + - If you see a version number, you're good to go! ✅ + +### Linux Installation (Ubuntu/Debian) + +1. **Update Package Manager**: + ```bash + sudo apt update + ``` + +2. **Install Node.js**: + ```bash + curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash - + sudo apt install -y nodejs + ``` + +3. **Verify Installation**: + ```bash + node --version + npm --version + ``` + Both commands should return version numbers. + +### macOS Installation + +1. **Download Node.js**: + - Visit [https://nodejs.org/](https://nodejs.org/) + - Download the **LTS version** for macOS + - Or use Homebrew: `brew install node` + +2. **Verify Installation**: + - Open **Terminal** + - Type: `node --version` + - You should see a version number like `v18.x.x` + +## 🔽 Step 2: Installing the Resource + +### Download from GitHub + +1. **Get the Resource**: + - Go to [https://github.com/Ingenium-Games/ingenium.sql](https://github.com/Ingenium-Games/ingenium.sql) + - Click the green **"Code"** button + - Select **"Download ZIP"** + +2. **Extract the Files**: + - Extract the downloaded ZIP file + - You should see a folder named `ingenium.sql-main` or similar + +3. **Move to Resources Folder**: + - Rename the folder to exactly `ingenium.sql` (remove `-main` or any suffix) + - Move the `ingenium.sql` folder to your FiveM server's `resources` folder + - Example path: `C:\FiveM\server-data\resources\ingenium.sql` + +### Install Dependencies + +This is a crucial step! The resource needs additional packages to work. + +1. **Open Terminal/Command Prompt**: + - **Windows**: Navigate to the resource folder in File Explorer, then type `cmd` in the address bar and press Enter + - **Linux/Mac**: Use `cd` command to navigate to the resource folder + +2. **Run npm install**: + ```bash + npm install + ``` + +3. **Wait for Completion**: + - You'll see downloading progress + - Wait until you see "added X packages" or similar + - This may take 1-3 minutes depending on your internet speed + +### Common npm Install Errors and Solutions + +#### ❌ Error: "npm: command not found" or "'npm' is not recognized" + +**Solution**: Node.js isn't installed correctly or not in your PATH. +- Re-install Node.js following the steps above +- Make sure to restart Command Prompt/Terminal after installation +- On Windows, try restarting your computer + +#### ❌ Error: "EACCES: permission denied" + +**Solution**: You don't have permission to install packages. +- **Linux/Mac**: Run with sudo: `sudo npm install` +- **Windows**: Run Command Prompt as Administrator (right-click → "Run as administrator") + +#### ❌ Error: "Network timeout" or "Unable to resolve" + +**Solution**: Network/firewall issues. +- Check your internet connection +- If behind a corporate firewall, you may need to configure npm proxy +- Try using a different network or VPN + +#### ❌ Error: "gyp ERR" or Python-related errors + +**Solution**: Build tools are missing (rare, but can happen). +- **Windows**: Run: `npm install --global windows-build-tools` +- **Linux**: Install: `sudo apt install build-essential` +- **Mac**: Install Xcode Command Line Tools: `xcode-select --install` + +## 🗄️ Step 3: Database Setup + +You need a MySQL or MariaDB database for this resource. **MariaDB is recommended** as it performs better with FiveM. + +### Installing MariaDB + +#### Windows + +1. **Download MariaDB**: + - Visit [https://mariadb.org/download/](https://mariadb.org/download/) + - Select your Windows version + - Download the installer (MSI package) + +2. **Run Installer**: + - Double-click the downloaded file + - Follow the installation wizard + - **Important**: Remember the root password you set! + - Keep default port: 3306 + +3. **Verify Installation**: + - Open Command Prompt + - Type: `mysql --version` + - You should see MariaDB version information + +#### Linux (Ubuntu/Debian) + +```bash +# Update package manager +sudo apt update + +# Install MariaDB +sudo apt install mariadb-server mariadb-client + +# Secure installation (follow prompts) +sudo mysql_secure_installation +``` + +When prompted: +- Set a root password (remember it!) +- Remove anonymous users: **Yes** +- Disallow root login remotely: **Yes** +- Remove test database: **Yes** +- Reload privilege tables: **Yes** + +#### macOS + +```bash +# Using Homebrew +brew install mariadb + +# Start MariaDB service +brew services start mariadb + +# Secure installation +mysql_secure_installation +``` + +### Creating Your Database + +1. **Connect to MariaDB**: + ```bash + mysql -u root -p + ``` + Enter the root password when prompted. + +2. **Create the Database**: + ```sql + CREATE DATABASE fivem_database; + ``` + +3. **Create a Database User** (Important for security!): + ```sql + CREATE USER 'fivem_user'@'localhost' IDENTIFIED BY 'your_secure_password'; + ``` + ⚠️ Replace `your_secure_password` with a strong password! + +4. **Grant Permissions**: + ```sql + GRANT SELECT, INSERT, UPDATE, DELETE ON fivem_database.* TO 'fivem_user'@'localhost'; + FLUSH PRIVILEGES; + ``` + +5. **Exit MariaDB**: + ```sql + EXIT; + ``` + +### Verifying Database Connection + +Test your database connection: + +```bash +mysql -u fivem_user -p fivem_database +``` + +If you can log in successfully, your database is ready! ✅ + +## ⚙️ Step 4: Configuration + +Now we need to tell your FiveM server how to connect to the database. + +### Option 1: Connection String (Recommended - Easiest) + +1. **Open your server.cfg file**: + - Located in your FiveM server folder + - Example: `C:\FiveM\server-data\server.cfg` + +2. **Add this line** (edit with your details): + ```cfg + set mysql_connection_string "mysql://fivem_user:your_secure_password@localhost:3306/fivem_database" + ``` + + **Breaking it down**: + - `fivem_user` = your database username + - `your_secure_password` = your database password + - `localhost` = database host (use `localhost` if database is on same machine) + - `3306` = database port (default for MySQL/MariaDB) + - `fivem_database` = your database name + +### Option 2: Individual Settings (Alternative) + +If you prefer, you can set each option separately: + +```cfg +set mysql_host "localhost" +set mysql_port "3306" +set mysql_user "fivem_user" +set mysql_password "your_secure_password" +set mysql_database "fivem_database" +``` + +### Optional Settings + +Add these if you need to customize performance: + +```cfg +# Maximum concurrent database connections (default: 10) +# Increase for busy servers (20-50), decrease for small servers (5-10) +set mysql_connection_limit "10" + +# Character encoding (default: utf8mb4) +# utf8mb4 supports emoji and international characters +set mysql_charset "utf8mb4" +``` + +### Starting the Resource + +Add this line to your `server.cfg` (usually near the bottom, after other `ensure` lines): + +```cfg +ensure ingenium.sql +``` + +### Testing the Connection + +1. **Start your FiveM server** +2. **Check the server console** for these messages: + - `✓ ingenium.sql: Database connection established` + - `✓ ingenium.sql: Resource started successfully` + +If you see these messages, congratulations! 🎉 Your installation is complete! + +## 🔧 Step 5: Troubleshooting Common Issues + +### Issue: "npm: command not found" or "npm is not recognized" + +**Problem**: Node.js is not installed or not in your system PATH. + +**Solution**: +1. Verify Node.js installation: `node --version` +2. If command not found, reinstall Node.js +3. Restart your terminal/command prompt +4. On Windows, restart your computer after installing Node.js + +### Issue: "Cannot connect to database" or "Connection refused" + +**Problem**: FiveM can't reach your database. + +**Solutions**: +1. **Check if database is running**: + ```bash + # Windows + services.msc # Look for MySQL/MariaDB service + + # Linux + sudo systemctl status mariadb + ``` + +2. **Verify credentials**: + - Double-check username, password, database name in server.cfg + - Test connection manually: `mysql -u fivem_user -p fivem_database` + +3. **Check database exists**: + ```bash + mysql -u root -p + SHOW DATABASES; # Look for your database in the list + ``` + +4. **Firewall blocking** (if database is on different machine): + - Make sure port 3306 is open + - Check Windows Firewall or iptables (Linux) + +### Issue: "Module not found" or "Cannot find module 'mysql2'" + +**Problem**: npm dependencies weren't installed correctly. + +**Solution**: +1. Navigate to resource folder: `cd resources/ingenium.sql` +2. Delete `node_modules` folder if it exists +3. Run: `npm install` +4. Wait for completion +5. Restart FiveM server + +### Issue: "Access denied for user" or "Permission denied" + +**Problem**: Database user doesn't have proper permissions. + +**Solution**: +1. Connect as root: `mysql -u root -p` +2. Grant permissions again: + ```sql + GRANT SELECT, INSERT, UPDATE, DELETE ON fivem_database.* TO 'fivem_user'@'localhost'; + FLUSH PRIVILEGES; + ``` +3. Test connection: `mysql -u fivem_user -p fivem_database` + +### Issue: "Table doesn't exist" errors in game + +**Problem**: Your database tables haven't been created yet. + +**Solution**: +- This resource only provides the connection to the database +- Other resources create their own tables when they start +- Make sure other resources are starting AFTER ingenium.sql +- Check documentation for other resources to see if they need manual table creation + +### Issue: Resource not starting or "Failed to load resource" + +**Problem**: Resource folder name or configuration issue. + +**Solutions**: +1. **Verify folder name**: Must be exactly `ingenium.sql` (case-sensitive on Linux) +2. **Check fxmanifest.lua**: File must exist and be readable +3. **Check server.cfg**: Make sure line says `ensure ingenium.sql` not `start ingenium.sql` +4. **Look for errors**: Read server console carefully for specific error messages + +## 📚 Additional Resources + +- **Node.js Documentation**: [https://nodejs.org/docs](https://nodejs.org/docs) +- **MariaDB Documentation**: [https://mariadb.com/kb/en/documentation/](https://mariadb.com/kb/en/documentation/) +- **MySQL Documentation**: [https://dev.mysql.com/doc/](https://dev.mysql.com/doc/) +- **FiveM Documentation**: [https://docs.fivem.net/](https://docs.fivem.net/) +- **npm Documentation**: [https://docs.npmjs.com/](https://docs.npmjs.com/) + +## 🆘 Still Need Help? + +If you're still having issues after trying the troubleshooting steps: + +1. **Check existing issues**: [GitHub Issues](https://github.com/Ingenium-Games/ingenium.sql/issues) +2. **Report a new bug**: Use our [Bug Report Template](https://github.com/Ingenium-Games/ingenium.sql/issues/new/choose) +3. **Include in your report**: + - Exact error message (copy from console) + - Screenshots if relevant + - Your operating system + - Node.js version (`node --version`) + - FiveM server version + - Database type and version + +Remember: The more information you provide, the faster we can help you! 🚀 + +## ✅ Installation Checklist + +Use this checklist to make sure you've completed all steps: + +- [ ] Node.js installed and verified (`node --version` works) +- [ ] npm installed and verified (`npm --version` works) +- [ ] Resource downloaded and extracted +- [ ] Folder renamed to exactly `ingenium.sql` +- [ ] Resource moved to FiveM resources folder +- [ ] `npm install` completed successfully in resource folder +- [ ] MariaDB/MySQL installed and running +- [ ] Database created +- [ ] Database user created with permissions +- [ ] Database connection tested manually +- [ ] server.cfg updated with connection string or individual settings +- [ ] `ensure ingenium.sql` added to server.cfg +- [ ] FiveM server started successfully +- [ ] No errors in server console +- [ ] Database connection confirmed in console + +If all items are checked, you're ready to go! 🎉 diff --git a/INTEGRATION.md b/INTEGRATION.md deleted file mode 100644 index 72c46a4..0000000 --- a/INTEGRATION.md +++ /dev/null @@ -1,203 +0,0 @@ -# Integration Guide for Ingenium Framework - -This guide explains how to integrate `ingenium.sql` with the main Ingenium framework resource. - -## For Ingenium Core Resource - -The `_handler.lua` file provides a Lua wrapper that can be included in the main `ingenium` resource to provide convenient access to SQL functions through the `ig.sql` namespace. - -### Option 1: Direct Integration (Copy File) - -1. Copy `_handler.lua` from this resource to your main ingenium resource -2. Add it to the ingenium resource's `fxmanifest.lua`: - ```lua - server_scripts { - -- ... other scripts - 'path/to/_handler.lua', - -- ... other scripts - } - ``` -3. Ensure `ingenium.sql` is started BEFORE the ingenium resource: - ```cfg - ensure ingenium.sql - ensure ingenium - ``` - -### Option 2: Export-based Integration (No Copy Needed) - -Instead of copying `_handler.lua`, you can create a similar wrapper directly in your ingenium resource: - -```lua --- In your ingenium resource's server-side Lua file -if not ig.sql then ig.sql = {} end - -function ig.sql.Query(query, parameters, callback) - return exports['ingenium.sql']:query(query, parameters or {}, callback) -end - -function ig.sql.FetchSingle(query, parameters, callback) - return exports['ingenium.sql']:fetchSingle(query, parameters or {}, callback) -end - -function ig.sql.FetchScalar(query, parameters, callback) - return exports['ingenium.sql']:fetchScalar(query, parameters or {}, callback) -end - -function ig.sql.Insert(query, parameters, callback) - return exports['ingenium.sql']:insert(query, parameters or {}, callback) -end - -function ig.sql.Update(query, parameters, callback) - return exports['ingenium.sql']:update(query, parameters or {}, callback) -end - -function ig.sql.Transaction(queries, callback) - return exports['ingenium.sql']:transaction(queries, callback) -end - -function ig.sql.Batch(queries, callback) - return exports['ingenium.sql']:batch(queries, callback) -end - -function ig.sql.IsReady() - return exports['ingenium.sql']:isReady() -end - -function ig.sql.AwaitReady(timeout) - local maxWait = timeout or 30000 - local waited = 0 - local interval = 100 - - while not ig.sql.IsReady() and waited < maxWait do - Citizen.Wait(interval) - waited = waited + interval - end - - return ig.sql.IsReady() -end - --- Wait for SQL to be ready -Citizen.CreateThread(function() - if ig.sql.AwaitReady() then - print("^2[Ingenium] SQL connection ready^7") - else - print("^1[Ingenium] SQL connection timeout^7") - end -end) -``` - -## Usage in Ingenium Resources - -Once integrated, you can use SQL functions through the `ig.sql` namespace: - -```lua --- Query multiple rows -local users = ig.sql.Query('SELECT * FROM users WHERE active = ?', {1}) - --- Get single row -local user = ig.sql.FetchSingle('SELECT * FROM users WHERE id = ?', {userId}) - --- Get single value -local count = ig.sql.FetchScalar('SELECT COUNT(*) FROM users') - --- Insert -local insertId = ig.sql.Insert('INSERT INTO logs (message) VALUES (?)', {'User logged in'}) - --- Update -local affected = ig.sql.Update('UPDATE users SET last_login = NOW() WHERE id = ?', {userId}) - --- Transaction -local result = ig.sql.Transaction({ - {query = 'UPDATE accounts SET balance = balance - ? WHERE id = ?', parameters = {100, fromAccount}}, - {query = 'UPDATE accounts SET balance = balance + ? WHERE id = ?', parameters = {100, toAccount}} -}) - --- Named parameters -local user = ig.sql.FetchSingle( - 'SELECT * FROM users WHERE name = @name AND age > @age', - {name = 'John', age = 18} -) - --- With callbacks -ig.sql.Query('SELECT * FROM users', {}, function(results) - for _, user in ipairs(results) do - print(user.name) - end -end) -``` - -## Direct Export Usage (Without ig.sql wrapper) - -Any resource can also use `ingenium.sql` directly without the wrapper: - -```lua --- In any FiveM resource -local users = exports['ingenium.sql']:query('SELECT * FROM users') -local user = exports['ingenium.sql']:fetchSingle('SELECT * FROM users WHERE id = ?', {1}) -``` - -## Migration from Internal Implementation - -If you previously had an internalized SQL implementation in the ingenium resource: - -1. Remove the old SQL files from ingenium resource -2. Remove mysql2 from ingenium's package.json dependencies -3. Add `ingenium.sql` as a separate resource -4. Update your server.cfg to start `ingenium.sql` before `ingenium` -5. Update any direct SQL calls to use the new export pattern -6. Test all SQL operations to ensure they work correctly - -## Troubleshooting - -### "Connection pool is not ready" Error - -This happens when trying to execute queries before the pool is initialized. Solutions: - -1. Use `ig.sql.AwaitReady()` before making queries -2. Ensure `ingenium.sql` is started before your resource -3. Add a dependency in your resource's fxmanifest.lua: - ```lua - dependency 'ingenium.sql' - ``` - -### Resource Load Order - -The correct order in server.cfg: -```cfg -# Database resource MUST be started first -ensure ingenium.sql - -# Then your other resources that depend on it -ensure ingenium -ensure ig.bank -# ... other resources -``` - -### Performance Issues - -If you experience slow queries: -1. Check `exports['ingenium.sql']:getStats()` for performance metrics -2. Increase `mysql_connection_limit` if you have many concurrent queries -3. Add database indexes to frequently queried columns -4. Use batch operations instead of multiple individual queries - -## Benefits of External Resource - -The external `ingenium.sql` resource provides several advantages over the internalized version: - -1. **Separation of Concerns**: Database logic is isolated from game logic -2. **Reusability**: Other resources can use `ingenium.sql` without depending on ingenium -3. **Easier Updates**: Update database connector without touching main resource -4. **Better Connection Management**: Dedicated resource for connection pooling -5. **Independent Testing**: Can test database operations separately -6. **Reduced Resource Size**: Main ingenium resource is smaller without SQL code - -## Next Steps - -After integrating `ingenium.sql`: - -1. Test all existing SQL queries to ensure they work -2. Monitor performance and connection stability -3. Update documentation for other developers -4. Consider creating database migration scripts if needed -5. Review and optimize slow queries diff --git a/OPTIMIZATION_SUMMARY.md b/OPTIMIZATION_SUMMARY.md deleted file mode 100644 index 88b9456..0000000 --- a/OPTIMIZATION_SUMMARY.md +++ /dev/null @@ -1,122 +0,0 @@ -# Performance Optimization Summary - -## Overview -This PR implements comprehensive performance optimizations to the ingenium.sql resource, improving query execution speed and reducing resource usage without changing the API surface. - -## Changes Made - -### 1. Regex Pattern Caching (server.js) -- **Before**: Created new RegExp object on every named parameter query -- **After**: Cache and reuse compiled regex patterns -- **Impact**: 30-50% faster named parameter processing - -### 2. Query Type Detection Caching (server.js) -- **Before**: Ran regex match on every execute() call -- **After**: Cache query types using first 100 characters as key (up to 1,000 entries) -- **Impact**: Near-instant query type detection for repeated patterns - -### 3. Welford's Incremental Average (_pool.js) -- **Before**: Recalculated average on every getStats() call -- **After**: Maintain average incrementally using numerically stable algorithm -- **Impact**: 20x faster stats retrieval, no floating-point precision loss - -### 4. Exponential Backoff (_handler.lua) -- **Before**: Fixed 100ms polling interval -- **After**: 50ms → 100ms → 200ms → 400ms → 500ms (capped) -- **Impact**: ~60% reduction in CPU usage during connection wait - -### 5. Early Exit Optimization (server.js) -- **Before**: Always ran regex matching in parameter processing -- **After**: Quick indexOf('@') check before expensive operations -- **Impact**: Near-zero overhead for positional parameter queries - -### 6. Code Quality Improvements -- Removed unused preparedStatementCache declaration -- Fixed potential regex state issues with global flag -- Improved loop structure with modern for...of syntax -- Added comprehensive inline documentation - -## Testing - -### Syntax Validation -✓ All JavaScript files validated with node -c -✓ No syntax errors - -### Pattern Verification -✓ Regex caching: Working correctly -✓ Query type caching: Working correctly -✓ Incremental average: Mathematically correct -✓ Parameter processing: All cases handled -✓ Exponential backoff: Proper progression - -### Security Scanning -✓ CodeQL: 0 alerts (JavaScript) -✓ No security vulnerabilities introduced - -## Performance Benchmarks (Estimated) - -| Operation | Before | After | Improvement | -|-----------|--------|-------|-------------| -| Named param processing | 33k ops/sec | 50k ops/sec | +50% | -| Query type detection (cached) | 100k ops/sec | 1M ops/sec | +900% | -| Stats retrieval | 500k ops/sec | 10M ops/sec | +1900% | -| Connection wait CPU | 100% | 40% | -60% | - -## Memory Usage - -Additional memory overhead: < 100KB -- Regex cache: ~20KB (unbounded but limited by app parameter names) -- Query type cache: ~50KB (hard limit of 1,000 entries) -- Stats structure: 40 bytes (5 doubles) - -## Backward Compatibility - -✓ 100% backward compatible -✓ No API changes -✓ No breaking changes -✓ All existing code continues to work - -## Files Changed - -- `server.js`: Parameter processing, query type detection optimizations -- `_pool.js`: Incremental statistics, removed unused cache -- `_handler.lua`: Exponential backoff for AwaitReady -- `README.md`: Expanded performance tips section -- `PERFORMANCE.md`: New comprehensive performance documentation -- `package.json`: Version bump to 1.0.1 -- `fxmanifest.lua`: Version bump to 1.0.1 - -## Documentation - -Added comprehensive documentation: -- PERFORMANCE.md: Detailed explanation of all optimizations -- README.md: Updated performance tips section -- Inline comments: Marked all optimized functions - -## Recommendations for Users - -1. **Use the resource as before** - all optimizations are automatic -2. **Monitor with getStats()** - now very cheap to call frequently -3. **Use prepared statements** - mysql2 caching works even better now -4. **Add database indexes** - still #1 optimization for slow queries - -## Future Work (Not in This PR) - -Potential future optimizations identified but not implemented: -- Query result caching with TTL -- Connection pre-warming -- Parallel batch query execution -- Adaptive slow query thresholds - -## Version History - -- v1.0.0: Initial release -- v1.0.1: Performance optimizations (this PR) - -## Credits - -Performance improvements based on: -- Code review feedback -- Industry best practices (Welford's algorithm) -- FiveM community insights -- oxmysql reference architecture diff --git a/PERFORMANCE.md b/PERFORMANCE.md deleted file mode 100644 index 8a15b20..0000000 --- a/PERFORMANCE.md +++ /dev/null @@ -1,259 +0,0 @@ -# Performance Improvements - -This document details the performance optimizations made to ingenium.sql to improve query execution speed and reduce resource usage. - -## Overview - -The ingenium.sql resource has been optimized to handle high-load scenarios with minimal overhead. These improvements focus on reducing CPU usage, memory allocations, and eliminating redundant computations. - -## Key Optimizations - -### 1. Cached Regex Patterns (server.js) - -**Problem**: The `processParameters` function was compiling regex patterns on every query execution, even for repeated parameter names. - -**Solution**: Implemented a regex cache using a Map that stores pre-compiled patterns: -- Regex patterns for named parameters (@param) are compiled once and reused -- Eliminates repeated `new RegExp()` calls -- Reduces CPU overhead for parameter processing - -**Impact**: -- ~30-50% faster parameter processing for queries with named parameters -- Reduced garbage collection pressure from fewer object allocations - -```javascript -// Before: New regex created every time -const regex = new RegExp(paramName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'); - -// After: Regex cached and reused -function getCachedRegex(paramName) { - if (!regexCache.has(paramName)) { - const escaped = paramName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - regexCache.set(paramName, new RegExp(escaped, 'g')); - } - return regexCache.get(paramName); -} -``` - -### 2. Query Type Detection Caching (server.js) - -**Problem**: Query type (SELECT, INSERT, UPDATE, DELETE) was detected using regex matching on every `execute()` call, even for repeated query patterns. - -**Solution**: Implemented a query type cache with normalized query keys: -- Cache stores the detected query type for up to 1,000 unique query patterns -- Uses first 100 characters as cache key to handle queries with different parameter values -- Eliminates repeated regex operations for common queries -- Size limit prevents unbounded memory growth - -**Impact**: -- Immediate return for cached queries (no regex matching) -- Particularly beneficial for applications with repeated query patterns -- Memory-efficient caching strategy (~50KB for 1,000 entries) - -```javascript -// Before: Regex match on every call -const match = sqlQuery.match(/^\s*(\w+)/i); -const queryType = match ? match[1].toUpperCase() : ''; - -// After: Check cache first with normalized key -const cacheKey = normalizeQueryForCache(sqlQuery); -if (queryTypeCache.has(cacheKey)) { - return queryTypeCache.get(cacheKey); -} -``` - -### 3. Incremental Statistics Calculation (_pool.js) - -**Problem**: Average query time was recalculated on every `getStats()` call by dividing total time by total queries. - -**Solution**: Calculate average incrementally using Welford's online algorithm: -``` -delta = value - avg_old -avg_new = avg_old + delta / count -``` - -**Impact**: -- O(1) operation with no division on stats retrieval -- Numerically stable for large query counts (prevents floating-point precision loss) -- Stats can be retrieved with zero computational overhead - -```javascript -// Before: Recalculate on every getStats() call -getStats() { - const avgTime = this.stats.totalQueries > 0 ? - this.stats.totalTime / this.stats.totalQueries : 0; - return { ...this.stats, averageTime: avgTime }; -} - -// After: Maintain incrementally with Welford's algorithm -const delta = duration - this.stats.averageTime; -this.stats.averageTime = this.stats.averageTime + delta / this.stats.totalQueries; -``` - -### 4. Exponential Backoff in AwaitReady (_handler.lua) - -**Problem**: The Lua `AwaitReady` function used busy-waiting with fixed 100ms intervals, wasting CPU cycles. - -**Solution**: Implemented exponential backoff: -- Starts with 50ms intervals for quick responsiveness -- Doubles the interval up to 500ms maximum -- Reduces total CPU cycles while maintaining responsiveness - -**Impact**: -- ~60% reduction in CPU usage during connection initialization -- Still responsive (50ms initial check) -- Better for resource-constrained servers - -```lua --- Before: Fixed interval -local interval = 100 -while not ig.sql.IsReady() and waited < maxWait do - Citizen.Wait(interval) - waited = waited + interval -end - --- After: Exponential backoff -local interval = 50 -while not ig.sql.IsReady() and waited < maxWait do - Citizen.Wait(interval) - waited = waited + interval - interval = math.min(interval * 2, maxInterval) -end -``` - -### 5. Early Exit Optimization (server.js) - -**Problem**: Parameter processing always performed regex matching even when queries had no named parameters. - -**Solution**: Added early exit check: -- Quick `indexOf('@')` check before regex processing -- Immediately returns for queries using only positional parameters (?) -- Avoids unnecessary string operations - -**Impact**: -- Significant speedup for queries without named parameters -- Near-zero overhead for positional parameter queries - -```javascript -// Quick check: if no @ symbol, return early -if (query.indexOf('@') === -1) { - return { query, params: [] }; -} -``` - -### 6. Set-based Duplicate Detection (server.js) - -**Problem**: Used `forEach` with Set checking inside the loop for parameter deduplication. - -**Solution**: Replaced with modern `for...of` loop with Set: -- More idiomatic and slightly faster -- Better early-continue logic -- Clearer intent in code - -**Impact**: -- Minor performance improvement -- Better code readability and maintainability - -## Performance Benchmarks - -### Parameter Processing -- **Positional parameters**: ~100,000 ops/sec (unchanged, already optimal) -- **Named parameters (cached)**: ~50,000 ops/sec (50% improvement from ~33,000) -- **Named parameters (uncached)**: ~33,000 ops/sec (first execution baseline) - -### Query Type Detection -- **Cached queries**: ~1,000,000 ops/sec (99.9% improvement) -- **Uncached queries**: ~100,000 ops/sec (baseline) - -### Statistics Retrieval -- **Before**: ~500,000 ops/sec -- **After**: ~10,000,000 ops/sec (20x improvement) - -### Connection Wait Time (Lua) -- **CPU usage during wait**: ~60% reduction -- **Time to detect ready**: No change (~50ms first check) - -## Memory Usage - -All optimizations are designed to be memory-efficient: - -- **Regex cache**: Unbounded but limited by unique parameter names in application (typically <100 entries) -- **Query type cache**: Hard-limited to 1,000 entries (~50KB) with normalized keys -- **Stats structure**: Fixed size (5 numbers) - -Total additional memory overhead: < 100KB in typical usage - -## Recommendations for Users - -To get the best performance from ingenium.sql: - -1. **Use consistent query patterns**: Query type caching works best with repeated queries -2. **Prefer positional parameters**: Slightly faster than named parameters -3. **Use named parameters for clarity**: When you do use them, they're well-optimized -4. **Monitor stats regularly**: `getStats()` is now very cheap to call -5. **Tune connection limit**: Based on your specific workload -6. **Add database indexes**: Still the #1 performance improvement for slow queries - -## Future Optimization Opportunities - -Potential areas for further optimization (not yet implemented): - -1. **Query result caching**: Cache results for identical queries within a time window -2. **Connection pre-warming**: Pre-establish connections before resource start -3. **Batch query optimization**: Parallel execution for independent queries in batch operations -4. **Adaptive slow query threshold**: Dynamically adjust based on server performance -5. **Query plan caching**: Cache EXPLAIN results for complex queries - -## Version History - -- **v1.0.0**: Initial release with basic optimizations -- **v1.0.1** (current): Added comprehensive performance improvements - - Regex pattern caching - - Query type detection caching - - Incremental statistics - - Exponential backoff in Lua - - Early exit optimizations - -## Monitoring Performance - -To monitor the effectiveness of these optimizations: - -```lua --- Get statistics -local stats = exports['ingenium.sql']:getStats() - -print('Total queries: ' .. stats.totalQueries) -print('Average time: ' .. stats.averageTime .. 'ms') -print('Slow queries: ' .. stats.slowQueries) -print('Failed queries: ' .. stats.failedQueries) -``` - -Watch for: -- **Average time < 10ms**: Excellent performance -- **Average time 10-50ms**: Good performance -- **Average time 50-150ms**: Acceptable, consider optimization -- **Average time > 150ms**: Investigate slow queries and add indexes - -## Troubleshooting Performance Issues - -If you experience performance problems: - -1. **Check `getStats()`**: Identify if the problem is query count or slow queries -2. **Monitor slow query events**: Listen for `ingenium.sql:SlowQuery` events -3. **Review database indexes**: Most slow queries need better indexes -4. **Check connection limit**: May need to increase for high concurrency -5. **Verify network latency**: Database on different server? Check network -6. **Profile your queries**: Use MySQL's `EXPLAIN` to analyze query plans - -## Contributing - -If you identify additional performance improvements, please: -1. Benchmark the improvement with realistic workloads -2. Document the change clearly -3. Ensure backward compatibility -4. Submit a pull request with benchmarks - ---- - -**Last Updated**: January 2026 -**Version**: 1.0.1 diff --git a/README.md b/README.md index a5f2e80..6f80966 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,14 @@ External MySQL connection pool resource for FiveM using mysql2. `ingenium.sql` is a standalone FiveM resource that provides MySQL database connectivity for the Ingenium framework. The resource folder should be named `ingenium.sql` and other resources should reference it as `exports['ingenium.sql']` when importing functionality. +## ⚠️ Important Notice + +**This repository is actively maintained and open to community contributions.** + +When reporting issues, **you MUST use the provided issue template**. Issues that do not follow the template will be automatically closed without review. This helps us help you more efficiently. + +**📝 [Report a Bug](https://github.com/Ingenium-Games/ingenium.sql/issues/new/choose)** | **📖 [Installation Guide](INSTALLATION.md)** | **🤝 [Contributing Guide](CONTRIBUTING.md)** + ## Features - **Connection Pooling**: Efficient connection management using mysql2's built-in pooling @@ -49,54 +57,348 @@ External MySQL connection pool resource for FiveM using mysql2. ## Usage -### From Lua (Other Resources) +### Quick Start Example + +Here's a simple example to get you started: ```lua --- Query - Get multiple rows -local users = exports['ingenium.sql']:query('SELECT * FROM users WHERE age > ?', {18}) +-- Check if database is ready before using it +if exports['ingenium.sql']:isReady() then + -- Get a user from the database + local user = exports['ingenium.sql']:fetchSingle( + 'SELECT * FROM users WHERE id = ?', + {1} -- Parameters go here + ) + + -- Check if user was found + if user then + print('Found user: ' .. user.name) + else + print('User not found') + end +end +``` --- FetchSingle - Get one row -local user = exports['ingenium.sql']:fetchSingle('SELECT * FROM users WHERE id = ?', {userId}) +### Common Use Cases --- FetchScalar - Get single value -local count = exports['ingenium.sql']:fetchScalar('SELECT COUNT(*) FROM users WHERE active = ?', {1}) +#### 1. Player Data Management --- Insert - Returns insert ID -local insertId = exports['ingenium.sql']:insert('INSERT INTO users (name, age) VALUES (?, ?)', {'John', 25}) +```lua +-- Get player data when they join +RegisterNetEvent('playerJoining') +AddEventHandler('playerJoining', function() + local src = source + local identifier = GetPlayerIdentifier(src, 0) + + -- Try to find existing player + local player = exports['ingenium.sql']:fetchSingle( + 'SELECT * FROM players WHERE identifier = ?', + {identifier} + ) + + if not player then + -- Create new player if not found + local insertId = exports['ingenium.sql']:insert( + 'INSERT INTO players (identifier, name, first_joined) VALUES (?, ?, NOW())', + {identifier, GetPlayerName(src)} + ) + print('Created new player with ID: ' .. insertId) + else + -- Update last seen + exports['ingenium.sql']:update( + 'UPDATE players SET last_seen = NOW() WHERE id = ?', + {player.id} + ) + print('Player ' .. player.name .. ' returned!') + end +end) +``` --- Update - Returns affected rows -local affected = exports['ingenium.sql']:update('UPDATE users SET age = ? WHERE id = ?', {26, userId}) +#### 2. Inventory System --- Transaction - Multiple queries atomically -local result = exports['ingenium.sql']:transaction({ - {query = 'UPDATE accounts SET balance = balance - ? WHERE id = ?', parameters = {100, fromAccount}}, - {query = 'UPDATE accounts SET balance = balance + ? WHERE id = ?', parameters = {100, toAccount}} -}) +```lua +-- Get all items in player's inventory +function GetPlayerInventory(playerId) + local items = exports['ingenium.sql']:query( + 'SELECT * FROM inventory WHERE player_id = ? ORDER BY slot', + {playerId} + ) + + -- Returns array of items, or empty array if none found + return items or {} +end + +-- Add item to inventory +function AddItem(playerId, itemName, quantity) + -- Check if player already has this item + local existing = exports['ingenium.sql']:fetchSingle( + 'SELECT * FROM inventory WHERE player_id = ? AND item_name = ?', + {playerId, itemName} + ) + + if existing then + -- Update quantity + local affected = exports['ingenium.sql']:update( + 'UPDATE inventory SET quantity = quantity + ? WHERE id = ?', + {quantity, existing.id} + ) + return affected > 0 + else + -- Insert new item + local insertId = exports['ingenium.sql']:insert( + 'INSERT INTO inventory (player_id, item_name, quantity) VALUES (?, ?, ?)', + {playerId, itemName, quantity} + ) + return insertId > 0 + end +end + +-- Remove item from inventory +function RemoveItem(playerId, itemName, quantity) + local affected = exports['ingenium.sql']:update( + 'UPDATE inventory SET quantity = quantity - ? WHERE player_id = ? AND item_name = ? AND quantity >= ?', + {quantity, playerId, itemName, quantity} + ) + + if affected > 0 then + -- Clean up items with 0 quantity + exports['ingenium.sql']:update( + 'DELETE FROM inventory WHERE player_id = ? AND quantity <= 0', + {playerId} + ) + return true + end + + return false -- Not enough items +end +``` + +#### 3. Banking System with Transactions + +```lua +-- Transfer money between accounts (atomic operation) +function TransferMoney(fromAccount, toAccount, amount) + -- Get current balances first to validate + local fromBalance = exports['ingenium.sql']:fetchScalar( + 'SELECT balance FROM accounts WHERE id = ?', + {fromAccount} + ) + + if not fromBalance or fromBalance < amount then + return false, 'Insufficient funds' + end + + -- Execute transfer as a transaction (all or nothing) + local result = exports['ingenium.sql']:transaction({ + { + query = 'UPDATE accounts SET balance = balance - ? WHERE id = ?', + parameters = {amount, fromAccount} + }, + { + query = 'UPDATE accounts SET balance = balance + ? WHERE id = ?', + parameters = {amount, toAccount} + }, + { + query = 'INSERT INTO transactions (from_account, to_account, amount, timestamp) VALUES (?, ?, ?, NOW())', + parameters = {fromAccount, toAccount, amount} + } + }) + + if result.success then + return true, 'Transfer completed' + else + return false, 'Transaction failed' + end +end + +-- Get account balance +function GetBalance(accountId) + local balance = exports['ingenium.sql']:fetchScalar( + 'SELECT balance FROM accounts WHERE id = ?', + {accountId} + ) + return balance or 0 +end + +-- Get transaction history +function GetTransactionHistory(accountId, limit) + limit = limit or 10 + local transactions = exports['ingenium.sql']:query( + 'SELECT * FROM transactions WHERE from_account = ? OR to_account = ? ORDER BY timestamp DESC LIMIT ?', + {accountId, accountId, limit} + ) + return transactions or {} +end +``` + +### Error Handling Examples + +```lua +-- Example 1: Basic error handling with pcall +local success, result = pcall(function() + return exports['ingenium.sql']:fetchSingle( + 'SELECT * FROM users WHERE id = ?', + {userId} + ) +end) + +if success then + if result then + print('User found: ' .. result.name) + else + print('User not found') + end +else + print('Database error: ' .. tostring(result)) +end + +-- Example 2: With callback for async operations +exports['ingenium.sql']:query( + 'SELECT * FROM users WHERE active = ?', + {1}, + function(results) + if results then + print('Found ' .. #results .. ' active users') + for _, user in ipairs(results) do + print('- ' .. user.name) + end + else + print('Query failed or returned no results') + end + end +) + +-- Example 3: Validating before operations +function SafeUpdateUser(userId, newName) + -- Check if database is ready + if not exports['ingenium.sql']:isReady() then + return false, 'Database not ready' + end + + -- Validate input + if not userId or not newName or newName == '' then + return false, 'Invalid parameters' + end + + -- Check if user exists + local exists = exports['ingenium.sql']:fetchScalar( + 'SELECT COUNT(*) FROM users WHERE id = ?', + {userId} + ) + + if exists == 0 then + return false, 'User not found' + end + + -- Perform update + local affected = exports['ingenium.sql']:update( + 'UPDATE users SET name = ? WHERE id = ?', + {newName, userId} + ) + + if affected > 0 then + return true, 'User updated' + else + return false, 'Update failed' + end +end +``` --- Batch - Multiple queries without transaction +### Named Parameters vs Positional Parameters + +```lua +-- Positional parameters (?) - slightly faster +local user = exports['ingenium.sql']:fetchSingle( + 'SELECT * FROM users WHERE name = ? AND age > ?', + {'John', 18} -- Order must match the ? placeholders +) + +-- Named parameters (@name) - more readable for complex queries +local user = exports['ingenium.sql']:fetchSingle( + 'SELECT * FROM users WHERE name = @name AND age > @minAge', + {name = 'John', minAge = 18} -- Order doesn't matter +) + +-- Named parameters are especially useful for complex queries +local results = exports['ingenium.sql']:query( + [[ + SELECT u.*, a.balance + FROM users u + LEFT JOIN accounts a ON u.id = a.user_id + WHERE u.name LIKE @searchName + AND u.age BETWEEN @minAge AND @maxAge + AND a.balance > @minBalance + ]], + { + searchName = '%John%', + minAge = 18, + maxAge = 65, + minBalance = 1000 + } +) +``` + +### Batch Operations for Performance + +```lua +-- Instead of multiple separate queries (slow): +local user = exports['ingenium.sql']:fetchSingle('SELECT * FROM users WHERE id = ?', {userId}) +local inventory = exports['ingenium.sql']:query('SELECT * FROM inventory WHERE player_id = ?', {userId}) +local accounts = exports['ingenium.sql']:query('SELECT * FROM accounts WHERE user_id = ?', {userId}) + +-- Use batch to execute all at once (faster): local results = exports['ingenium.sql']:batch({ - {query = 'SELECT * FROM users WHERE id = ?', parameters = {1}}, - {query = 'SELECT * FROM accounts WHERE user_id = ?', parameters = {1}} + {query = 'SELECT * FROM users WHERE id = ?', parameters = {userId}}, + {query = 'SELECT * FROM inventory WHERE player_id = ?', parameters = {userId}}, + {query = 'SELECT * FROM accounts WHERE user_id = ?', parameters = {userId}} }) --- Named Parameters -local user = exports['ingenium.sql']:fetchSingle( - 'SELECT * FROM users WHERE name = @name AND age > @age', - {name = 'John', age = 18} +-- Access results by index +local user = results[1][1] -- First query, first row +local inventory = results[2] -- Second query, all rows +local accounts = results[3] -- Third query, all rows +``` + +### Prepared Statements for Repeated Queries + +```lua +-- For queries executed many times, prepare them once +local getUserQuery = exports['ingenium.sql']:prepareQuery( + 'SELECT * FROM users WHERE id = ?' ) --- Prepared Statements -local queryId = exports['ingenium.sql']:prepareQuery('SELECT * FROM users WHERE id = ?') -local user = exports['ingenium.sql']:executePrepared(queryId, {userId}) +-- Then execute multiple times with different parameters +for i = 1, 100 do + local user = exports['ingenium.sql']:executePrepared(getUserQuery, {i}) + if user then + print('Found user: ' .. user.name) + end +end --- Check Connection Status -if exports['ingenium.sql']:isReady() then - print('Database is ready') +-- This is more efficient than calling query() 100 times +``` + +### Advanced: Checking Connection Status and Statistics + +```lua +-- Check if database is ready before critical operations +if not exports['ingenium.sql']:isReady() then + print('WARNING: Database not ready, waiting...') + -- Wait or retry logic here + return end --- Get Statistics +-- Get performance statistics (useful for monitoring) local stats = exports['ingenium.sql']:getStats() -print(json.encode(stats)) +print('Total queries: ' .. stats.totalQueries) +print('Failed queries: ' .. stats.failedQueries) +print('Average query time: ' .. stats.avgQueryTime .. 'ms') +print('Slowest query: ' .. stats.slowestQuery .. 'ms') + +-- Log slow queries for optimization +if stats.slowestQuery > 500 then + print('WARNING: Slow queries detected, check database indexes') +end ``` ### From Ingenium Core (ig.sql namespace) @@ -344,10 +646,20 @@ FLUSH PRIVILEGES; ## Support -For issues related to: -- **This resource**: Open an issue in this repository -- **Ingenium framework**: Contact the ingenium development team -- **MySQL/MariaDB**: Consult official documentation +**Need help?** We're here to assist! + +- **🐛 Found a bug?** [Report it using our Bug Report template](https://github.com/Ingenium-Games/ingenium.sql/issues/new/choose) + - **Important**: Issues must follow the template or they will be closed without review +- **📖 Installation help?** Check our [Installation Guide](INSTALLATION.md) with step-by-step instructions +- **💬 Questions or discussions?** Visit [GitHub Discussions](https://github.com/Ingenium-Games/ingenium.sql/discussions) +- **🤝 Want to contribute?** Read our [Contributing Guide](CONTRIBUTING.md) +- **📚 Ingenium framework issues?** Contact the Ingenium development team +- **🔧 MySQL/MariaDB issues?** Consult the [official documentation](https://mariadb.com/kb/en/documentation/) + +**Before asking for help:** +1. Read the [Installation Guide](INSTALLATION.md) and try troubleshooting steps +2. Search existing issues to see if your problem has been solved +3. Make sure you're using a supported version (Node.js 16+, MySQL 5.7+/MariaDB 10.3+) ## License diff --git a/SOLUTION.md b/SOLUTION.md deleted file mode 100644 index 0563102..0000000 --- a/SOLUTION.md +++ /dev/null @@ -1,134 +0,0 @@ -# Solution Summary for Issue: Revert internal SQL dependency and make it external - -## Problem Statement - -The ingenium resource previously had an internalized mysql2 NodeJS module that experienced issues: -- Connections would establish but then timeout -- Database showed active connection pool in sleep state -- The connection appeared to be connected but was not responsive - -This was removed from the main ingenium resource, and a new external resource was needed. - -## Solution Implemented - -Created a complete standalone FiveM resource called **ingenium.sql** (resource name: `ig.sql`) that: - -### 1. Addresses Timeout Issues -- **Root Cause**: Previous implementation likely lacked keep-alive packets, causing idle connections to timeout -- **Solution**: Implemented connection pool with `enableKeepAlive: true` and `keepAliveInitialDelay: 10000ms` -- **Result**: Connections now send periodic keep-alive packets to prevent timeout while idle - -### 2. Proper Connection Pool Architecture -Following oxmysql best practices: -- Uses mysql2's built-in connection pooling -- Configurable connection limits (default: 10) -- Automatic connection reuse -- Proper connection cleanup on resource stop -- Timezone normalization (UTC) - -### 3. Complete Query API -Maintains the same interface expected by ingenium resources: -- `query()` - Multiple rows -- `fetchSingle()` - Single row -- `fetchScalar()` - Single value -- `insert()` - Returns insertId -- `update()` - Returns affectedRows -- `transaction()` - Atomic operations -- `batch()` - Multiple queries -- `prepareQuery()` & `executePrepared()` - Prepared statements - -### 4. Parameter Flexibility -- Supports positional parameters: `SELECT * FROM users WHERE id = ?` -- Supports named parameters: `SELECT * FROM users WHERE name = @name` -- Properly handles repeated parameters -- Prevents SQL injection through prepared statements - -### 5. Framework Integration -- Provided `_handler.lua` for ingenium's `c.sql` namespace -- Resource name is `ig.sql` as expected by framework -- Can be integrated into ingenium or used standalone by any resource - -## Technical Decisions - -### MySQL2 vs MariaDB Connector -**Decision: MySQL2** - -Reasons: -1. More widely adopted in FiveM community (oxmysql uses it) -2. Better Node.js integration -3. Extensive documentation and community support -4. Compatible with both MySQL and MariaDB databases -5. Proven track record in production FiveM servers - -The MariaDB connector was considered but mysql2 offers better ecosystem compatibility. - -### Architecture Pattern -Followed oxmysql architecture as requested: -- Dedicated connection pool module (`_pool.js`) -- Query handler with business logic (`server.js`) -- Optional Lua wrapper (`_handler.lua`) -- Clean separation of concerns - -## Files Delivered - -1. **fxmanifest.lua** - FiveM resource manifest -2. **package.json** - NPM dependencies (mysql2) -3. **server.js** - Query implementation (381 lines) -4. **_pool.js** - Connection pool manager (172 lines) -5. **_handler.lua** - Lua wrapper for ingenium (197 lines) -6. **README.md** - Complete documentation -7. **INTEGRATION.md** - Integration guide -8. **config.example.cfg** - Configuration example -9. **.gitignore** - Git exclusions - -## Validation - -✅ **Security**: CodeQL scan passed (0 alerts) -✅ **Syntax**: All JavaScript and Lua files validated -✅ **Structure**: All required exports and dependencies present -✅ **Documentation**: Comprehensive guides provided - -## Next Steps - -As requested in the original issue: - -> "Build this resource and then make an issue on the Ingenium resource to review and update to utilize this new resource if required." - -The resource is now complete and ready. The ingenium team should: - -1. **Install this resource** in their FiveM server -2. **Test the connection** to ensure timeout issues are resolved -3. **Create an issue** in the main ingenium repository to: - - Remove old internalized SQL code - - Integrate `_handler.lua` (or create equivalent wrapper) - - Update documentation to reference `ig.sql` resource - - Test all SQL operations in ingenium -4. **Monitor performance** after deployment - -## Key Improvements Over Previous Implementation - -| Aspect | Before (Internalized) | After (External ig.sql) | -|--------|----------------------|-------------------------| -| Timeout Issues | ❌ Frequent timeouts | ✅ Keep-alive prevents timeouts | -| Maintainability | ❌ Coupled to ingenium | ✅ Standalone, easy to update | -| Reusability | ❌ Only ingenium | ✅ Any resource can use it | -| Testing | ❌ Hard to test | ✅ Independent testing | -| Connection Pool | ⚠️ Basic | ✅ Advanced with monitoring | -| Documentation | ⚠️ Limited | ✅ Comprehensive | -| Security | ⚠️ Basic | ✅ Validated (CodeQL) | - -## Contact & Support - -For issues with this resource: -- Repository: https://github.com/Ingenium-Games/ingenium.sql -- Documentation: README.md and INTEGRATION.md - -For integrating with ingenium framework: -- See INTEGRATION.md for detailed instructions -- Create issue in ingenium repository for integration tasks - ---- - -**Status**: ✅ Implementation Complete -**Date**: January 3, 2026 -**Version**: 1.0.0 diff --git a/server.cfg.example b/server.cfg.example new file mode 100644 index 0000000..5555970 --- /dev/null +++ b/server.cfg.example @@ -0,0 +1,119 @@ +# ============================================================================= +# ingenium.sql Configuration Example +# ============================================================================= +# Copy these lines to your server.cfg and customize with your database details +# +# SECURITY TIP: Never share your server.cfg file publicly with passwords in it! +# ============================================================================= + +# --- DATABASE CONNECTION (Choose ONE method) --- + +# METHOD 1: Connection String (RECOMMENDED - easiest setup) +# This is the simplest way to configure your database connection. +# Format: mysql://username:password@host:port/database_name +# +# Replace these values with your actual database credentials: +# - username: Your database user (e.g., fivem_user) +# - password: Your database password (keep this secure!) +# - host: Database server address (use 'localhost' if on same machine) +# - port: Database port (default is 3306 for MySQL/MariaDB) +# - database_name: Name of your database (e.g., fivem_database) +# +# Example: +set mysql_connection_string "mysql://fivem_user:your_secure_password@localhost:3306/fivem_database" + + +# METHOD 2: Individual Settings (alternative) +# If you prefer to set each option separately, comment out the connection +# string above and uncomment these lines below: +# +# set mysql_host "localhost" # Database server address +# set mysql_port "3306" # Database port (default: 3306) +# set mysql_user "fivem_user" # Database username +# set mysql_password "your_secure_password" # Database password (keep secure!) +# set mysql_database "fivem_database" # Database name +# +# IMPORTANT: Use EITHER the connection string OR individual settings, not both! +# If both are provided, the connection string takes priority. + + +# --- OPTIONAL SETTINGS --- + +# Maximum Concurrent Database Connections (default: 10) +# This controls how many simultaneous database queries can run at once. +# +# Recommendations by server size: +# - Small server (< 20 players): 5-10 connections +# - Medium server (20-50 players): 10-20 connections +# - Large server (50-100 players): 20-40 connections +# - Very large server (100+ players): 40-60 connections +# +# Note: More connections use more RAM. Start low and increase if needed. +# Monitor using: exports['ingenium.sql']:getStats() from another resource +# +set mysql_connection_limit "10" + + +# Character Encoding (default: utf8mb4) +# utf8mb4 supports all Unicode characters including emoji and international text. +# +# Options: +# - utf8mb4: RECOMMENDED - Full Unicode support (emoji, special characters) +# - utf8: Legacy Unicode support (no emoji) +# - latin1: Basic ASCII only (not recommended) +# +# Unless you have a specific reason, leave this as utf8mb4 +# +set mysql_charset "utf8mb4" + + +# --- ADVANCED OPTIONS (Usually not needed) --- + +# Connection Timeout (milliseconds) +# How long to wait when establishing a new database connection +# Default: 10000 (10 seconds) +# +# set mysql_connect_timeout "10000" + +# Query Timeout (milliseconds) +# Maximum time to wait for a query to complete +# Default: 60000 (60 seconds) +# +# set mysql_query_timeout "60000" + + +# --- START THE RESOURCE --- +# This line should be added after your other resource 'ensure' lines +# Make sure ingenium.sql starts before any resources that need database access +# +ensure ingenium.sql + + +# ============================================================================= +# TROUBLESHOOTING TIPS +# ============================================================================= +# +# ❌ "Cannot connect to database" +# - Check that MySQL/MariaDB is running +# - Verify your username, password, and database name are correct +# - Make sure the database exists (create it if needed) +# - Check that your database user has proper permissions +# +# ❌ "Access denied for user" +# - Your username or password is incorrect +# - Or the database user doesn't have permissions on this database +# - Try connecting manually: mysql -u username -p database_name +# +# ❌ "Unknown database" +# - The database doesn't exist +# - Create it: CREATE DATABASE fivem_database; +# +# ❌ Resource fails to start +# - Check the resource folder is named exactly "ingenium.sql" +# - Make sure you ran "npm install" in the resource folder +# - Check FiveM server console for specific error messages +# +# 📖 For detailed help, see: +# https://github.com/Ingenium-Games/ingenium.sql/blob/main/INSTALLATION.md +# +# =============================================================================