วิธีเชื่อม FiveM กับ MySQL หรือ MariaDB ด้วย oxmysql แบบ Step-by-Step

การเชื่อม FiveM กับ MySQL หรือ MariaDB ด้วย oxmysql ต้องมี 3 ส่วนหลักคือ Database Server, Resource oxmysql และ mysql_connection_string ที่ถูกต้อง จากนั้น Resource FiveM ฝั่ง Server จึงสามารถใช้ MySQL.query, MySQL.single, MySQL.insert, MySQL.update หรือ API อื่นเพื่ออ่านและบันทึกข้อมูลได้

โครงสร้างคือ

FiveM Resource
↓
Server Script
↓
oxmysql
↓
Connection String
↓
MySQL / MariaDB
↓
Database

ตัวอย่าง server.cfg

set mysql_connection_string "mysql://fivem_user:password@127.0.0.1:3306/fivem"

ensure oxmysql
ensure my_resource

ตัวอย่าง fxmanifest.lua

fx_version 'cerulean'
game 'gta5'

dependency 'oxmysql'

server_script '@oxmysql/lib/MySQL.lua'
server_script 'server.lua'

และ server.lua

local result =
    MySQL.scalar.await(
        'SELECT 1'
    )

print(
    'Database test:',
    result
)

ถ้าได้ค่า

1

แสดงว่า FiveM Resource สามารถส่ง Query ผ่าน oxmysql ไปยัง Database ได้แล้ว

① สิ่งที่ต้องเตรียมก่อนเชื่อม Database

ควรมี

① FXServer ที่ใช้งานได้
② MySQL หรือ MariaDB
③ Database สำหรับ FiveM
④ Database User
⑤ oxmysql
⑥ server.cfg
⑦ Resource ที่ต้องการใช้ Database

อย่าเริ่มเขียน SQL ใน Script ก่อนที่ Database Connection จะทำงาน

ควรทำให้

SELECT 1

ผ่านก่อน แล้วจึงสร้างระบบ Character, Garage หรือ Inventory

② MySQL กับ MariaDB เลือกอะไร

oxmysql รองรับ MySQL และ MariaDB

เอกสาร oxmysql ปัจจุบันแนะนำ MariaDB เพื่อ Compatibility กับ FiveM Resources จำนวนมาก เนื่องจาก Scripts รุ่นเก่าหลายตัวถูกออกแบบโดยอิงพฤติกรรมในยุค MySQL 5.7

MySQL 8 ก็ใช้งานได้ แต่ Script เก่าบางตัวอาจมีปัญหาเรื่อง

Reserved keywords
JSON/LONGTEXT defaults
SQL compatibility

ถ้าเริ่ม Server ใหม่และไม่มี Requirement พิเศษ MariaDB เป็นตัวเลือกที่เหมาะมาก

③ XAMPP จำเป็นไหม

ไม่

oxmysql Documentation ไม่แนะนำให้ติดตั้ง XAMPP เพียงเพื่อใช้ Database

XAMPP เป็น Web Development Stack ที่รวมหลาย Service เช่น

Apache
PHP
MariaDB

สำหรับ Production FiveM ควรติดตั้ง MariaDB/MySQL โดยตรงตาม Environment ที่ใช้

④ Database Server อยู่เครื่องเดียวกับ FiveM ได้ไหม

ได้

Architecture ง่ายที่สุดคือ

FXServer
+
MariaDB

อยู่เครื่องเดียวกัน

แล้วใช้ Host

127.0.0.1

หรือ

localhost

เช่น

set mysql_connection_string "mysql://fivem_user:password@127.0.0.1:3306/fivem"

ข้อดีคือไม่ต้องส่ง Database Traffic ผ่าน Internet

⑤ Database อยู่คนละเครื่องได้ไหม

ได้

เช่น

FXServer
192.168.1.10

Database
192.168.1.20

Connection String

set mysql_connection_string "mysql://fivem_user:password@192.168.1.20:3306/fivem"

แต่ Database Server ต้อง

Listen Interface ถูกต้อง
Firewall อนุญาต
Database User อนุญาต Host
Network เชื่อมถึงกัน

อย่าเปิด Port 3306 สู่ Internet โดยไม่จำเป็น

⑥ Port MySQL ปกติคืออะไร

Port มาตรฐานที่พบทั่วไปคือ

3306

ตัวอย่าง

127.0.0.1:3306

ถ้า Database Server ตั้ง Port อื่น ต้องใช้ Port จริง

เช่น

3307

Connection String ก็ต้องเปลี่ยนตาม

⑦ สร้าง Database สำหรับ FiveM

ตัวอย่าง SQL

CREATE DATABASE fivem
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;

Database ชื่อ

fivem

จะเป็น Container หลักของ Tables เช่น

characters
vehicles
inventories
properties

ชื่อ Database สามารถเปลี่ยนได้ตาม Project

⑧ ทำไมใช้ utf8mb4

utf8mb4 รองรับ Unicode ได้กว้าง รวมถึง

ภาษาไทย
ภาษาอังกฤษ
อีโมจิ
อักขระพิเศษ

เหมาะกับข้อมูล FiveM เช่น

Character Name
Phone Messages
Business Names
Notes

แต่ Collation ที่เลือกควรสอดคล้องกับ Database Version และ Architecture จริง

⑨ ควรใช้ root เชื่อม FiveM ไหม

ไม่แนะนำสำหรับ Production

แม้ตัวอย่างทั่วไปจะเห็น

root

แต่ควรสร้าง Database User เฉพาะสำหรับ FiveM

เช่น

fivem_user

แล้วให้สิทธิ์เฉพาะ Database ที่ต้องใช้

⑩ สร้าง Database User

ตัวอย่าง MariaDB/MySQL

CREATE USER
'fivem_user'@'localhost'
IDENTIFIED BY
'CHANGE_THIS_PASSWORD';

จากนั้นให้ Permission

GRANT ALL PRIVILEGES
ON fivem.*
TO 'fivem_user'@'localhost';

แล้ว

FLUSH PRIVILEGES;

สำหรับ Production ควรพิจารณาหลัก Least Privilege ให้เหมาะกับสิ่งที่ Resource ต้องทำจริง

⑪ ทำไมไม่ควรใช้ root

เพราะ root มีสิทธิ์สูงมาก

หาก Credential หลุดหรือ Script มีช่องโหว่ ผลกระทบอาจเกิน Database FiveM หนึ่งชุด

ดีกว่าใช้

fivem_user
↓
เข้าถึงเฉพาะ
fivem.*

เพื่อลดขอบเขตความเสียหาย

⑫ Remote Database User ต่างจาก localhost

ถ้า FXServer กับ Database คนละเครื่อง

User

'fivem_user'@'localhost'

อาจไม่อนุญาต Remote Connection

ต้องตั้ง Host Rule ตาม Network Architecture

เช่น Database ที่รับจาก Private IP เฉพาะเครื่อง FXServer

ไม่ควรใช้

%

แบบเปิดกว้างโดยไม่เข้าใจผลด้าน Security

⑬ ทดสอบ Database ก่อน oxmysql

ก่อนโทษ oxmysql ควรยืนยันว่า Database ใช้งานได้จริง

ทดสอบ Login ด้วย Database Client เช่น

MariaDB client
MySQL client
HeidiSQL
DBeaver

โดยใช้ Credential เดียวกับที่ FiveM จะใช้

ถ้า Login จาก Database Client ยังไม่ได้ oxmysql ก็ไม่น่าจะแก้ปัญหานั้นให้

⑭ ติดตั้ง oxmysql ที่ไหน

นำ Resource oxmysql ไปไว้ใต้

resources/

เช่น

resources/
└── [standalone]/
    └── oxmysql/
        ├── fxmanifest.lua
        ├── dist/
        ├── lib/
        └── ...

ชื่อ Resource ควรเป็น

oxmysql

ตามที่ Scripts และ Dependencies อ้าง

⑮ ระวัง Folder ซ้อนหลังแตก ZIP

ผิด

resources/
└── oxmysql/
    └── oxmysql/
        ├── fxmanifest.lua
        └── ...

ควรเป็น

resources/
└── oxmysql/
    ├── fxmanifest.lua
    └── ...

ให้ fxmanifest.lua อยู่ Root ของ Resource oxmysql

⑯ ตรวจว่า FXServer เห็น oxmysql

ถ้าเพิ่มระหว่าง Server ทำงาน

ใช้

refresh

จากนั้น

ensure oxmysql

ถ้า Console แจ้งว่าไม่พบ Resource ให้ตรวจ Folder/Manifest ก่อน Connection String

⑰ ตั้ง mysql_connection_string ตรงไหน

ตั้งใน

server.cfg

และต้องตั้ง ก่อน Start oxmysql

ตัวอย่าง

set mysql_connection_string "mysql://fivem_user:password@127.0.0.1:3306/fivem"

ensure oxmysql

อย่ากลับลำดับเป็น

ensure oxmysql

set mysql_connection_string "..."

เพราะ oxmysql ต้องอ่าน Configuration ตอนเริ่มทำงาน

⑱ Connection String แบบ URI

รูปแบบพื้นฐาน

mysql://USER:PASSWORD@HOST:PORT/DATABASE

ตัวอย่าง

set mysql_connection_string "mysql://fivem_user:MyPassword@127.0.0.1:3306/fivem"

แยกเป็น

USER
→ fivem_user

PASSWORD
→ MyPassword

HOST
→ 127.0.0.1

PORT
→ 3306

DATABASE
→ fivem

⑲ Connection String แบบ Semicolon

oxmysql รองรับอีกรูปแบบ เช่น

set mysql_connection_string "user=fivem_user;password=MyPassword;host=127.0.0.1;port=3306;database=fivem"

มีประโยชน์หาก URI Format มีปัญหากับ Character บางตัวใน Password

⑳ Password มีอักขระพิเศษต้องระวัง

เอกสาร oxmysql เตือนว่า Connection String บาง Format มี Reserved/Special Characters ที่อาจ Parse ไม่ตามที่คาด เช่นกลุ่ม

;
,
/
?
:
@
&
=
+
$
#

หาก Connection String ดูถูกต้องแต่ Parse ไม่ผ่าน ให้ตรวจ Password หรือทดลอง Connection String อีก Format

ทางที่ดีคือสร้าง Password ที่แข็งแรงแต่ไม่สร้าง Parsing Ambiguity กับ Format ที่เลือก

㉑ ไม่ควรเผย Connection String

ห้ามโพสต์

mysql://realuser:realpassword@host/database

ลง

Discord
Forum
GitHub
Screenshot
Public config

เพราะ Connection String มี Credential

หากเคยเปิดเผยแล้วควรเปลี่ยน Password ไม่ใช่แค่ลบข้อความ

㉒ set กับ setr ใช้อะไร

oxmysql Documentation ระบุให้ตั้ง Connection String ด้วย

set mysql_connection_string "..."

ใช้ set

ไม่ใช่การ Replicate Credential ไป Client

Database Credentials ต้องเป็น Server-side Configuration

㉓ server.cfg พื้นฐานควรเรียงอย่างไร

ตัวอย่าง

# Database
set mysql_connection_string "mysql://fivem_user:password@127.0.0.1:3306/fivem"

# Database connector
ensure oxmysql

# Libraries
ensure ox_lib

# Framework
ensure my_core

# Gameplay
ensure my_inventory
ensure my_garage

หลักคือ

Configuration
↓
Database Connector
↓
Core/Libraries
↓
Gameplay Resources

㉔ ตรวจ oxmysql Start สำเร็จอย่างไร

ดู FXServer Console

หาก Connection มีปัญหา oxmysql มักแสดง Error ที่เกี่ยวกับ

Authentication
Host
Database
Connection refused
Connection string
Unknown database
Access denied

อ่านข้อความแรกที่เกี่ยวข้องกับ Database

อย่า Restart ซ้ำโดยไม่อ่าน

㉕ Access denied หมายถึงอะไร

มักเกี่ยวกับ

Username ผิด
Password ผิด
Host Permission ผิด
Database User ไม่มีสิทธิ์

เช่น User ถูกสร้างเฉพาะ

localhost

แต่ Connection เกิดจาก Host อื่น

Database จะปฏิเสธ

㉖ Unknown database คืออะไร

ถ้า Error บอกว่าไม่รู้จัก Database

ตรวจ Connection String เช่น

/fivem

แล้วตรวจว่ามี Database

SHOW DATABASES;

จริงหรือไม่

อย่าสร้าง Tables ใน Database ชื่อหนึ่งแต่ Connection ไปอีกชื่อ

㉗ Connection refused คืออะไร

มักหมายถึง FXServer ติดต่อ Database Service ที่ Host/Port นั้นไม่ได้

ตรวจ

Database Service ทำงาน?
Host ถูก?
Port ถูก?
Firewall?
Bind address?
Docker/Container network?

แตกต่างจาก Access denied ซึ่งหมายถึงไปถึง Database แล้วแต่ Authentication ไม่ผ่าน

㉘ Timeout คืออะไร

ถ้า Connection Timeout

อาจเกี่ยวกับ

Network Route
Firewall
Database Host
Cloud Security Group
Port
Database Service

โดยเฉพาะ Database อยู่คนละ Server

ควรตรวจ Network ก่อนแก้ SQL Script

㉙ สร้าง Resource ทดสอบ Database

สร้าง

resources/
└── [local]/
    └── db_test/
        ├── fxmanifest.lua
        └── server.lua

เพื่อแยก Database Connection ออกจาก Framework ขนาดใหญ่

นี่เป็นวิธี Debug ที่ดีมาก

㉚ fxmanifest.lua สำหรับ db_test

fx_version 'cerulean'
game 'gta5'

author 'comsiam'
description 'oxmysql connection test'
version '1.0.0'

dependency 'oxmysql'

server_script '@oxmysql/lib/MySQL.lua'
server_script 'server.lua'

สองส่วนสำคัญคือ

dependency 'oxmysql'

และ

server_script '@oxmysql/lib/MySQL.lua'

㉛ ทำไม import MySQL.lua

@oxmysql/lib/MySQL.lua ให้ Lua Resource ใช้ Global API

MySQL

เช่น

