برنامه نویسی

FiveM x Prisma ORM – انجمن DEV

Summarize this content to 400 words in Persian Lang
⚠️ ما در مورد سرور JavaScript/TypeScript FiveM صحبت می کنیم ⚠️⚠️

FiveM یک تغییر برای Grand Theft Auto V است که به شما امکان می‌دهد به صورت چند نفره روی سرورهای اختصاصی سفارشی‌سازی شده با Cfx.re بازی کنید.

وقتی یک سرور FiveM را توسعه می‌دهید، استفاده از پایگاه داده برای ذخیره داده‌های پخش‌کننده و همه اطلاعات پایدار دیگر کلاسیک است.

برای استفاده از پایگاه داده در کد سمت سرور، جامعه FiveM چندین اتصال ایجاد کرد:

fivem-async-mysql
oxmysql

همه این کانکتورها روش هایی را برای اجرای پرس و جوهای خام ارائه می دهند مانند:

const response = await MySQL.query(‘SELECT `firstname`, `lastname` FROM `users` WHERE `identifier` = ?’, [
identifier
])

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

2 مشکل اصلی در این روش تعامل با پایگاه داده وجود دارد:

Dev باید پاسخ را تجزیه کند تا آن را با یک مدل خوب تایپ کند
پرس و جو در مواجهه با طرحواره پایگاه داده قبل از اجرا تایید نمی شود

این نقاط درد با استفاده از ORM حل می شوند، آنها به شما اجازه می دهند مدل های خود را توصیف کنید، بررسی نوع را مدیریت می کند. در این مقاله نحوه ادغام Prisma ORM را در FiveM خواهیم دید.

پوشه منبع FiveM که در مقاله خود استفاده خواهم کرد یک فضای کاری pnpm است. این اجازه می دهد تا چندین بسته را با کد مشترک مدیریت کنید.

├── player-manager
│ └── prisma
│ │ ├── player.schema.prisma
│ │ └── package.json // @player-manager/prisma package
│ └── server
│ │ ├── server.ts
│ │ └── package.json // @player-manager/server package
│ └── fxmanifest.lua
├── lib
│ └── prisma-orm. // @lib/prisma-orm package
│ ├── package.json
│ └── …
├── package.json
└── pnpm-worspace.yml

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

طرحواره منشور را تقسیم کنید

در prisma-orm راه اندازی پوشه Prisma پس از شروع. ابتدا، ما طرحواره Prisma را به چند منبع تقسیم می کنیم.

برای این کار از ابزار merge-prisma-schema استفاده می کنیم. از آن برای ادغام قطعات طرحواره در ماژول های چندگانه استفاده می شود

merge-prisma-schema.config.ts

const config = {
schemas: [
“header.prisma”,
“@player-manager/prisma/player.schema.prisma”,
],
output: “prisma/schema.prisma”,
schemaSearchFolders: [“node_modules”, “prisma”],
prismaCli: “npx prisma”,
};

export default config;

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

header.prisma

// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

generator client {
provider = “prisma-client-js”
engineType = “binary”
binaryTargets = [“windows”] output = “C:/gta-fivem/txData/CFXDefaultFiveM_8AE314.base/resources/lib/prisma-orm/prisma/generated”
}

