├── .eslintrc.json ├── .gitignore ├── LICENSE ├── README.md ├── actions └── fivem.ts ├── app ├── brokenArtifacts.tsx ├── check │ └── route.ts ├── globals.css ├── json │ └── route.ts ├── layout.tsx └── page.tsx ├── db.json ├── next.config.mjs ├── package-lock.json ├── package.json ├── postcss.config.mjs ├── tailwind.config.ts └── tsconfig.json /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next/core-web-vitals", "next/typescript"] 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FiveM Artifacts DB 2 | 3 | A simple website that keeps a **log of FiveM artifacts with known issues**; and provides a download link to the most recent artifacts if no issues have been reported with it. 4 | 5 | ## Contributing 6 | 7 | Make a pull request (PR) to update [`db.json`](https://github.com/jgscripts/fivem-artifacts-db/blob/main/db.json) (in the root of this repository), with the artifact number and an explanation of what is wrong with it. We will review it ASAP and merge your contribution which will automatically update the site. 8 | 9 | If you are not comfortable making a PR, could you also [create an issue](https://github.com/jgscripts/fivem-artifacts-db/issues) or send an email to [hello@jgscripts.com](mailto:hello@jgscripts.com) with details of the artifact bug. We will review your report and update the database. 10 | 11 | Please provide some evidence which shows what is broken - a link to a Cfx GitHub issue, screenshots or error logs would be ideal. 12 | 13 | ## API 14 | 15 | - **`/check?artifact=XXXX`: Check specific artifact to see if it has a reported issue** 16 | 17 | **Response** 18 | 19 | | **Name** | **Type** | 20 | | -------- | --------------------------- | 21 | | `status` | `"OK"` or `"BROKEN"` | 22 | | `reason` | `string` (only if "BROKEN") | 23 | 24 | - **`/json`: Full site in JSON format (all broken artifacts, download links, recommend stable artifacts)** 25 | 26 | **Response** 27 | 28 | | **Name** | **Type** | 29 | | --------------------- | -------- | 30 | | `recommendedArtifact` | `string` | 31 | | `windowsDownloadLink` | `string` | 32 | | `linuxDownloadLink` | `string` | 33 | | `brokenArtifacts` | `object` | 34 | 35 | ## FiveM Resource 36 | 37 | Want to check your FXServer version hasn't had reported issues every time you start your server? I've written a small standalone script that does just that: https://github.com/jgscripts/jg-artifactcheck 38 | 39 | You can steal the code to implement in your resources, or use it directly. 40 | 41 | ## Website & Sharing 42 | 43 | You can share this site with people to help them download the right artifacts here: https://artifacts.jgscripts.com/ 44 | 45 | If you want to make an improvement to the site itself, feel free to submit a PR with that too :) 46 | 47 | ## Legal 48 | 49 | The code and all database data within this repo is released into the public domain. You can read the full license here: [LICENSE](./LICENSE). 50 | 51 | "FiveM" is a copyright and registered trademark of Take-Two Interactive Software, Inc. 52 | -------------------------------------------------------------------------------- /actions/fivem.ts: -------------------------------------------------------------------------------- 1 | import artifactDb from "@/db.json"; 2 | 3 | const GITHUB_REPO_TAGS = 4 | "https://api.github.com/repos/citizenfx/fivem/tags?per_page=50"; 5 | const DOWNLOAD_LINK_BASE = "https://runtime.fivem.net/artifacts/fivem"; 6 | const WINDOWS_MASTER = "build_server_windows/master"; 7 | const WINDOWS_FILE = "server.zip"; 8 | const LINUX_MASTER = "build_proot_linux/master"; 9 | const LINUX_FILE = "fx.tar.xz"; 10 | 11 | type ReturnType = 12 | | { 13 | recommendedArtifact: string; 14 | windowsDownloadLink: string; 15 | linuxDownloadLink: string; 16 | } 17 | | false; 18 | 19 | export function getAllBrokenArtifacts(): { [key: string]: string } { 20 | const brokenArtifacts = JSON.parse( 21 | JSON.stringify(artifactDb.brokenArtifacts) 22 | ); // it's a really fast deep clone, it looks ugly i know 23 | 24 | for (const [artifact, reason] of Object.entries(brokenArtifacts)) { 25 | if (artifact.includes("-")) { 26 | const artifactRange = artifact.split("-").map((a) => parseInt(a)); 27 | 28 | for (let i = artifactRange[0]; i <= artifactRange[1]; i++) { 29 | brokenArtifacts[i.toString()] = reason; 30 | } 31 | 32 | delete brokenArtifacts[artifact]; 33 | } 34 | } 35 | 36 | return brokenArtifacts; 37 | } 38 | 39 | export async function getRecommendedArtifact(): Promise { 40 | try { 41 | const brokenArtifacts = getAllBrokenArtifacts(); 42 | if (!brokenArtifacts) return false; 43 | 44 | // Get git commit sha from tag 45 | const gitReq = await fetch(GITHUB_REPO_TAGS, { 46 | next: { revalidate: 86400 }, 47 | }); 48 | if (!gitReq.ok) return false; 49 | const gitData: { name: string; commit: { sha: string } }[] = 50 | await gitReq.json(); 51 | 52 | // Filter tags for just automated releases, and convert to {[name]: sha} 53 | const latestReleases = gitData 54 | .filter(({ name }) => name.startsWith("v1.0.0.")) 55 | .map(({ name, commit }) => ({ 56 | artifact: name.split("v1.0.0.")[1], 57 | sha: commit.sha, 58 | })); 59 | 60 | // Go through the most recent artifacts and find one without an entry in brokenArtifacts 61 | let recommendedArtifact: { artifact: string; sha: string } = 62 | latestReleases[0]; 63 | for (const artifact of latestReleases) { 64 | if (!brokenArtifacts[artifact.artifact]) { 65 | recommendedArtifact = artifact; 66 | break; 67 | } 68 | } 69 | 70 | // Generate download link 71 | const windowsDownloadLink = `${DOWNLOAD_LINK_BASE}/${WINDOWS_MASTER}/${recommendedArtifact.artifact}-${recommendedArtifact.sha}/${WINDOWS_FILE}`; 72 | const linuxDownloadLink = `${DOWNLOAD_LINK_BASE}/${LINUX_MASTER}/${recommendedArtifact.artifact}-${recommendedArtifact.sha}/${LINUX_FILE}`; 73 | 74 | return { 75 | recommendedArtifact: recommendedArtifact.artifact, 76 | windowsDownloadLink, 77 | linuxDownloadLink, 78 | }; 79 | } catch { 80 | return false; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /app/brokenArtifacts.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useMemo, useState } from "react"; 4 | import artifactDb from "@/db.json"; 5 | 6 | const BrokenArtifacts = () => { 7 | const [searchQuery, setSearchQuery] = useState(""); 8 | 9 | const filteredArtifacts = useMemo(() => { 10 | return Object.entries(artifactDb.brokenArtifacts) 11 | .sort((a, b) => parseInt(b[0]) - parseInt(a[0])) 12 | .filter(([key]) => { 13 | if (!searchQuery) return true; 14 | if (searchQuery === key) return true; 15 | 16 | if (key.includes("-")) { 17 | const queryAsNumber = parseInt(searchQuery); 18 | if (!queryAsNumber) return false; 19 | 20 | const range = key.split("-").map((a) => parseInt(a)); 21 | if (queryAsNumber >= range[0] && queryAsNumber <= range[1]) 22 | return true; 23 | } 24 | 25 | return false; 26 | }); 27 | }, [searchQuery]); 28 | 29 | return ( 30 |
31 |
32 | 33 | 34 | 35 | 41 | 42 | 43 | 44 | 45 | setSearchQuery(e.target.value)} 51 | maxLength={10} 52 | /> 53 |
54 | 55 | {!filteredArtifacts.length ? ( 56 |
57 | 58 | 59 | OK 60 | 61 | 62 | Artifact{" "} 63 | 64 | "{searchQuery}" 65 | {" "} 66 | has not had any reported issues. 67 | 68 | 69 |
70 | ) : ( 71 | filteredArtifacts.map(([key, value]) => ( 72 |
76 | 77 | 78 | {key} 79 | 80 | {value} 81 | 82 |
83 | )) 84 | )} 85 |
86 | ); 87 | }; 88 | 89 | export default BrokenArtifacts; 90 | -------------------------------------------------------------------------------- /app/check/route.ts: -------------------------------------------------------------------------------- 1 | import { getAllBrokenArtifacts } from "@/actions/fivem"; 2 | import { NextRequest } from "next/server"; 3 | 4 | export async function GET(request: NextRequest) { 5 | const artifact = request?.nextUrl?.searchParams.get("artifact"); 6 | if (!artifact) { 7 | return Response.json( 8 | { 9 | error: true, 10 | msg: "You have not provided an artifact to check", 11 | }, 12 | { status: 400 } 13 | ); 14 | } 15 | 16 | const allBrokenArtifacts = getAllBrokenArtifacts(); 17 | if (allBrokenArtifacts[artifact]) { 18 | return Response.json({ 19 | status: "BROKEN", 20 | reason: allBrokenArtifacts[artifact], 21 | }); 22 | } 23 | 24 | return Response.json({ status: "OK" }); 25 | } 26 | -------------------------------------------------------------------------------- /app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; -------------------------------------------------------------------------------- /app/json/route.ts: -------------------------------------------------------------------------------- 1 | import { getRecommendedArtifact } from "@/actions/fivem"; 2 | import db from "@/db.json"; 3 | 4 | type ResponseData = { 5 | error?: boolean; 6 | msg?: string; 7 | brokenArtifacts?: object; 8 | windowsDownloadLink?: string; 9 | linuxDownloadLink?: string; 10 | recommendedArtifact?: string; 11 | }; 12 | 13 | export async function GET() { 14 | const data: ResponseData | false = await getRecommendedArtifact(); 15 | if (!data) 16 | return Response.json({ 17 | error: true, 18 | msg: "Could not fetch data - please try again later.", 19 | }); 20 | 21 | data.brokenArtifacts = db.brokenArtifacts; 22 | 23 | return Response.json(data); 24 | } 25 | -------------------------------------------------------------------------------- /app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import "./globals.css"; 3 | 4 | export const metadata: Metadata = { 5 | title: "FiveM Artifacts DB", 6 | description: 7 | "Find and download the latest recommended artifacts, and avoid artifacts with known issues", 8 | }; 9 | 10 | export default function RootLayout({ 11 | children, 12 | }: Readonly<{ 13 | children: React.ReactNode; 14 | }>) { 15 | return ( 16 | 17 | 18 | 19 | 20 | 21 | {children} 22 | 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /app/page.tsx: -------------------------------------------------------------------------------- 1 | import { getRecommendedArtifact } from "@/actions/fivem"; 2 | import BrokenArtifacts from "./brokenArtifacts"; 3 | 4 | export default async function Home() { 5 | const data: 6 | | { 7 | windowsDownloadLink: string; 8 | linuxDownloadLink: string; 9 | recommendedArtifact: string; 10 | } 11 | | false = await getRecommendedArtifact(); 12 | 13 | if (!data) 14 | return ( 15 |
16 | Could not fetch artifacts data. Please try again later. 17 |
18 | ); 19 | 20 | return ( 21 |
22 |
23 |

FiveM Artifacts DB

24 |

25 | An open source project by JG Scripts.{" "} 26 | 31 | View it on GitHub. 32 | 33 |

34 |
35 | 36 |
37 |

38 | 39 | Latest* artifact with 40 | no reported issues 41 | 42 | 43 | {data.recommendedArtifact} 44 | 45 |

46 |
47 |

Download:

48 | 52 | Windows 53 | 54 | 58 | Linux 59 | 60 |
61 |
62 | 63 |
64 | 65 | *Note: 66 | 67 | The very newest artifact will not be recommended immediately due to a 68 | short wait period, to allow time for issues to be reported. 69 |
70 | 71 |
72 |

Artifacts with reported issues:

73 | 78 | Know an issue? Report it here 79 | 80 |
81 | 82 | 83 |
84 | ); 85 | } 86 | -------------------------------------------------------------------------------- /db.json: -------------------------------------------------------------------------------- 1 | { 2 | "brokenArtifacts": { 3 | "8509": "State bags not replicated to clients", 4 | "10072": "Crashing when players join", 5 | "10191": "GetVehicleNumberPlateText server native broken, will cause issues with scripts involving vehicles", 6 | "10268-10309": "sv_experimentalNetGameEventHandler enabled by default; can cause server crashing and reports of issues downgrading after upgrading", 7 | "10930": "Failed build, ignore", 8 | "12078-12083": "Some clients will fail to connect with 'ReadBulk of header failed' error", 9 | "12092-12135": "SetPlayerModel may cause SIGSEGV crashes on some clients due to changes in player handling. 12031 and below works fine.", 10 | "12151": "Crashing if using newest title update (TU)", 11 | "12160-12165": "Cannot use entity native calls in entityRemoved", 12 | "12255": "Unconfirmed, but several reports of server-side issues that don't persist when downgrading; best to avoid", 13 | "12509": "Failed build, ignore", 14 | "12651": "Failed Linux build, ignore", 15 | "12767": "os.date() appends null terminator; can break Lua scripts", 16 | "12913-12932": "Causes a crash when restarting Node.js 22", 17 | "12933-13045": "Still crashes (sometimes) when restarting resources due to Node.js 22", 18 | "13079": "Failed Linux build (works OK if using Windows!)", 19 | "13380-13458": "Server crashing due to integer encoding (PR#3235)", 20 | "13759-13890": "Mumble (voice) external connections blocked by default", 21 | "14583-14716": "Crash when using the new onEntityBucketChange", 22 | "14583-14862": "Timeouts due to latency units being in nanoseconds" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {}; 3 | 4 | export default nextConfig; 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fivem-artifacts-db", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev --turbopack", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "react": "19.0.0", 13 | "react-dom": "19.0.0", 14 | "next": "15.1.4" 15 | }, 16 | "devDependencies": { 17 | "typescript": "^5", 18 | "@types/node": "^20", 19 | "@types/react": "19.0.4", 20 | "@types/react-dom": "19.0.2", 21 | "postcss": "^8", 22 | "tailwindcss": "^3.4.1", 23 | "eslint": "^8", 24 | "eslint-config-next": "15.1.4" 25 | }, 26 | "overrides": { 27 | "@types/react": "19.0.4", 28 | "@types/react-dom": "19.0.2" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: { 4 | tailwindcss: {}, 5 | }, 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "tailwindcss"; 2 | import { fontFamily } from 'tailwindcss/defaultTheme' 3 | 4 | const config: Config = { 5 | content: [ 6 | "./pages/**/*.{js,ts,jsx,tsx,mdx}", 7 | "./components/**/*.{js,ts,jsx,tsx,mdx}", 8 | "./app/**/*.{js,ts,jsx,tsx,mdx}", 9 | ], 10 | theme: { 11 | extend: { 12 | colors: { 13 | background: "var(--background)", 14 | foreground: "var(--foreground)", 15 | }, 16 | fontFamily: { 17 | sans: ['Inter var', ...fontFamily.sans], 18 | }, 19 | }, 20 | }, 21 | plugins: [], 22 | }; 23 | export default config; 24 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": [ 4 | "dom", 5 | "dom.iterable", 6 | "esnext" 7 | ], 8 | "allowJs": true, 9 | "skipLibCheck": true, 10 | "strict": true, 11 | "noEmit": true, 12 | "esModuleInterop": true, 13 | "module": "esnext", 14 | "moduleResolution": "bundler", 15 | "resolveJsonModule": true, 16 | "isolatedModules": true, 17 | "jsx": "preserve", 18 | "incremental": true, 19 | "plugins": [ 20 | { 21 | "name": "next" 22 | } 23 | ], 24 | "paths": { 25 | "@/*": [ 26 | "./*" 27 | ] 28 | }, 29 | "target": "ES2017" 30 | }, 31 | "include": [ 32 | "next-env.d.ts", 33 | "**/*.ts", 34 | "**/*.tsx", 35 | ".next/types/**/*.ts" 36 | ], 37 | "exclude": [ 38 | "node_modules" 39 | ] 40 | } 41 | --------------------------------------------------------------------------------