MySQL.query.await(...)
MySQL.single.await(...)
MySQL.scalar.await(...)

ได้สะดวก

ควรประกาศก่อน Server Scripts ที่ใช้งานมัน

㉜ ทดสอบ SELECT 1

server.lua

CreateThread(function()

    local result =
        MySQL.scalar.await(
            'SELECT 1'
        )

    print(
        '[db_test] SELECT 1 =',
        result
    )
end)

ถ้า Console แสดง

[db_test] SELECT 1 = 1

Database Query พื้นฐานทำงาน

㉝ ทำไม SELECT 1 เหมาะกับการ Test

เพราะไม่ต้องพึ่ง

Table
Column
Schema ของ Framework

ถ้า

SELECT 1

ยังไม่ได้ ปัญหาน่าจะอยู่ Connection/oxmysql/Database Layer

ถ้า SELECT 1 ผ่านแต่ Query Character ไม่ผ่าน ปัญหาค่อยอยู่ Schema หรือ SQL Query

㉞ Start db_test อย่างไร

ถ้าเพิ่ม Resource ใหม่

refresh
ensure db_test

จากนั้นดู Console

อย่าทดสอบผ่าน Client Event ก่อน

ทดสอบ Server→Database ตรง ๆ ให้ผ่านก่อน

㉟ ทดสอบชื่อ Database

สามารถ Query

local database =
    MySQL.scalar.await(
        'SELECT DATABASE()'
    )

print(
    'Current database:',
    database
)

ช่วยยืนยันว่า Connection กำลังใช้ Database ตัวไหนจริง

㊱ สร้าง Table สำหรับ Test

ตัวอย่าง

CREATE TABLE test_players (
    id INT UNSIGNED
        NOT NULL
        AUTO_INCREMENT,

    name VARCHAR(100)
        NOT NULL,

    PRIMARY KEY (id)
);

ตอนนี้มี Table สำหรับทดสอบ CRUD

㊲ ทดสอบ INSERT

local id =
    MySQL.insert.await(
        [[
            INSERT INTO test_players (
                name
            )
            VALUES (?)
        ]],
        {
            'Somchai'
        }
    )

print(
    'Inserted ID:',
    id
)

ถ้าได้ ID เช่น

1

แสดงว่า INSERT ใช้งานได้

㊳ ทดสอบ SELECT

local player =
    MySQL.single.await(
        [[
            SELECT
                id,
                name
            FROM test_players
            WHERE id = ?
            LIMIT 1
        ]],
        {
            1
        }
    )

if player then

    print(
        player.id,
        player.name
    )
end

ใช้ single เพราะต้องการเพียง Row เดียว

㊴ ทดสอบ UPDATE

local affectedRows =
    MySQL.update.await(
        [[
            UPDATE test_players
            SET name = ?
            WHERE id = ?
        ]],
        {
            'Mana',
            1
        }
    )

print(
    'Updated rows:',
    affectedRows
)

ถ้าได้ค่าอย่าง

1

แสดงว่ามี Row ถูก Update

㊵ ทดสอบ DELETE

ตัวอย่าง

local affectedRows =
    MySQL.update.await(
        [[
            DELETE FROM test_players
            WHERE id = ?
        ]],
        {
            1
        }
    )

print(
    'Deleted rows:',
    affectedRows
)

ใช้ Test Table เท่านั้น อย่าทดสอบ DELETE กับ Production Player Data

㊶ ทำไมต้องใช้ ?

ตัวอย่าง

WHERE id = ?

แล้ว Parameters

{
    playerId
}

ทำให้ SQL Structure กับ Values แยกกัน

เป็น Pattern ที่ปลอดภัยกว่าการ Concatenate Input ลง SQL

㊷ ห้ามทำ SQL แบบนี้

ไม่ควร

local sql =
    "SELECT * FROM users WHERE name = '"
    .. playerName
    .. "'"

โดยเฉพาะ playerName มาจาก Client

ควรเป็น

MySQL.single.await(
    'SELECT * FROM users WHERE name = ? LIMIT 1',
    {
        playerName
    }
)

㊸ Parameterized Query แทน Server Validation ไหม

ไม่

สมมติ Client ส่ง

vehicleId = 500

แม้ใช้

WHERE id = ?

อย่างปลอดภัย Server ยังต้องตรวจว่า Vehicle 500 เป็นของ Player หรือไม่

ดังนั้น

Parameterized SQL
→ ป้องกัน Database Input Layer

ส่วน

Server Validation
→ ป้องกัน Gameplay/Authorization Layer

ต้องมีทั้งคู่

㊹ Resource จริงควรประกาศ dependency

เช่น Garage

fx_version 'cerulean'
game 'gta5'

dependency 'oxmysql'

server_script '@oxmysql/lib/MySQL.lua'

server_scripts {
    'server/database.lua',
    'server/main.lua'
}