datasource db {
provider = “sqlite”
url = “file:C:/gta-fivem/txData/CFXDefaultFiveM_8AE314.base/resources/dev.db”
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

داخل @player-module فایل player.schema.prisma

model Player {
id Int @id @default(autoincrement())
steamId String @unique
lastname String
firstname String
inventory Inventory?
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

به روز رسانی prisma-orm ماژول package.json

{
“name”: “@lib/database”,
“version”: “1.0.0”,
“scripts”: {
“——— BUILD ———“: “——— BUILD ———“,
“pregenerate”: “npx merge-prisma-schema && rm -rf ./prisma/generated”,
“generate”: “prisma generate”,
“——— MIGRATE ———“: “——— MIGRATE ———“,
“premigrate”: “npx merge-prisma-schema”,
“migrate”: “prisma migrate”
},
“devDependencies”: {
“@player-manager/prisma”: “workspace:*”,
“merge-prisma-schema”: “^1.0.0”,
“prisma”: “catalog:”,
“ts-node”: “catalog:”,
“typescript”: “catalog:”,
},
“dependencies”: {
“@prisma/client”: “catalog:”
}
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اگر بدوید npx pregenerate آن را به روز می کند prisma.schema فایل مانند این:

// Generated at 2024-09-07T20:46:01.740Z by “merge-prisma-schema”

// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

generator client {
provider = “prisma-client-js”
engineType = “binary”
binaryTargets = [“windows”] output = “C:/gta-fivem/txData/CFXDefaultFiveM_8AE314.base/resources/lib/database/prisma/generated”
}

datasource db {
provider = “sqlite”
url = “file:C:/gta-fivem/txData/CFXDefaultFiveM_8AE314.base/resources/dev.db”
}

model Player {
id Int @id @default(autoincrement())
steamId String @unique
lastname String
firstname String
inventory Inventory?
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

زمان اجرا منشور را ایجاد کنید

استفاده خواهیم کرد npx generate برای ایجاد زمان اجرا Prisma، همانطور که در مستندات توضیح داده شده است. پس از تولید، از یک اسکریپت برای اصلاح فایل استفاده می کنیم index.js که تولید شده است. برای انجام آن از بسته استفاده می شود tsx.

package.json

{
“name”: “@lib/prisma-orm”,
“version”: “1.0.0”,
“scripts”: {
“——— BUILD ———“: “——— BUILD ———“,
“pregenerate”: “npx merge-prisma-schema && rm -rf ./prisma/generated”,
“generate”: “prisma generate”,
“postgenerate”: “npx tsx patch-generated-runtime.ts”,
“——— MIGRATE ———“: “——— MIGRATE ———“,
“premigrate”: “npx merge-prisma-schema”,
“migrate”: “prisma migrate”
},
“devDependencies”: {
“@player-manager/prisma”: “workspace:*”,
“merge-prisma-schema”: “^1.0.0”,
“prisma”: “catalog:”,
“ts-node”: “catalog:”,
“typescript”: “catalog:”,
},
“dependencies”: {
“@prisma/client”: “catalog:”
}
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

patch-generated-runtime.ts

import { readFileSync, writeFileSync } from “fs”;
import { join } from “path”;

const toReplace = `
const alternativePath = alternativePaths.find((altPath) => {
return fs.existsSync(path.join(process.cwd(), altPath, ‘schema.prisma’))
}) ?? alternativePaths[0]

config.dirname = path.join(process.cwd(), alternativePath)
config.isBundled = true
`;

const replaceValue = `
const alternativePath = alternativePaths.find((altPath) => {
return fs.existsSync(path.join(process.cwd(), altPath, ‘schema.prisma’))
});

if(alternativePath) {
config.dirname = path.join(process.cwd(), alternativePath)
} else if(config?.generator?.output?.value) {
config.dirname = config.generator.output.value;
} else {
config.dirname = path.join(process.cwd(), alternativePaths[0]);
}

config.isBundled = true
`;

const file = join(process.cwd(), ‘prisma’, ‘generated’, ‘index.js’);

const indexContent = readFileSync(file, { encoding: ‘utf-8’});

const newIndexContent = indexContent.replace(toReplace, replaceValue);

writeFileSync(file, newIndexContent);

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

با پچ، دایرکتوری Prisma با عبارت تعریف می شود output اموال تنظیم شده در schema.prisma

بسته نرم افزاری prisma client

همانطور که FiveM فقط با آن کار می کند commonjs فایل های همراه، ما استفاده خواهیم کرد rollup برای ایجاد یک کد ساخته شده سازگار. این کد با بسته ها/منابع دیگر به اشتراک گذاشته خواهد شد.

rollup.config.mjs

import typescript from “@rollup/plugin-typescript”;
import commonjs from “@rollup/plugin-commonjs”;
import resolve from “@rollup/plugin-node-resolve”;

const banner = `
const { resolve, join } = require(“path”);
const { cwd } = require(“process”);

var __dirname = resolve();
var __filename = join(__dirname, “index.js”);

process.env[“PRISMA_QUERY_ENGINE_BINARY”] = join(cwd(), “resources”, “lib”, “prisma-orm”, “prisma”, “generated”, “query-engine-windows.exe”);
`;

export default {
input: “./index.ts”,
output: {
dir: “dist”,
format: “cjs”,
sourcemap: false,
banner,
},
plugins: [resolve(), typescript(), commonjs()],
};

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

با این پیکربندی، یک بسته نرم افزاری ایجاد می کنیم و پیشوند آن را با برخی از اطلاعات مهم اضافه می کنیم:

__dirname و __filename در زمینه اجرای گره FiveM وجود ندارد، بنابراین ما آن را به صورت دستی تعریف می کنیم زیرا Prisma به آن نیاز دارد.

process.env[“PRISMA_QUERY_ENGINE_BINARY”] محل اجرای موتور پرس و جو است که در پوشه زمان اجرا Prisma ایجاد شده است

نمونه استفاده

داخل @player-manager/server که یک بسته Typescript با پیکربندی rollup است که در اینجا توضیح داده شده است، اضافه می کنم @lib/prisma-orm به dependencies.

server.ts

import { prisma } from “@lib/database”

on(“onResourceStart”, async (resName: string) => {
if (resName === GetCurrentResourceName()) {
const players = await prisma.player.findMany();
console.log(“players”, JSON.stringify(players))
}
});

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

می توانید یک مثال کامل را در اینجا بازیابی کنید https://github.com/JustinMartinDev/experimental-rp

⚠️ ما در مورد سرور JavaScript/TypeScript FiveM صحبت می کنیم ⚠️⚠️

FiveM یک تغییر برای Grand Theft Auto V است که به شما امکان می‌دهد به صورت چند نفره روی سرورهای اختصاصی سفارشی‌سازی شده با Cfx.re بازی کنید.

وقتی یک سرور FiveM را توسعه می‌دهید، استفاده از پایگاه داده برای ذخیره داده‌های پخش‌کننده و همه اطلاعات پایدار دیگر کلاسیک است.

برای استفاده از پایگاه داده در کد سمت سرور، جامعه FiveM چندین اتصال ایجاد کرد:

  • fivem-async-mysql
  • oxmysql

همه این کانکتورها روش هایی را برای اجرای پرس و جوهای خام ارائه می دهند مانند:

const response = await MySQL.query('SELECT `firstname`, `lastname` FROM `users` WHERE `identifier` = ?', [
  identifier
])
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

2 مشکل اصلی در این روش تعامل با پایگاه داده وجود دارد:

  • Dev باید پاسخ را تجزیه کند تا آن را با یک مدل خوب تایپ کند
  • پرس و جو در مواجهه با طرحواره پایگاه داده قبل از اجرا تایید نمی شود

این نقاط درد با استفاده از ORM حل می شوند، آنها به شما اجازه می دهند مدل های خود را توصیف کنید، بررسی نوع را مدیریت می کند. در این مقاله نحوه ادغام Prisma ORM را در FiveM خواهیم دید.

پوشه منبع FiveM که در مقاله خود استفاده خواهم کرد یک فضای کاری pnpm است. این اجازه می دهد تا چندین بسته را با کد مشترک مدیریت کنید.

├── player-manager
   └── prisma
        ├── player.schema.prisma
      └── package.json // @player-manager/prisma package
     └── server
        ├── server.ts
      └── package.json // @player-manager/server package
   └── fxmanifest.lua
├── lib
   └── prisma-orm.  // @lib/prisma-orm package
          ├── package.json
          └── ... 
├── package.json
└── pnpm-worspace.yml

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

طرحواره منشور را تقسیم کنید

در prisma-orm راه اندازی پوشه Prisma پس از شروع. ابتدا، ما طرحواره Prisma را به چند منبع تقسیم می کنیم.

برای این کار از ابزار merge-prisma-schema استفاده می کنیم. از آن برای ادغام قطعات طرحواره در ماژول های چندگانه استفاده می شود

merge-prisma-schema.config.ts

const config = {
  schemas: [
    "header.prisma",
    "@player-manager/prisma/player.schema.prisma",
  ],
  output: "prisma/schema.prisma",
  schemaSearchFolders: ["node_modules", "prisma"],
  prismaCli: "npx prisma",
};

export default config;
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

header.prisma

// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

generator client {
  provider      = "prisma-client-js"
  engineType    = "binary"
  binaryTargets = ["windows"]
  output        = "C:/gta-fivem/txData/CFXDefaultFiveM_8AE314.base/resources/lib/prisma-orm/prisma/generated"
}

datasource db {
  provider = "sqlite"
  url      = "file:C:/gta-fivem/txData/CFXDefaultFiveM_8AE314.base/resources/dev.db"
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

داخل @player-module فایل player.schema.prisma

model Player {
  id          Int      @id @default(autoincrement())
  steamId     String   @unique 
  lastname    String
  firstname   String
  inventory   Inventory?
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

به روز رسانی prisma-orm ماژول package.json

{
  "name": "@lib/database",
  "version": "1.0.0",
  "scripts": {
    "--------- BUILD ---------": "--------- BUILD ---------",
    "pregenerate": "npx merge-prisma-schema && rm -rf ./prisma/generated",
    "generate": "prisma generate",
    "--------- MIGRATE ---------": "--------- MIGRATE ---------",
    "premigrate": "npx merge-prisma-schema",
    "migrate": "prisma migrate"
  },
  "devDependencies": {
    "@player-manager/prisma": "workspace:*",
    "merge-prisma-schema": "^1.0.0",
    "prisma": "catalog:",
    "ts-node": "catalog:",
    "typescript": "catalog:",
  },
  "dependencies": {
    "@prisma/client": "catalog:"
  }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اگر بدوید npx pregenerate آن را به روز می کند prisma.schema فایل مانند این:

// Generated at 2024-09-07T20:46:01.740Z by "merge-prisma-schema"

// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

generator client {
  provider      = "prisma-client-js"
  engineType    = "binary"
  binaryTargets = ["windows"]
  output        = "C:/gta-fivem/txData/CFXDefaultFiveM_8AE314.base/resources/lib/database/prisma/generated"
}

datasource db {
  provider = "sqlite"
  url      = "file:C:/gta-fivem/txData/CFXDefaultFiveM_8AE314.base/resources/dev.db"
}

model Player {
  id          Int      @id @default(autoincrement())
  steamId     String   @unique 
  lastname    String
  firstname   String
  inventory   Inventory?
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

زمان اجرا منشور را ایجاد کنید

استفاده خواهیم کرد npx generate برای ایجاد زمان اجرا Prisma، همانطور که در مستندات توضیح داده شده است. پس از تولید، از یک اسکریپت برای اصلاح فایل استفاده می کنیم index.js که تولید شده است. برای انجام آن از بسته استفاده می شود tsx.

package.json

{
  "name": "@lib/prisma-orm",
  "version": "1.0.0",
  "scripts": {
    "--------- BUILD ---------": "--------- BUILD ---------",
    "pregenerate": "npx merge-prisma-schema && rm -rf ./prisma/generated",
    "generate": "prisma generate",
    "postgenerate": "npx tsx patch-generated-runtime.ts",
    "--------- MIGRATE ---------": "--------- MIGRATE ---------",
    "premigrate": "npx merge-prisma-schema",
    "migrate": "prisma migrate"
  },
  "devDependencies": {
    "@player-manager/prisma": "workspace:*",
    "merge-prisma-schema": "^1.0.0",
    "prisma": "catalog:",
    "ts-node": "catalog:",
    "typescript": "catalog:",
  },
  "dependencies": {
    "@prisma/client": "catalog:"
  }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

patch-generated-runtime.ts

import { readFileSync, writeFileSync } from "fs";
import { join } from "path";

const toReplace = `
  const alternativePath = alternativePaths.find((altPath) => {
    return fs.existsSync(path.join(process.cwd(), altPath, 'schema.prisma'))
  }) ?? alternativePaths[0]

  config.dirname = path.join(process.cwd(), alternativePath)
  config.isBundled = true
`;

const replaceValue = `
  const alternativePath = alternativePaths.find((altPath) => {
    return fs.existsSync(path.join(process.cwd(), altPath, 'schema.prisma'))
  }); 

  if(alternativePath) {
    config.dirname = path.join(process.cwd(), alternativePath)
  } else if(config?.generator?.output?.value) {
    config.dirname = config.generator.output.value;
  } else {
    config.dirname = path.join(process.cwd(), alternativePaths[0]);
  }

  config.isBundled = true  
`;

const file = join(process.cwd(), 'prisma', 'generated', 'index.js');

const indexContent = readFileSync(file, { encoding: 'utf-8'});

const newIndexContent = indexContent.replace(toReplace, replaceValue);

writeFileSync(file, newIndexContent);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

با پچ، دایرکتوری Prisma با عبارت تعریف می شود output اموال تنظیم شده در schema.prisma

بسته نرم افزاری prisma client

همانطور که FiveM فقط با آن کار می کند commonjs فایل های همراه، ما استفاده خواهیم کرد rollup برای ایجاد یک کد ساخته شده سازگار. این کد با بسته ها/منابع دیگر به اشتراک گذاشته خواهد شد.

rollup.config.mjs

import typescript from "@rollup/plugin-typescript";
import commonjs from "@rollup/plugin-commonjs";
import resolve from "@rollup/plugin-node-resolve";

const banner = `
  const { resolve, join } = require("path");
  const { cwd } = require("process");

  var __dirname = resolve();
  var __filename = join(__dirname, "index.js");

  process.env["PRISMA_QUERY_ENGINE_BINARY"] = join(cwd(), "resources", "lib", "prisma-orm", "prisma", "generated", "query-engine-windows.exe");
`;

export default {
  input: "./index.ts",
  output: {
    dir: "dist",
    format: "cjs",
    sourcemap: false,
    banner,
  },
  plugins: [resolve(), typescript(), commonjs()],
};
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

با این پیکربندی، یک بسته نرم افزاری ایجاد می کنیم و پیشوند آن را با برخی از اطلاعات مهم اضافه می کنیم:

  • __dirname و __filename در زمینه اجرای گره FiveM وجود ندارد، بنابراین ما آن را به صورت دستی تعریف می کنیم زیرا Prisma به آن نیاز دارد.
  • process.env["PRISMA_QUERY_ENGINE_BINARY"] محل اجرای موتور پرس و جو است که در پوشه زمان اجرا Prisma ایجاد شده است

نمونه استفاده

داخل @player-manager/server که یک بسته Typescript با پیکربندی rollup است که در اینجا توضیح داده شده است، اضافه می کنم @lib/prisma-orm به dependencies.

server.ts

import { prisma } from "@lib/database"

on("onResourceStart", async (resName: string) => {
  if (resName === GetCurrentResourceName()) {
    const players = await prisma.player.findMany();
    console.log("players", JSON.stringify(players))
  }
});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

می توانید یک مثال کامل را در اینجا بازیابی کنید https://github.com/JustinMartinDev/experimental-rp

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا