Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
Demigod committed Aug 21, 2023
1 parent ed197ff commit 7701870
Show file tree
Hide file tree
Showing 21 changed files with 1,770 additions and 357 deletions.
19 changes: 0 additions & 19 deletions LICENSE

This file was deleted.

234 changes: 48 additions & 186 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,216 +1,78 @@
<div align="center">
<img href="https://projecterror.dev" width="150" src="https://i.tasoagc.dev/c1pD" alt="Material-UI logo" />
</div>
<h1 align="center">FiveM React and Lua Boilerplate</h1>
# demi_lootbox - README.md

<div align="center">
A simple and extendable React (TypeScript) boilerplate designed around the Lua ScRT
</div>
## Overview

<div align="center">

[![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/project-error/pe-utils/master/LICENSE)
![Discord](https://img.shields.io/discord/791854454760013827?label=Our%20Discord)
![David](https://img.shields.io/david/project-error/fivem-react-boilerplate-lua)
[![Dependabot Status](https://api.dependabot.com/badges/status?host=github&repo=project-error/fivem-react-boilerplate-lua)](https://dependabot.com)
</div>

This repository is a basic boilerplate for getting started
with React in NUI. It contains several helpful utilities and
is bootstrapped using `create-react-app`. It is for both browser
and in-game based development workflows.

For in-game workflows, Utilizing `craco` to override CRA, we can have hot
builds that just require a resource restart instead of a full
production build

This version of the boilerplate is meant for the CfxLua runtime.

## Requirements
* [Node > v10.6](https://nodejs.org/en/)
* [Yarn](https://yarnpkg.com/getting-started/install) (Preferred but not required)

*A basic understanding of the modern web development workflow. If you don't
know this yet, React might not be for you just yet.*

## Getting Started

First clone the repository or use the template option and place
it within your `resources` folder

### Installation

*The boilerplate was made using `yarn` but is still compatible with
`npm`.*

Install dependencies by navigating to the `web` folder within
a terminal of your choice and type `npm i` or `yarn`.
`demi_lootbox` is a FiveM script that brings a CSGO-style case opening user interface into your server. With a visual representation and configurable case contents, this script enhances the in-game economy with randomized loot mechanics.

## Features

This boilerplate comes with some utilities and examples to work off of.

### Lua Utils
- **Customizable Cases:** Define your own cases with varying rarity levels (common, uncommon, rare, epic, legendary).
- **UI:** A UI that gives players a sense of anticipation when opening cases.
- **Dependencies:** The script requires `ox_inventory` to function properly.

**SendReactMessage**
## Installation

This is a small wrapper for dispatching NUI messages. This is designed
to be used with the `useNuiEvent` React hook.

Signature
```lua
---@param action string The action you wish to target
---@param data any The data you wish to send along with this action
SendReactMessage(action, data)
```

Usage
```lua
SendReactMessage('setVisible', true)
```
1. Ensure you have the `ox_inventory` installed and properly configured in your FiveM server.
2. Copy the `demi_lootbox` folder into your server's resources directory.
3. Add `ensure demi_lootbox` to your server configuration file.

**debugPrint**
## Configuration

A debug printing utility that is dependent on a convar,
if the convar is set this will print out to the console.
The primary configuration for your cases is done within the `CASES` table found in `server/data.lua` Here, you can define your own cases with varying rarity levels and the weapons/items each rarity level might contain.

The convar is dependent on the name given to the resource.
It follows this format `YOUR_RESOURCE_NAME-debugMode`
each rarity should have at least 1 item in it, or the script wont work properly.

To turn on debugMode add `setr YOUR_RESOURCE_NAME-debugMode 1` to
your server.cfg or use the `setr` console command instead.
Example:

Signature (Replicates `print`)
```lua
---@param ... any[] The arguments you wish to send
debugPrint(...)
```

Usage
```lua
debugPrint('wow cool string to print', true, someOtherVar)
```

### React Utils

Signatures are not included for these utilities as the type definitions
are sufficient enough.

**useNuiEvent**

This is a custom React hook that is designed to intercept and handle
messages dispatched by the game scripts. This is the primary
way of creating passive listeners.


*Note: For now handlers can only be registered a single time. I haven't
come across a personal usecase for a cascading event system*

**Usage**
```jsx
const MyComp: React.FC = () => {
const [state, setState] = useState('')

useNuiEvent<string>('myAction', (data) => {
// the first argument to the handler function
// is the data argument sent using SendReactMessage

// do whatever logic u want here
setState(data)
})

return(
<div>
<h1>Some component</h1>
<p>{state}</p>
</div>
)
CASES = {
['weapon_case'] = {
common = {
{
name = 'WEAPON_PISTOL',
amount = 1,
},
...
},
...
}
}

```

**fetchNui**

This is a simple NUI focused wrapper around the standard `fetch` API.
This is the main way to accomplish active NUI data fetching
or to trigger NUI callbacks in the game scripts.

When using this, you must always at least callback using `{}`
in the gamescripts.

*This can be heavily customized to your use case*
## Usage

**Usage**
```ts
// First argument is the callback event name.
fetchNui<ReturnData>('getClientData').then(retData => {
console.log('Got return data from client scripts:')
console.dir(retData)
setClientData(retData)
}).catch(e => {
console.error('Setting mock data due to error', e)
setClientData({ x: 500, y: 300, z: 200})
})
To test a case:
```

**debugData**

This is a function allowing for mocking dispatched game script
actions in a browser environment. It will trigger `useNuiEvent` handlers
as if they were dispatched by the game scripts. **It will only fire if the current
environment is a regular browser and not CEF**

**Usage**
```ts
// This will target the useNuiEvent hooks registered with `setVisible`
// and pass them the data of `true`
debugData([
{
action: 'setVisible',
data: true,
}
])
/opencase [case_name]
```
Replace `[case_name]` with the actual name of the case you want to open (for instance, `weapon_case`).

**Misc Utils**

These are small but useful included utilities.

* `isEnvBrowser()` - Will return a boolean indicating if the current
environment is a regular browser. (Useful for logic in development)

## Development Workflow
ideally you would remove this command before putting it in your server

This boilerplate was designed with development workflow in mind.
It includes some helpful scripts to accomplish that.
## Exported Functions
You can use the exported function to open a case programmatically through an item use or something:

**Hot Builds In-Game**
```lua
exports.demi_lootbox.openCase(caseName)
```

When developing in-game, you can use the hot build system by
running the `start:game` script. This is essentially the start
script but it writes to disk. Meaning all that is required is a
resource restart to update the game script

**Usage**
```sh
# yarn
yarn start:game
# npm
npm run start:game
```
## Probabilities
for those curious about the chances

**Production Builds**
- Common: 80%
- Uncommon: 16%
- Rare: 3.10%
- Epic: 0.64%
- Legendary: 0.26%

When you are done with development phase for your resource. You
must create a production build that is optimized and minimized.
## Client-Side Functions

You can do this by running the following:
- `getWinnerForCase(case)`: A function to get the winning item for a specific case.
- Command `/opencase`: To open a specified case.
- NUI Callback for when the case opening animation finishes.

```sh
npm run build
yarn build
```
## Feedback & Support

## Additional Notes
For any feedback or support regarding the script, please reach out in the forums or discord `demiautomatic`.

Need further support? Join our [Discord](https://discord.com/invite/HYwBjTbAY5)!
31 changes: 12 additions & 19 deletions client/client.lua
Original file line number Diff line number Diff line change
@@ -1,25 +1,18 @@
local function toggleNuiFrame(shouldShow)
SetNuiFocus(shouldShow, shouldShow)
SendReactMessage('setVisible', shouldShow)
end
local function getWinnerForCase(case)
local lootPool, winner = lib.callback.await('demi_lootbox:getCaseAndWinner', false, case)

RegisterCommand('show-nui', function()
toggleNuiFrame(true)
debugPrint('Show NUI frame')
end)
SendReactMessage('setLootData', { pool = lootPool, winner = winner })
end

RegisterNUICallback('hideFrame', function(_, cb)
toggleNuiFrame(false)
debugPrint('Hide NUI frame')
cb({})
RegisterCommand('opencase', function(src, args)
if not args[1] then return end
getWinnerForCase(args[1])
end)

RegisterNUICallback('getClientData', function(data, cb)
debugPrint('Data sent by React', json.encode(data))
exports('openCase', getWinnerForCase)

-- Lets send back client coords to the React frame for use
local curCoords = GetEntityCoords(PlayerPedId())

local retData <const> = { x = curCoords.x, y = curCoords.y, z = curCoords.z }
cb(retData)
end)
RegisterNUICallback('finished', function(_,cb)
TriggerServerEvent('demi_lootbox:getQueuedItem')
cb({})
end)
22 changes: 0 additions & 22 deletions client/utils.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
--- A simple wrapper around SendNUIMessage that you can use to
--- dispatch actions to the React frame.
---
---@param action string The action you wish to target
---@param data any The data you wish to send along with this action
function SendReactMessage(action, data)
Expand All @@ -9,22 +6,3 @@ function SendReactMessage(action, data)
data = data
})
end

local currentResourceName = GetCurrentResourceName()

local debugIsEnabled = GetConvarInt(('%s-debugMode'):format(currentResourceName), 0) == 1

--- A simple debug print function that is dependent on a convar
--- will output a nice prettfied message if debugMode is on
function debugPrint(...)
if not debugIsEnabled then return end
local args <const> = { ... }

local appendStr = ''
for _, v in ipairs(args) do
appendStr = appendStr .. ' ' .. tostring(v)
end
local msgTemplate = '^3[%s]^0%s'
local finalMsg = msgTemplate:format(currentResourceName, appendStr)
print(finalMsg)
end
20 changes: 6 additions & 14 deletions fxmanifest.lua
Original file line number Diff line number Diff line change
@@ -1,23 +1,15 @@
fx_version "cerulean"

description "Basic React (TypeScript) & Lua Game Scripts Boilerplate"
author "Project Error"
version '1.0.0'
repository 'https://github.com/project-error/fivem-react-boilerplate-lua'

lua54 'yes'

games {
"gta5",
"rdr3"
}
game "gta5"

ui_page 'web/build/index.html'

shared_script '@ox_lib/init.lua'

client_script "client/**/*"
server_script "server/**/*"

files {
'web/build/index.html',
'web/build/**/*',
}
'web/build/index.html',
'web/build/**/*',
}
Loading

0 comments on commit 7701870

Please sign in to comment.