เปิด Manifest แล้วเห็นทันทีว่า Garage ต้องใช้ Database Connector

㊺ Start Order ของ Resource จริง

server.cfg

set mysql_connection_string "mysql://fivem_user:password@127.0.0.1:3306/fivem"

ensure oxmysql

ensure my_core
ensure my_inventory
ensure my_garage

ถ้า my_core พึ่ง oxmysql ก็ควรอยู่หลัง oxmysql

㊻ อย่าแก้ Start Order ด้วย Wait

ผิด

CreateThread(function()

    Wait(5000)

    MySQL.query.await(
        'SELECT 1'
    )
end)

เพียงเพราะ Database Resource Start ไม่ทัน

ควรแก้

Dependency
Start order
Connection
Initialization

ให้ถูกต้อง

㊼ Database Ready ควรออกแบบอย่างไร

อย่า Query ก่อน Database Layer พร้อมโดยไม่มีเหตุผล

Resource ที่มี Dependency ถูกต้องและ oxmysql Start ก่อนจะลดปัญหานี้มาก

สำหรับ Data Initialization จำนวนมาก อาจสร้าง Function

local function loadData()

    local rows =
        MySQL.query.await(
            'SELECT * FROM garages'
        )

    -- Build cache
end

แล้วเรียกใน Resource Initialization ที่เหมาะสม

㊽ เปิด Slow Query Warning

สามารถตั้ง Threshold

set mysql_slow_query_warning 150

oxmysql จะเตือน Query ที่ใช้เวลานานเกิน Threshold ที่กำหนด

มีประโยชน์มากเมื่อ Server เริ่มมี Players และ Tables ใหญ่

㊾ Slow Query Warning ไม่ได้แปลว่า SQL พัง

Query Time สามารถได้รับผลจาก

Server Hitch
Database Load
Disk
Network
Startup Load
Lock
Large Result

ดังนั้น Warning คือจุดเริ่มตรวจ

ไม่ใช่คำตอบสุดท้าย

㊿ เปิด mysql_debug

ระหว่าง Development สามารถใช้

set mysql_debug true

เพื่อช่วยดู Query Activity ตามที่ oxmysql รองรับ

แต่อย่าเปิด Logging หนักโดยไม่จำเป็นตลอด Production

เพราะอาจสร้าง Log Noise จำนวนมาก

51 วิธี Debug ถ้า SELECT 1 ไม่ผ่าน

ใช้ลำดับ

① oxmysql Resource มีจริง?
② oxmysql Start ผ่าน?
③ mysql_connection_string ถูก?
④ Database Service Running?
⑤ Host ถูก?
⑥ Port ถูก?
⑦ Database Name ถูก?
⑧ Username ถูก?
⑨ Password ถูก?
⑩ User Host Permission ถูก?
⑪ Firewall เปิดหรือไม่?
⑫ Database User มี Permission หรือไม่?

อย่าไปแก้ server.lua ก่อนถ้า Connection ยังไม่ผ่าน

52 วิธี Debug ถ้า SELECT 1 ผ่าน แต่ Query จริงไม่ผ่าน

ถ้า

SELECT 1

ผ่าน แสดงว่า Connection Layer ดีในระดับพื้นฐาน

ตรวจต่อ

Table มีไหม?
Column มีไหม?
ชื่อ Database ถูกไหม?
SQL Syntax ถูกไหม?
Reserved keyword?
Parameter ถูกไหม?
Data Type ตรงไหม?

ตอนนี้ปัญหาอยู่ Schema/Query Layer มากกว่า Connection

53 Table doesn't exist แก้อย่างไร

ตรวจ

SHOW TABLES;

และตรวจว่า Script ต้องการชื่ออะไร

เช่น Script Query

SELECT *
FROM owned_vehicles

แต่ Database มี

vehicles

ก็ไม่ใช่ Table เดียวกัน

ต้อง Import Schema ที่ Resource ต้องการหรือแก้ Query ตาม Architecture

54 Unknown column แก้อย่างไร

ตัวอย่าง

Unknown column 'garage'

หมายถึง SQL กำลังอ้าง Column ที่ไม่มีใน Table ที่ Database ใช้อยู่

อาจเกิดจาก

SQL migration ยังไม่ได้ Import
Resource ใหม่กว่า Schema
Schema ใหม่กว่า Script
เลือก Database ผิด

ไม่ใช่ปัญหา Connection String แล้ว

55 Reserved Keyword มีผลไหม

มี โดยเฉพาะ Resource รุ่นเก่ากับ MySQL รุ่นใหม่

คำอย่าง

group
stored

อาจมี Compatibility Considerations ตาม Version/SQL Mode

นี่เป็นหนึ่งในเหตุผลที่ oxmysql Documentation แนะนำ MariaDB เพื่อ Compatibility กับ FiveM Resources จำนวนมาก

56 Database อยู่ Remote Server ต้องทำอะไรเพิ่ม

ตรวจ

Database bind address
Firewall
Cloud security rules
Database user host
Private networking
Latency
TLS ตาม Environment

ถ้าเป็น VPS สองเครื่องใน Private Network ควรใช้ Private IP เมื่อเหมาะสม แทนเปิด Database สู่ Public Internet

57 Database User ต้องมี Permission อะไร

ขึ้นกับ Resource

Gameplay Resource ทั่วไปอาจต้อง

SELECT
INSERT
UPDATE
DELETE

Framework Installer หรือ Migration อาจต้องเพิ่ม

CREATE
ALTER
INDEX

ถ้าต้องสร้าง/เปลี่ยน Schema

หลักคือให้สิทธิ์เท่าที่ระบบต้องใช้จริง

58 Backup ก่อน Import SQL

ก่อน Import Script SQL ลง Production Database ควร Backup

เพราะไฟล์ SQL อาจมี

DROP TABLE
ALTER TABLE
DELETE
TRUNCATE

อย่า Import โดยไม่อ่าน

โดยเฉพาะ SQL จาก Resource ที่ไม่รู้ที่มา

59 Checklist เชื่อม FiveM กับ MySQL/MariaDB

ตรวจให้ครบ

① MariaDB/MySQL ติดตั้งแล้ว?
② Database Service Running?
③ สร้าง Database แล้ว?
④ Character Set เหมาะสม?
⑤ สร้าง Dedicated User แล้ว?
⑥ Password ปลอดภัย?
⑦ User มี Permission?
⑧ Host Permission ถูก?
⑨ oxmysql อยู่ใน resources?
⑩ Folder oxmysql ไม่ซ้อน?
⑪ FXServer เห็น oxmysql?
⑫ mysql_connection_string ถูก?
⑬ ใช้ set ไม่ใช่ Replicated Credential?
⑭ Connection String อยู่ก่อน oxmysql?
⑮ Host ถูก?
⑯ Port ถูก?
⑰ Database Name ถูก?
⑱ Special Character ทำให้ Parse มีปัญหาหรือไม่?
⑲ oxmysql Start ผ่าน?
⑳ Consumer Start หลัง oxmysql?
㉑ Resource มี dependency 'oxmysql'?
㉒ MySQL.lua ถูก Import ก่อน Server Scripts?
㉓ SELECT 1 ผ่าน?
㉔ SELECT DATABASE() ถูก Database?
㉕ INSERT ผ่าน?
㉖ SELECT ผ่าน?
㉗ UPDATE ผ่าน?
㉘ Queries ใช้ Parameters?
㉙ Client Input ถูก Validate?
㉚ Backup Database แล้ว?

⑥⓪ ตัวอย่าง Setup FiveM + MariaDB + oxmysql แบบครบ

สมมติ Database คือ

com_fivem

และ Database User

com_server

สร้าง Database

CREATE DATABASE com_fivem
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;

สร้าง User

CREATE USER
'com_server'@'localhost'
IDENTIFIED BY
'CHANGE_THIS_PASSWORD';

ให้ Permission

GRANT ALL PRIVILEGES
ON com_fivem.*
TO 'com_server'@'localhost';

FLUSH PRIVILEGES;

server.cfg

set mysql_connection_string "mysql://com_server:CHANGE_THIS_PASSWORD@127.0.0.1:3306/com_fivem"

set mysql_slow_query_warning 150

ensure oxmysql
ensure com_database_test

Resource Structure

com_database_test/
├── fxmanifest.lua
└── server/
    └── main.lua

fxmanifest.lua

fx_version 'cerulean'
game 'gta5'

author 'comsiam'
description 'FiveM database connection test'
version '1.0.0'

dependency 'oxmysql'

server_script '@oxmysql/lib/MySQL.lua'

server_script 'server/main.lua'

server/main.lua

CreateThread(function()

    local test =
        MySQL.scalar.await(
            'SELECT 1'
        )

    if test ~= 1 then

        print(
            '[DB TEST] Database test failed'
        )

        return
    end

    local database =
        MySQL.scalar.await(
            'SELECT DATABASE()'
        )

    print(
        '[DB TEST] Connection OK'
    )

    print(
        '[DB TEST] Database:',
        database
    )
end)

เมื่อ Start

refresh
ensure com_database_test

ถ้าทุกอย่างถูกต้องควรได้ Concept

oxmysql
↓
Connected
↓
SELECT 1
↓
1
↓
SELECT DATABASE()
↓
com_fivem

ตอนนี้จึงค่อยเริ่มสร้าง Tables และ Resource จริง

ตัวอย่าง Resource Garage เชื่อม Database

fxmanifest.lua

fx_version 'cerulean'
game 'gta5'

dependency 'oxmysql'

server_script '@oxmysql/lib/MySQL.lua'

server_scripts {
    'server/database.lua',
    'server/main.lua'
}

server/database.lua

Database = {}

function Database.getVehicles(
    characterId
)

    return MySQL.query.await(
        [[
            SELECT
                id,
                plate,
                model,
                stored
            FROM vehicles
            WHERE character_id = ?
        ]],
        {
            characterId
        }
    )
end

server/main.lua

RegisterNetEvent(
    'garage:getVehicles',
    function()

        local src =
            source

        local characterId =
            GetCharacterId(src)

        if not characterId then
            return
        end

        local vehicles =
            Database.getVehicles(
                characterId
            )

        TriggerClientEvent(
            'garage:vehicles',
            src,
            vehicles
        )
    end
)

Architecture คือ

Client
↓
garage:getVehicles
↓
Server
↓
หา Character ID
↓
Database.getVehicles
↓
oxmysql
↓
MariaDB
↓
Rows
↓
Server
↓
Client

Client ไม่ได้เข้าถึง Database โดยตรง

อย่าเปิด Generic SQL Event ให้ Client

ห้ามออกแบบ

RegisterNetEvent(
    'database:query',
    function(sql)

        MySQL.query.await(
            sql
        )
    end
)

เพราะเท่ากับให้ Client ส่ง SQL มายัง Database Server

ควรออกแบบ Event เป็น Business Action

เช่น

garage:getVehicles
shop:buyItem
character:create

และให้ Server เป็นผู้กำหนด SQL

อย่าเก็บ Connection String ใน Shared Script

ไม่ควร

Config.Database =
    'mysql://user:password@host/database'

ใน

shared/config.lua

เพราะ Shared Script ถูก Client โหลด

Connection String ต้องอยู่ Server Configuration

mysql_connection_string ต้องมาก่อน oxmysql

จำลำดับนี้ให้แม่น

server.cfg
↓
mysql_connection_string
↓
oxmysql
↓
Core
↓
Gameplay Resources

ไม่ใช่

Gameplay Resource
↓
oxmysql
↓
Connection String

เพราะ Database Layer ต้องพร้อมก่อน Consumer เรียกใช้งาน

คำถามที่พบบ่อยเกี่ยวกับการเชื่อม FiveM กับ MySQL/MariaDB

FiveM เชื่อม MySQL อย่างไร

ใช้ Database Connector เช่น oxmysql ระหว่าง Server Scripts กับ MySQL/MariaDB

oxmysql เป็น Database ไหม

ไม่ เป็น Connector

ต้องติดตั้ง MariaDB ด้วยไหม

ต้องมี MySQL-compatible Database Server ที่ oxmysql เชื่อมได้

MariaDB หรือ MySQL ดีกว่าสำหรับ FiveM

oxmysql Documentation แนะนำ MariaDB สำหรับ Compatibility กับ FiveM Resources จำนวนมาก ขณะที่ MySQL 8 ก็รองรับ

ต้องใช้ XAMPP ไหม

ไม่ oxmysql Documentation ไม่แนะนำ XAMPP เพียงเพื่อทำ Database Server

mysql_connection_string อยู่ตรงไหน

โดยทั่วไปอยู่ใน server.cfg

Connection String ต้องอยู่ก่อน oxmysql ไหม

ใช่ ควรกำหนดก่อน Start Resource ที่ใช้ Database

รูปแบบ Connection String เป็นอย่างไร

ตัวอย่าง

set mysql_connection_string "mysql://user:password@127.0.0.1:3306/fivem"

ใช้ Semicolon Format ได้ไหม

ได้ เช่น

set mysql_connection_string "user=user;password=password;host=127.0.0.1;port=3306;database=fivem"

Password มี @ แล้วต่อไม่ได้ทำอย่างไร

อักขระบางชนิดมีความหมายพิเศษใน Connection String Format ให้เปลี่ยน Format หรือใช้ Password ที่ไม่สร้าง Parsing Conflict ตามแนวทาง Documentation

oxmysql ต้อง ensure ก่อน Framework ไหม

ถ้า Framework ใช้ Database ควรให้ oxmysql พร้อมก่อน

fxmanifest.lua ต้องเขียนอะไร

ตัวอย่าง

dependency 'oxmysql'

server_script '@oxmysql/lib/MySQL.lua'

ทดสอบ Connection อย่างไร

ใช้

SELECT 1

ผ่าน MySQL.scalar.await

SELECT 1 ได้ 1 หมายความว่าอะไร

แสดงว่า Server Resource สามารถส่ง Database Query ผ่าน oxmysql ได้ในระดับพื้นฐาน

ตรวจชื่อ Database ที่เชื่อมอยู่ได้ไหม

ได้ด้วย

SELECT DATABASE()

Access denied แก้อย่างไร

ตรวจ Username, Password, Host Permission และ Database Privileges

Unknown database แก้อย่างไร

ตรวจว่าชื่อ Database ใน Connection String มีอยู่จริง

Connection refused แก้อย่างไร

ตรวจ Database Service, Host, Port, Firewall และ Network

Table doesn't exist เป็นปัญหา oxmysql ไหม

ไม่จำเป็น Connection อาจทำงานแล้วแต่ Database Schema ไม่มี Table ที่ Script ต้องการ

Unknown column คืออะไร

SQL กำลังเรียก Column ที่ไม่มีหรือ Schema Version ไม่ตรงกับ Resource

Database Remote ต้องเปิด 3306 สาธารณะไหม

ไม่ควรหากหลีกเลี่ยงได้ ควรใช้ Private Network/Firewall Rules ที่จำกัดแหล่งเชื่อมต่อ

ใช้ root เชื่อม Database ได้ไหม

ทำได้ทางเทคนิคในหลายระบบ แต่ไม่แนะนำสำหรับ Production ควรสร้าง Dedicated User

Database Password ใส่ใน client.lua ได้ไหม

ไม่ได้และไม่ควรอย่างยิ่ง

Resource ต้องใช้ Placeholder ไหม

ควรใช้ Parameterized Queries แทนการ Concatenate Untrusted Values ลง SQL

oxmysql ป้องกัน Cheat ไหม

ไม่ Server-side Validation ยังจำเป็น

Slow Query Warning เปิดอย่างไร

ตัวอย่าง

set mysql_slow_query_warning 150

mysql_debug เปิดอย่างไร

ตัวอย่าง

set mysql_debug true

ใช้เมื่อ Debug ตามความจำเป็น

สรุปวิธีเชื่อม FiveM กับ MySQL หรือ MariaDB ด้วย oxmysql

ขั้นตอนทั้งหมดสามารถสรุปเป็น

① ติดตั้ง MariaDB/MySQL
↓
② สร้าง Database
↓
③ สร้าง Database User
↓
④ กำหนด Permission
↓
⑤ ติดตั้ง oxmysql
↓
⑥ ตั้ง mysql_connection_string
↓
⑦ Start oxmysql
↓
⑧ Import MySQL.lua ใน Resource
↓
⑨ ประกาศ dependency
↓
⑩ ทดสอบ SELECT 1
↓
⑪ ทดสอบ SELECT DATABASE()
↓
⑫ ค่อยสร้างระบบจริง

ตัวอย่าง Configuration ขั้นต่ำ

set mysql_connection_string "mysql://fivem_user:password@127.0.0.1:3306/fivem"

ensure oxmysql
ensure my_resource

Manifest

fx_version 'cerulean'
game 'gta5'

dependency 'oxmysql'

server_script '@oxmysql/lib/MySQL.lua'
server_script 'server.lua'

และ Test

local result =
    MySQL.scalar.await(
        'SELECT 1'
    )

print(result)

ถ้าได้

1

จึงค่อยพัฒนา Query จริง เช่น

Characters
Vehicles
Inventory
Money
Properties

สิ่งสำคัญที่สุดคืออย่า Debug ทุก Layer พร้อมกัน

ถ้า Connection ยังไม่ผ่าน ให้แก้

Database
Credential
Host
Port
Connection String
oxmysql

ก่อน

ถ้า SELECT 1 ผ่านแล้วแต่ vehicles Query พัง ค่อยแก้

Schema
Table
Column
SQL
Parameters

สำหรับ Developer ที่เรียน FiveM กับ comsiam วิธีนี้จะช่วยลดเวลา Debug ได้มาก เพราะเรารู้แน่นอนว่าปัญหาอยู่ Connection Layer หรือ Application Layer

อีกหลักสำคัญจาก comsiam คือ Database ต้องอยู่หลัง Server Authority เสมอ:

Client Request
↓
Server Validate
↓
Server Query
↓
Database

ไม่ใช่ให้ Client เลือก SQL, Table หรือค่าที่มีผลต่อ Economy แล้วส่งให้ Database บันทึกตรง ๆ

หัวข้อถัดไปคือ FiveM Database Connection Failed แก้อย่างไร ซึ่งจะเจาะ Error จริง เช่น Access denied, ECONNREFUSED, Unknown database, Timeout, Password มีอักขระพิเศษ, Host Permission, Port 3306 และวิธีแยกว่า Error อยู่ที่ FXServer, oxmysql, Network หรือ MariaDB

Comments

Popular posts from this blog

FiveM ยังน่าเล่นไหม? Enhanced เปลี่ยน FiveM แค่ไหน

FiveM คืออะไร เล่นอย่างไร สำหรับมือใหม่ เริ่มต้นตั้งแต่ศูนย์

วิธีตั้ง Admin Permission ด้วย add_ace และ add_principal FiveM แบบละเอียด