├── resources
├── [local]
│ └── .gitkeep
├── [gameplay]
│ ├── chat
│ │ ├── README.md
│ │ ├── .gitignore
│ │ ├── html
│ │ │ ├── index.d.ts
│ │ │ ├── vendor
│ │ │ │ ├── fonts
│ │ │ │ │ ├── LatoBold.woff2
│ │ │ │ │ ├── LatoBold2.woff2
│ │ │ │ │ ├── LatoLight.woff2
│ │ │ │ │ ├── LatoLight2.woff2
│ │ │ │ │ ├── LatoRegular.woff2
│ │ │ │ │ └── LatoRegular2.woff2
│ │ │ │ ├── latofonts.css
│ │ │ │ └── flexboxgrid.6.3.1.min.css
│ │ │ ├── main.ts
│ │ │ ├── Message.vue
│ │ │ ├── tsconfig.json
│ │ │ ├── index.html
│ │ │ ├── config.ts
│ │ │ ├── utils.ts
│ │ │ ├── Suggestions.vue
│ │ │ ├── App.vue
│ │ │ ├── Suggestions.ts
│ │ │ ├── index.css
│ │ │ └── Message.ts
│ │ ├── package.json
│ │ ├── fxmanifest.lua
│ │ ├── webpack.config.js
│ │ ├── cl_chat.lua
│ │ └── sv_chat.lua
│ ├── [examples]
│ │ ├── money-fountain-example-map
│ │ │ ├── map.lua
│ │ │ └── fxmanifest.lua
│ │ ├── ped-money-drops
│ │ │ ├── fxmanifest.lua
│ │ │ ├── server.lua
│ │ │ └── client.lua
│ │ ├── money
│ │ │ ├── fxmanifest.lua
│ │ │ ├── client.lua
│ │ │ └── server.lua
│ │ └── money-fountain
│ │ │ ├── fxmanifest.lua
│ │ │ ├── mapdata.lua
│ │ │ ├── server.lua
│ │ │ └── client.lua
│ ├── player-data
│ │ ├── fxmanifest.lua
│ │ └── server.lua
│ ├── chat-theme-gtao
│ │ ├── fxmanifest.lua
│ │ ├── style.css
│ │ └── shadow.js
│ └── playernames
│ │ ├── fxmanifest.lua
│ │ ├── playernames_sv.lua
│ │ ├── template
│ │ └── LICENSE
│ │ ├── playernames_api.lua
│ │ └── playernames_cl.lua
├── [system]
│ ├── runcode
│ │ ├── .gitignore
│ │ ├── runcode.js
│ │ ├── runcode_cl.lua
│ │ ├── runcode_shared.lua
│ │ ├── fxmanifest.lua
│ │ ├── runcode_sv.lua
│ │ ├── web
│ │ │ └── nui.html
│ │ ├── runcode_ui.lua
│ │ └── runcode_web.lua
│ ├── [builders]
│ │ ├── webpack
│ │ │ ├── .gitignore
│ │ │ ├── package.json
│ │ │ ├── fxmanifest.lua
│ │ │ ├── webpack_runner.js
│ │ │ └── webpack_builder.js
│ │ └── yarn
│ │ │ ├── fxmanifest.lua
│ │ │ └── yarn_builder.js
│ ├── sessionmanager-rdr3
│ │ ├── .gitignore
│ │ ├── package.json
│ │ ├── fxmanifest.lua
│ │ ├── yarn.lock
│ │ ├── rline.proto
│ │ └── sm_server.js
│ ├── hardcap
│ │ ├── client.lua
│ │ ├── fxmanifest.lua
│ │ └── server.lua
│ ├── sessionmanager
│ │ ├── client
│ │ │ └── empty.lua
│ │ ├── fxmanifest.lua
│ │ └── server
│ │ │ └── host_lock.lua
│ ├── baseevents
│ │ ├── fxmanifest.lua
│ │ ├── server.lua
│ │ ├── vehiclechecker.lua
│ │ └── deathevents.lua
│ └── rconlog
│ │ ├── rconlog_client.lua
│ │ ├── fxmanifest.lua
│ │ └── rconlog_server.lua
├── [test]
│ ├── example-loadscreen
│ │ ├── bankgothic.ttf
│ │ ├── loadscreen.jpg
│ │ ├── fxmanifest.lua
│ │ ├── index.html
│ │ └── keks.css
│ └── fivem
│ │ └── fxmanifest.lua
├── [gamemodes]
│ ├── [maps]
│ │ ├── redm-map-one
│ │ │ ├── map.lua
│ │ │ └── fxmanifest.lua
│ │ ├── fivem-map-hipster
│ │ │ ├── fxmanifest.lua
│ │ │ └── map.lua
│ │ └── fivem-map-skater
│ │ │ ├── fxmanifest.lua
│ │ │ └── map.lua
│ └── basic-gamemode
│ │ ├── basic_client.lua
│ │ └── fxmanifest.lua
└── [managers]
│ ├── spawnmanager
│ ├── fxmanifest.lua
│ └── spawnmanager.lua
│ └── mapmanager
│ ├── fxmanifest.lua
│ ├── mapmanager_shared.lua
│ ├── mapmanager_client.lua
│ └── mapmanager_server.lua
├── .gitignore
└── README.md
/resources/[local]/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/resources/[gameplay]/chat/README.md:
--------------------------------------------------------------------------------
1 | # Chat
2 |
--------------------------------------------------------------------------------
/resources/[system]/runcode/.gitignore:
--------------------------------------------------------------------------------
1 | data.json
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.cfg
2 | /cache/
3 | /resources/\[local\]/
4 | /db/
--------------------------------------------------------------------------------
/resources/[system]/[builders]/webpack/.gitignore:
--------------------------------------------------------------------------------
1 | .yarn.installed
2 | node_modules/
--------------------------------------------------------------------------------
/resources/[system]/sessionmanager-rdr3/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .yarn.installed
--------------------------------------------------------------------------------
/resources/[gameplay]/chat/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .yarn.installed
3 | yarn-error.log
4 | dist/
--------------------------------------------------------------------------------
/resources/[gameplay]/chat/html/index.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.vue' {
2 | import Vue from 'vue'
3 | export default Vue
4 | }
--------------------------------------------------------------------------------
/resources/[system]/sessionmanager-rdr3/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "dependencies": {
4 | "@citizenfx/protobufjs": "6.8.8"
5 | }
6 | }
--------------------------------------------------------------------------------
/resources/[test]/example-loadscreen/bankgothic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/citizenfx/cfx-server-data/HEAD/resources/[test]/example-loadscreen/bankgothic.ttf
--------------------------------------------------------------------------------
/resources/[test]/example-loadscreen/loadscreen.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/citizenfx/cfx-server-data/HEAD/resources/[test]/example-loadscreen/loadscreen.jpg
--------------------------------------------------------------------------------
/resources/[gameplay]/[examples]/money-fountain-example-map/map.lua:
--------------------------------------------------------------------------------
1 | money_fountain 'test_fountain' {
2 | vector3(97.334, -973.621, 29.36),
3 | amount = 75
4 | }
--------------------------------------------------------------------------------
/resources/[gameplay]/chat/html/vendor/fonts/LatoBold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/citizenfx/cfx-server-data/HEAD/resources/[gameplay]/chat/html/vendor/fonts/LatoBold.woff2
--------------------------------------------------------------------------------
/resources/[gameplay]/chat/html/vendor/fonts/LatoBold2.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/citizenfx/cfx-server-data/HEAD/resources/[gameplay]/chat/html/vendor/fonts/LatoBold2.woff2
--------------------------------------------------------------------------------
/resources/[gameplay]/chat/html/vendor/fonts/LatoLight.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/citizenfx/cfx-server-data/HEAD/resources/[gameplay]/chat/html/vendor/fonts/LatoLight.woff2
--------------------------------------------------------------------------------
/resources/[gameplay]/chat/html/vendor/fonts/LatoLight2.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/citizenfx/cfx-server-data/HEAD/resources/[gameplay]/chat/html/vendor/fonts/LatoLight2.woff2
--------------------------------------------------------------------------------
/resources/[gameplay]/chat/html/vendor/fonts/LatoRegular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/citizenfx/cfx-server-data/HEAD/resources/[gameplay]/chat/html/vendor/fonts/LatoRegular.woff2
--------------------------------------------------------------------------------
/resources/[gameplay]/chat/html/main.ts:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import App from './App.vue';
3 |
4 | const instance = new Vue({
5 | el: '#app',
6 | render: h => h(App),
7 | });
--------------------------------------------------------------------------------
/resources/[gameplay]/chat/html/vendor/fonts/LatoRegular2.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/citizenfx/cfx-server-data/HEAD/resources/[gameplay]/chat/html/vendor/fonts/LatoRegular2.woff2
--------------------------------------------------------------------------------
/resources/[gamemodes]/[maps]/redm-map-one/map.lua:
--------------------------------------------------------------------------------
1 | spawnpoint 'player_three' { x = -262.849, y = 793.404, z = 118.087 }
2 | spawnpoint 'player_zero' { x = -262.849, y = 793.404, z = 118.087 }
3 |
--------------------------------------------------------------------------------
/resources/[gamemodes]/basic-gamemode/basic_client.lua:
--------------------------------------------------------------------------------
1 | AddEventHandler('onClientMapStart', function()
2 | exports.spawnmanager:setAutoSpawn(true)
3 | exports.spawnmanager:forceRespawn()
4 | end)
5 |
--------------------------------------------------------------------------------
/resources/[gameplay]/chat/html/Message.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/resources/[system]/hardcap/client.lua:
--------------------------------------------------------------------------------
1 | Citizen.CreateThread(function()
2 | while true do
3 | Wait(0)
4 |
5 | if NetworkIsSessionStarted() then
6 | TriggerServerEvent('hardcap:playerActivated')
7 |
8 | return
9 | end
10 | end
11 | end)
--------------------------------------------------------------------------------
/resources/[system]/sessionmanager/client/empty.lua:
--------------------------------------------------------------------------------
1 | --This empty file causes the scheduler.lua to load clientside
2 | --scheduler.lua when loaded inside the sessionmanager resource currently manages remote callbacks.
3 | --Without this, callbacks will only work server->client and not client->server.
--------------------------------------------------------------------------------
/resources/[system]/runcode/runcode.js:
--------------------------------------------------------------------------------
1 | exports('runJS', (snippet) => {
2 | if (IsDuplicityVersion() && GetInvokingResource() !== GetCurrentResourceName()) {
3 | return [ 'Invalid caller.', false ];
4 | }
5 |
6 | try {
7 | return [ new Function(snippet)(), false ];
8 | } catch (e) {
9 | return [ false, e.toString() ];
10 | }
11 | });
--------------------------------------------------------------------------------
/resources/[system]/[builders]/webpack/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "webpack-builder",
3 | "version": "1.0.1",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "dependencies": {
12 | "async": "^3.1.0",
13 | "webpack": "^4.41.2",
14 | "worker-farm": "^1.7.0"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/resources/[gameplay]/chat/html/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "./",
4 | "module": "es6",
5 | "strict": true,
6 | "moduleResolution": "node",
7 | "target": "es6",
8 | "allowJs": true,
9 | "lib": [
10 | "es2017",
11 | "dom"
12 | ]
13 | },
14 | "include": [
15 | "./**/*"
16 | ],
17 | "exclude": []
18 | }
--------------------------------------------------------------------------------
/resources/[system]/runcode/runcode_cl.lua:
--------------------------------------------------------------------------------
1 | RegisterNetEvent('runcode:gotSnippet')
2 |
3 | AddEventHandler('runcode:gotSnippet', function(id, lang, code)
4 | local res, err = RunCode(lang, code)
5 |
6 | if not err then
7 | if type(res) == 'vector3' then
8 | res = json.encode({ table.unpack(res) })
9 | elseif type(res) == 'table' then
10 | res = json.encode(res)
11 | end
12 | end
13 |
14 | TriggerServerEvent('runcode:gotResult', id, res, err)
15 | end)
--------------------------------------------------------------------------------
/resources/[gameplay]/chat/html/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/resources/[test]/fivem/fxmanifest.lua:
--------------------------------------------------------------------------------
1 | -- This resource is part of the default Cfx.re asset pack (cfx-server-data)
2 | -- Altering or recreating for local use only is strongly discouraged.
3 |
4 | version '1.0.0'
5 | author 'Cfx.re '
6 | description 'A compatibility resource to load basic-gamemode.'
7 | repository 'https://github.com/citizenfx/cfx-server-data'
8 |
9 | -- compatibility wrapper
10 | fx_version 'adamant'
11 | game 'common'
12 |
13 | dependency 'basic-gamemode'
14 |
--------------------------------------------------------------------------------
/resources/[system]/[builders]/yarn/fxmanifest.lua:
--------------------------------------------------------------------------------
1 | -- This resource is part of the default Cfx.re asset pack (cfx-server-data)
2 | -- Altering or recreating for local use only is strongly discouraged.
3 |
4 | version '1.0.0'
5 | author 'Cfx.re '
6 | description 'Builds resources with yarn. To learn more: https://classic.yarnpkg.com'
7 | repository 'https://github.com/citizenfx/cfx-server-data'
8 |
9 | fx_version 'adamant'
10 | game 'common'
11 |
12 | server_script 'yarn_builder.js'
13 |
--------------------------------------------------------------------------------
/resources/[gameplay]/[examples]/money-fountain-example-map/fxmanifest.lua:
--------------------------------------------------------------------------------
1 | -- This resource is part of the default Cfx.re asset pack (cfx-server-data)
2 | -- Altering or recreating for local use only is strongly discouraged.
3 |
4 | version '1.0.0'
5 | description 'An example money system fountain spawn point.'
6 | repository 'https://github.com/citizenfx/cfx-server-data'
7 | author 'Cfx.re '
8 |
9 | fx_version 'cerulean'
10 | game 'gta5'
11 |
12 | map 'map.lua'
13 |
14 | dependency 'money-fountain'
15 |
--------------------------------------------------------------------------------
/resources/[gameplay]/[examples]/ped-money-drops/fxmanifest.lua:
--------------------------------------------------------------------------------
1 | -- This resource is part of the default Cfx.re asset pack (cfx-server-data)
2 | -- Altering or recreating for local use only is strongly discouraged.
3 |
4 | version '1.0.0'
5 | description 'An example money system client.'
6 | author 'Cfx.re '
7 | repository 'https://github.com/citizenfx/cfx-server-data'
8 |
9 | fx_version 'bodacious'
10 | game 'gta5'
11 |
12 | client_script 'client.lua'
13 | server_script 'server.lua'
14 |
15 | lua54 'yes'
16 |
--------------------------------------------------------------------------------
/resources/[system]/[builders]/webpack/fxmanifest.lua:
--------------------------------------------------------------------------------
1 | -- This resource is part of the default Cfx.re asset pack (cfx-server-data)
2 | -- Altering or recreating for local use only is strongly discouraged.
3 |
4 | version '1.0.0'
5 | author 'Cfx.re '
6 | description 'Builds resources with webpack. To learn more: https://webpack.js.org'
7 | repository 'https://github.com/citizenfx/cfx-server-data'
8 |
9 | dependency 'yarn'
10 | server_script 'webpack_builder.js'
11 |
12 | fx_version 'adamant'
13 | game 'common'
14 |
--------------------------------------------------------------------------------
/resources/[gameplay]/player-data/fxmanifest.lua:
--------------------------------------------------------------------------------
1 | -- This resource is part of the default Cfx.re asset pack (cfx-server-data)
2 | -- Altering or recreating for local use only is strongly discouraged.
3 |
4 | version '1.0.0'
5 | description 'A basic resource for storing player identifiers.'
6 | author 'Cfx.re '
7 | repository 'https://github.com/citizenfx/cfx-server-data'
8 |
9 | fx_version 'bodacious'
10 | game 'common'
11 |
12 | server_script 'server.lua'
13 |
14 | provides {
15 | 'cfx.re/playerData.v1alpha1'
16 | }
17 |
--------------------------------------------------------------------------------
/resources/[system]/sessionmanager/fxmanifest.lua:
--------------------------------------------------------------------------------
1 | -- This resource is part of the default Cfx.re asset pack (cfx-server-data)
2 | -- Altering or recreating for local use only is strongly discouraged.
3 |
4 | version '1.0.0'
5 | author 'Cfx.re '
6 | description 'Handles the "host lock" for non-OneSync servers. Do not disable.'
7 | repository 'https://github.com/citizenfx/cfx-server-data'
8 |
9 | fx_version 'cerulean'
10 | games { 'gta4', 'gta5' }
11 |
12 | server_script 'server/host_lock.lua'
13 | client_script 'client/empty.lua'
--------------------------------------------------------------------------------
/resources/[gamemodes]/[maps]/fivem-map-hipster/fxmanifest.lua:
--------------------------------------------------------------------------------
1 | -- This resource is part of the default Cfx.re asset pack (cfx-server-data)
2 | -- Altering or recreating for local use only is strongly discouraged.
3 |
4 | version '1.0.0'
5 | author 'Cfx.re '
6 | description 'Example spawn points for FiveM with a "hipster" model.'
7 | repository 'https://github.com/citizenfx/cfx-server-data'
8 |
9 | resource_type 'map' { gameTypes = { ['basic-gamemode'] = true } }
10 |
11 | map 'map.lua'
12 |
13 | fx_version 'adamant'
14 | game 'gta5'
15 |
--------------------------------------------------------------------------------
/resources/[gamemodes]/[maps]/fivem-map-skater/fxmanifest.lua:
--------------------------------------------------------------------------------
1 | -- This resource is part of the default Cfx.re asset pack (cfx-server-data)
2 | -- Altering or recreating for local use only is strongly discouraged.
3 |
4 | version '1.0.0'
5 | author 'Cfx.re '
6 | description 'Example spawn points for FiveM with a "skater" model.'
7 | repository 'https://github.com/citizenfx/cfx-server-data'
8 |
9 | resource_type 'map' { gameTypes = { ['basic-gamemode'] = true } }
10 |
11 | map 'map.lua'
12 |
13 | fx_version 'adamant'
14 | game 'gta5'
15 |
--------------------------------------------------------------------------------
/resources/[test]/example-loadscreen/fxmanifest.lua:
--------------------------------------------------------------------------------
1 | -- This resource is part of the default Cfx.re asset pack (cfx-server-data)
2 | -- Altering or recreating for local use only is strongly discouraged.
3 |
4 | version '1.0.0'
5 | author 'Cfx.re '
6 | description 'Example loading screen.'
7 | repository 'https://github.com/citizenfx/cfx-server-data'
8 |
9 | files {
10 | 'index.html',
11 | 'keks.css',
12 | 'bankgothic.ttf',
13 | 'loadscreen.jpg'
14 | }
15 |
16 | loadscreen 'index.html'
17 |
18 | fx_version 'bodacious'
19 | game 'gta5'
--------------------------------------------------------------------------------
/resources/[gameplay]/[examples]/money/fxmanifest.lua:
--------------------------------------------------------------------------------
1 | -- This resource is part of the default Cfx.re asset pack (cfx-server-data)
2 | -- Altering or recreating for local use only is strongly discouraged.
3 |
4 | version '1.0.0'
5 | description 'An example money system using KVS.'
6 | repository 'https://github.com/citizenfx/cfx-server-data'
7 | author 'Cfx.re '
8 |
9 | fx_version 'bodacious'
10 | game 'gta5'
11 |
12 | client_script 'client.lua'
13 | server_script 'server.lua'
14 |
15 | --dependency 'cfx.re/playerData.v1alpha1'
16 | lua54 'yes'
17 |
--------------------------------------------------------------------------------
/resources/[gamemodes]/basic-gamemode/fxmanifest.lua:
--------------------------------------------------------------------------------
1 | -- This resource is part of the default Cfx.re asset pack (cfx-server-data)
2 | -- Altering or recreating for local use only is strongly discouraged.
3 |
4 | version '1.0.0'
5 | author 'Cfx.re '
6 | description 'A basic freeroam gametype that uses the default spawn logic from spawnmanager.'
7 | repository 'https://github.com/citizenfx/cfx-server-data'
8 |
9 | resource_type 'gametype' { name = 'Freeroam' }
10 |
11 | client_script 'basic_client.lua'
12 |
13 | game 'common'
14 | fx_version 'adamant'
15 |
--------------------------------------------------------------------------------
/resources/[gameplay]/chat/html/config.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | defaultTemplateId: 'default', //This is the default template for 2 args1
3 | defaultAltTemplateId: 'defaultAlt', //This one for 1 arg
4 | templates: { //You can add static templates here
5 | 'default': '{0}: {1}',
6 | 'defaultAlt': '{0}',
7 | 'print': '{0}',
8 | 'example:important': '^2{0}
'
9 | },
10 | fadeTimeout: 7000,
11 | suggestionLimit: 5,
12 | style: {
13 | background: 'rgba(52, 73, 94, 0.7)',
14 | width: '38vw',
15 | height: '22%',
16 | }
17 | };
18 |
--------------------------------------------------------------------------------
/resources/[system]/baseevents/fxmanifest.lua:
--------------------------------------------------------------------------------
1 | -- This resource is part of the default Cfx.re asset pack (cfx-server-data)
2 | -- Altering or recreating for local use only is strongly discouraged.
3 |
4 | version '1.0.0'
5 | author 'Cfx.re '
6 | description 'Adds basic events for developers to use in their scripts. Some third party resources may depend on this resource.'
7 | repository 'https://github.com/citizenfx/cfx-server-data'
8 |
9 | client_script 'deathevents.lua'
10 | client_script 'vehiclechecker.lua'
11 | server_script 'server.lua'
12 |
13 | fx_version 'adamant'
14 | game 'gta5'
--------------------------------------------------------------------------------
/resources/[system]/rconlog/rconlog_client.lua:
--------------------------------------------------------------------------------
1 | RegisterNetEvent('rlUpdateNames')
2 |
3 | AddEventHandler('rlUpdateNames', function()
4 | local names = {}
5 |
6 | for i = 0, 31 do
7 | if NetworkIsPlayerActive(i) then
8 | names[GetPlayerServerId(i)] = { id = i, name = GetPlayerName(i) }
9 | end
10 | end
11 |
12 | TriggerServerEvent('rlUpdateNamesResult', names)
13 | end)
14 |
15 | Citizen.CreateThread(function()
16 | while true do
17 | Wait(0)
18 |
19 | if NetworkIsSessionStarted() then
20 | TriggerServerEvent('rlPlayerActivated')
21 |
22 | return
23 | end
24 | end
25 | end)
--------------------------------------------------------------------------------
/resources/[gameplay]/[examples]/money-fountain/fxmanifest.lua:
--------------------------------------------------------------------------------
1 | -- This resource is part of the default Cfx.re asset pack (cfx-server-data)
2 | -- Altering or recreating for local use only is strongly discouraged.
3 |
4 | version '1.0.0'
5 | description 'An example money system client containing a money fountain.'
6 | repository 'https://github.com/citizenfx/cfx-server-data'
7 | author 'Cfx.re '
8 |
9 | fx_version 'bodacious'
10 | game 'gta5'
11 |
12 | client_script 'client.lua'
13 | server_script 'server.lua'
14 |
15 | shared_script 'mapdata.lua'
16 |
17 | dependencies {
18 | 'mapmanager',
19 | 'money'
20 | }
21 |
22 | lua54 'yes'
23 |
--------------------------------------------------------------------------------
/resources/[gamemodes]/[maps]/redm-map-one/fxmanifest.lua:
--------------------------------------------------------------------------------
1 | -- This resource is part of the default Cfx.re asset pack (cfx-server-data)
2 | -- Altering or recreating for local use only is strongly discouraged.
3 |
4 | version '1.0.0'
5 | author 'Cfx.re '
6 | description 'Example spawn points for RedM.'
7 | repository 'https://github.com/citizenfx/cfx-server-data'
8 |
9 | resource_type 'map' { gameTypes = { ['basic-gamemode'] = true } }
10 |
11 | map 'map.lua'
12 |
13 | fx_version 'adamant'
14 | game 'rdr3'
15 |
16 | rdr3_warning 'I acknowledge that this is a prerelease build of RedM, and I am aware my resources *will* become incompatible once RedM ships.'
17 |
--------------------------------------------------------------------------------
/resources/[gameplay]/chat-theme-gtao/fxmanifest.lua:
--------------------------------------------------------------------------------
1 | -- This resource is part of the default Cfx.re asset pack (cfx-server-data)
2 | -- Altering or recreating for local use only is strongly discouraged.
3 |
4 | version '1.0.0'
5 | author 'Cfx.re '
6 | description 'A GTA Online-styled theme for the chat resource.'
7 | repository 'https://github.com/citizenfx/cfx-server-data'
8 |
9 | file 'style.css'
10 | file 'shadow.js'
11 |
12 | chat_theme 'gtao' {
13 | styleSheet = 'style.css',
14 | script = 'shadow.js',
15 | msgTemplates = {
16 | default = '{0}{1}'
17 | }
18 | }
19 |
20 | game 'common'
21 | fx_version 'adamant'
22 |
--------------------------------------------------------------------------------
/resources/[system]/rconlog/fxmanifest.lua:
--------------------------------------------------------------------------------
1 | -- This resource is part of the default Cfx.re asset pack (cfx-server-data)
2 | -- Altering or recreating for local use only is strongly discouraged.
3 |
4 | version '1.0.0'
5 | author 'Cfx.re '
6 | description 'Handles old-style server player management commands.'
7 | repository 'https://github.com/citizenfx/cfx-server-data'
8 |
9 | client_script 'rconlog_client.lua'
10 | server_script 'rconlog_server.lua'
11 |
12 | fx_version 'adamant'
13 | games { 'gta5', 'rdr3' }
14 |
15 | rdr3_warning 'I acknowledge that this is a prerelease build of RedM, and I am aware my resources *will* become incompatible once RedM ships.'
16 |
--------------------------------------------------------------------------------
/resources/[system]/hardcap/fxmanifest.lua:
--------------------------------------------------------------------------------
1 | -- This resource is part of the default Cfx.re asset pack (cfx-server-data)
2 | -- Altering or recreating for local use only is strongly discouraged.
3 |
4 | version '1.0.0'
5 | author 'Cfx.re '
6 | description 'Limits the number of players to the amount set by sv_maxclients in your server.cfg.'
7 | repository 'https://github.com/citizenfx/cfx-server-data'
8 |
9 | client_script 'client.lua'
10 | server_script 'server.lua'
11 |
12 | fx_version 'adamant'
13 | games { 'gta5', 'rdr3' }
14 | rdr3_warning 'I acknowledge that this is a prerelease build of RedM, and I am aware my resources *will* become incompatible once RedM ships.'
15 |
--------------------------------------------------------------------------------
/resources/[system]/sessionmanager-rdr3/fxmanifest.lua:
--------------------------------------------------------------------------------
1 | -- This resource is part of the default Cfx.re asset pack (cfx-server-data)
2 | -- Altering or recreating for local use only is strongly discouraged.
3 |
4 | version '1.0.0'
5 | author 'Cfx.re '
6 | description 'Handles Social Club conductor session API for RedM. Do not disable.'
7 | repository 'https://github.com/citizenfx/cfx-server-data'
8 |
9 | fx_version 'adamant'
10 | game 'rdr3'
11 | rdr3_warning 'I acknowledge that this is a prerelease build of RedM, and I am aware my resources *will* become incompatible once RedM ships.'
12 |
13 | dependencies {
14 | 'yarn'
15 | }
16 |
17 | server_script 'sm_server.js'
18 |
--------------------------------------------------------------------------------
/resources/[system]/runcode/runcode_shared.lua:
--------------------------------------------------------------------------------
1 | local runners = {}
2 |
3 | function runners.lua(arg)
4 | local code, err = load('return ' .. arg, '@runcode')
5 |
6 | -- if failed, try without return
7 | if err then
8 | code, err = load(arg, '@runcode')
9 | end
10 |
11 | if err then
12 | print(err)
13 | return nil, err
14 | end
15 |
16 | local status, result = pcall(code)
17 | print(result)
18 |
19 | if status then
20 | return result
21 | end
22 |
23 | return nil, result
24 | end
25 |
26 | function runners.js(arg)
27 | return table.unpack(exports[GetCurrentResourceName()]:runJS(arg))
28 | end
29 |
30 | function RunCode(lang, str)
31 | return runners[lang](str)
32 | end
--------------------------------------------------------------------------------
/resources/[managers]/spawnmanager/fxmanifest.lua:
--------------------------------------------------------------------------------
1 | -- This resource is part of the default Cfx.re asset pack (cfx-server-data)
2 | -- Altering or recreating for local use only is strongly discouraged.
3 |
4 | version '1.0.0'
5 | author 'Cfx.re '
6 | description 'Handles spawning a player in a unified fashion to prevent resources from having to implement custom spawn logic.'
7 | repository 'https://github.com/citizenfx/cfx-server-data'
8 |
9 | client_script 'spawnmanager.lua'
10 |
11 | fx_version 'adamant'
12 | games { 'rdr3', 'gta5' }
13 |
14 | rdr3_warning 'I acknowledge that this is a prerelease build of RedM, and I am aware my resources *will* become incompatible once RedM ships.'
15 |
--------------------------------------------------------------------------------
/resources/[gameplay]/chat/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "chat",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "license": "MIT",
6 | "dependencies": {
7 | "@types/vue": "^2.0.0",
8 | "copy-webpack-plugin": "^5.1.1",
9 | "css-loader": "^3.4.2",
10 | "html-webpack-inline-source-plugin": "^0.0.10",
11 | "html-webpack-plugin": "^3.2.0",
12 | "ts-loader": "^6.2.1",
13 | "typescript": "^3.8.3",
14 | "vue": "^2.6.11",
15 | "vue-loader": "^15.9.0",
16 | "vue-template-compiler": "^2.6.11",
17 | "webpack": "4"
18 | },
19 | "devDependencies": {
20 | "command-line-args": "^5.1.1",
21 | "webpack-cli": "^3.3.11",
22 | "webpack-dev-server": "^3.10.3"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/resources/[system]/baseevents/server.lua:
--------------------------------------------------------------------------------
1 | RegisterServerEvent('baseevents:onPlayerDied')
2 | RegisterServerEvent('baseevents:onPlayerKilled')
3 | RegisterServerEvent('baseevents:onPlayerWasted')
4 | RegisterServerEvent('baseevents:enteringVehicle')
5 | RegisterServerEvent('baseevents:enteringAborted')
6 | RegisterServerEvent('baseevents:enteredVehicle')
7 | RegisterServerEvent('baseevents:leftVehicle')
8 |
9 | AddEventHandler('baseevents:onPlayerKilled', function(killedBy, data)
10 | local victim = source
11 |
12 | RconLog({msgType = 'playerKilled', victim = victim, attacker = killedBy, data = data})
13 | end)
14 |
15 | AddEventHandler('baseevents:onPlayerDied', function(killedBy, pos)
16 | local victim = source
17 |
18 | RconLog({msgType = 'playerDied', victim = victim, attackerType = killedBy, pos = pos})
19 | end)
--------------------------------------------------------------------------------
/resources/[gameplay]/[examples]/money/client.lua:
--------------------------------------------------------------------------------
1 | local moneyTypes = {
2 | cash = `MP0_WALLET_BALANCE`,
3 | bank = `BANK_BALANCE`,
4 | }
5 |
6 | RegisterNetEvent('money:displayUpdate')
7 |
8 | AddEventHandler('money:displayUpdate', function(type, money)
9 | local stat = moneyTypes[type]
10 | if not stat then return end
11 | StatSetInt(stat, math.floor(money))
12 | end)
13 |
14 | TriggerServerEvent('money:requestDisplay')
15 |
16 | CreateThread(function()
17 | while true do
18 | Wait(0)
19 |
20 | if IsControlJustPressed(0, 20) then
21 | SetMultiplayerBankCash()
22 | SetMultiplayerWalletCash()
23 |
24 | Wait(4350)
25 |
26 | RemoveMultiplayerBankCash()
27 | RemoveMultiplayerWalletCash()
28 | end
29 | end
30 | end)
--------------------------------------------------------------------------------
/resources/[system]/runcode/fxmanifest.lua:
--------------------------------------------------------------------------------
1 | -- This resource is part of the default Cfx.re asset pack (cfx-server-data)
2 | -- Altering or recreating for local use only is strongly discouraged.
3 |
4 | version '1.0.0'
5 | author 'Cfx.re '
6 | description 'Allows server owners to execute arbitrary server-side or client-side JavaScript/Lua code. *Consider only using this on development servers.'
7 | repository 'https://github.com/citizenfx/cfx-server-data'
8 |
9 | game 'common'
10 | fx_version 'bodacious'
11 |
12 | client_script 'runcode_cl.lua'
13 | server_script 'runcode_sv.lua'
14 | server_script 'runcode_web.lua'
15 |
16 | shared_script 'runcode_shared.lua'
17 | shared_script 'runcode.js'
18 |
19 | client_script 'runcode_ui.lua'
20 |
21 | ui_page 'web/nui.html'
22 | files {
23 | 'web/nui.html'
24 | }
25 |
--------------------------------------------------------------------------------
/resources/[gameplay]/chat/html/utils.ts:
--------------------------------------------------------------------------------
1 | export function post(url: string, data: any) {
2 | var request = new XMLHttpRequest();
3 | request.open('POST', url, true);
4 | request.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
5 | request.send(data);
6 | }
7 |
8 | function emulate(type: string, detail = {}) {
9 | const detailRef = {
10 | type,
11 | ...detail
12 | };
13 |
14 | window.dispatchEvent(new CustomEvent('message', {
15 | detail: detailRef
16 | }));
17 | }
18 |
19 | (window as any)['emulate'] = emulate;
20 |
21 | (window as any)['demo'] = () => {
22 | emulate('ON_MESSAGE', {
23 | message: {
24 | args: [ 'me', 'hello!' ]
25 | }
26 | })
27 |
28 | emulate('ON_SCREEN_STATE_CHANGE', {
29 | shouldHide: false
30 | });
31 | };
--------------------------------------------------------------------------------
/resources/[system]/hardcap/server.lua:
--------------------------------------------------------------------------------
1 | local playerCount = 0
2 | local list = {}
3 |
4 | RegisterServerEvent('hardcap:playerActivated')
5 |
6 | AddEventHandler('hardcap:playerActivated', function()
7 | if not list[source] then
8 | playerCount = playerCount + 1
9 | list[source] = true
10 | end
11 | end)
12 |
13 | AddEventHandler('playerDropped', function()
14 | if list[source] then
15 | playerCount = playerCount - 1
16 | list[source] = nil
17 | end
18 | end)
19 |
20 | AddEventHandler('playerConnecting', function(name, setReason)
21 | local cv = GetConvarInt('sv_maxclients', 32)
22 |
23 | print('Connecting: ' .. name .. '^7')
24 |
25 | if playerCount >= cv then
26 | print('Full. :(')
27 |
28 | setReason('This server is full (past ' .. tostring(cv) .. ' players).')
29 | CancelEvent()
30 | end
31 | end)
32 |
--------------------------------------------------------------------------------
/resources/[gameplay]/[examples]/money-fountain/mapdata.lua:
--------------------------------------------------------------------------------
1 | -- define the money fountain list (SHARED SCRIPT)
2 | moneyFountains = {}
3 |
4 | -- index to know what to remove
5 | local fountainIdx = 1
6 |
7 | AddEventHandler('getMapDirectives', function(add)
8 | -- add a 'money_fountain' map directive
9 | add('money_fountain', function(state, name)
10 | return function(data)
11 | local coords = data[1]
12 | local amount = data.amount or 100
13 |
14 | local idx = fountainIdx
15 | fountainIdx += 1
16 |
17 | moneyFountains[idx] = {
18 | id = name,
19 | coords = coords,
20 | amount = amount
21 | }
22 |
23 | state.add('idx', idx)
24 | end
25 | end, function(state)
26 | moneyFountains[state.idx] = nil
27 | end)
28 | end)
--------------------------------------------------------------------------------
/resources/[gameplay]/chat/fxmanifest.lua:
--------------------------------------------------------------------------------
1 | -- This resource is part of the default Cfx.re asset pack (cfx-server-data)
2 | -- Altering or recreating for local use only is strongly discouraged.
3 |
4 | version '1.0.0'
5 | author 'Cfx.re '
6 | description 'Provides baseline chat functionality using a NUI-based interface.'
7 | repository 'https://github.com/citizenfx/cfx-server-data'
8 |
9 | ui_page 'dist/ui.html'
10 |
11 | client_script 'cl_chat.lua'
12 | server_script 'sv_chat.lua'
13 |
14 | files {
15 | 'dist/ui.html',
16 | 'dist/index.css',
17 | 'html/vendor/*.css',
18 | 'html/vendor/fonts/*.woff2',
19 | }
20 |
21 | fx_version 'adamant'
22 | games { 'rdr3', 'gta5' }
23 | rdr3_warning 'I acknowledge that this is a prerelease build of RedM, and I am aware my resources *will* become incompatible once RedM ships.'
24 |
25 | dependencies {
26 | 'yarn',
27 | 'webpack'
28 | }
29 |
30 | webpack_config 'webpack.config.js'
31 |
--------------------------------------------------------------------------------
/resources/[gameplay]/playernames/fxmanifest.lua:
--------------------------------------------------------------------------------
1 | -- This resource is part of the default Cfx.re asset pack (cfx-server-data)
2 | -- Altering or recreating for local use only is strongly discouraged.
3 |
4 | version '1.0.0'
5 | author 'Cfx.re '
6 | description 'A basic resource for displaying player names.'
7 | repository 'https://github.com/citizenfx/cfx-server-data'
8 |
9 | -- add scripts
10 | client_script 'playernames_api.lua'
11 | server_script 'playernames_api.lua'
12 |
13 | client_script 'playernames_cl.lua'
14 | server_script 'playernames_sv.lua'
15 |
16 | -- make exports
17 | local exportList = {
18 | 'setComponentColor',
19 | 'setComponentAlpha',
20 | 'setComponentVisibility',
21 | 'setWantedLevel',
22 | 'setHealthBarColor',
23 | 'setNameTemplate'
24 | }
25 |
26 | exports(exportList)
27 | server_exports(exportList)
28 |
29 | -- add files
30 | files {
31 | 'template/template.lua'
32 | }
33 |
34 | -- support the latest resource manifest
35 | fx_version 'adamant'
36 | game 'gta5'
37 |
--------------------------------------------------------------------------------
/resources/[managers]/mapmanager/fxmanifest.lua:
--------------------------------------------------------------------------------
1 | -- This resource is part of the default Cfx.re asset pack (cfx-server-data)
2 | -- Altering or recreating for local use only is strongly discouraged.
3 |
4 | version '1.0.0'
5 | author 'Cfx.re '
6 | description 'A flexible handler for game type/map association.'
7 | repository 'https://github.com/citizenfx/cfx-server-data'
8 |
9 | client_scripts {
10 | "mapmanager_shared.lua",
11 | "mapmanager_client.lua"
12 | }
13 |
14 | server_scripts {
15 | "mapmanager_shared.lua",
16 | "mapmanager_server.lua"
17 | }
18 |
19 | fx_version 'adamant'
20 | games { 'gta5', 'rdr3' }
21 |
22 | server_export "getCurrentGameType"
23 | server_export "getCurrentMap"
24 | server_export "changeGameType"
25 | server_export "changeMap"
26 | server_export "doesMapSupportGameType"
27 | server_export "getMaps"
28 | server_export "roundEnded"
29 |
30 | rdr3_warning 'I acknowledge that this is a prerelease build of RedM, and I am aware my resources *will* become incompatible once RedM ships.'
31 |
--------------------------------------------------------------------------------
/resources/[gameplay]/chat/html/Suggestions.vue:
--------------------------------------------------------------------------------
1 |
2 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/resources/[gameplay]/[examples]/ped-money-drops/server.lua:
--------------------------------------------------------------------------------
1 | local safePositions = {}
2 |
3 | RegisterNetEvent('money:allowPickupNear')
4 |
5 | AddEventHandler('money:allowPickupNear', function(pedId)
6 | local entity = NetworkGetEntityFromNetworkId(pedId)
7 |
8 | Wait(250)
9 |
10 | if not DoesEntityExist(entity) then
11 | return
12 | end
13 |
14 | if GetEntityHealth(entity) > 100 then
15 | return
16 | end
17 |
18 | local coords = GetEntityCoords(entity)
19 | safePositions[pedId] = coords
20 | end)
21 |
22 | RegisterNetEvent('money:tryPickup')
23 |
24 | AddEventHandler('money:tryPickup', function(entity)
25 | if not safePositions[entity] then
26 | return
27 | end
28 |
29 | local source = source
30 | local playerPed = GetPlayerPed(source)
31 | local coords = GetEntityCoords(playerPed)
32 |
33 | if #(safePositions[entity] - coords) < 2.5 then
34 | exports['money']:addMoney(source, 'cash', 40)
35 | end
36 |
37 | safePositions[entity] = nil
38 | end)
39 |
40 | AddEventHandler('entityRemoved', function(entity)
41 | safePositions[entity] = nil
42 | end)
--------------------------------------------------------------------------------
/resources/[system]/runcode/runcode_sv.lua:
--------------------------------------------------------------------------------
1 | function GetPrivs(source)
2 | return {
3 | canServer = IsPlayerAceAllowed(source, 'command.run'),
4 | canClient = IsPlayerAceAllowed(source, 'command.crun'),
5 | canSelf = IsPlayerAceAllowed(source, 'runcode.self'),
6 | }
7 | end
8 |
9 | RegisterCommand('run', function(source, args, rawCommand)
10 | local res, err = RunCode('lua', rawCommand:sub(4))
11 | end, true)
12 |
13 | RegisterCommand('crun', function(source, args, rawCommand)
14 | if not source then
15 | return
16 | end
17 |
18 | TriggerClientEvent('runcode:gotSnippet', source, -1, 'lua', rawCommand:sub(5))
19 | end, true)
20 |
21 | RegisterCommand('runcode', function(source, args, rawCommand)
22 | if not source then
23 | return
24 | end
25 |
26 | local df = LoadResourceFile(GetCurrentResourceName(), 'data.json')
27 | local saveData = {}
28 |
29 | if df then
30 | saveData = json.decode(df)
31 | end
32 |
33 | local p = GetPrivs(source)
34 |
35 | if not p.canServer and not p.canClient and not p.canSelf then
36 | return
37 | end
38 |
39 | p.saveData = saveData
40 |
41 | TriggerClientEvent('runcode:openUi', source, p)
42 | end, true)
--------------------------------------------------------------------------------
/resources/[gameplay]/playernames/playernames_sv.lua:
--------------------------------------------------------------------------------
1 | local curTemplate
2 | local curTags = {}
3 |
4 | local activePlayers = {}
5 |
6 | local function detectUpdates()
7 | SetTimeout(500, detectUpdates)
8 |
9 | local template = GetConvar('playerNames_template', '[{{id}}] {{name}}')
10 |
11 | if curTemplate ~= template then
12 | setNameTemplate(-1, template)
13 |
14 | curTemplate = template
15 | end
16 |
17 | template = GetConvar('playerNames_svTemplate', '[{{id}}] {{name}}')
18 |
19 | for v, _ in pairs(activePlayers) do
20 | local newTag = formatPlayerNameTag(v, template)
21 | if newTag ~= curTags[v] then
22 | setName(v, newTag)
23 |
24 | curTags[v] = newTag
25 | end
26 | end
27 |
28 | for i, tag in pairs(curTags) do
29 | if not activePlayers[i] then
30 | curTags[i] = nil -- in case curTags doesnt get cleared when the player left, clear it now.
31 | end
32 | end
33 | end
34 |
35 | AddEventHandler('playerDropped', function()
36 | curTags[source] = nil
37 | activePlayers[source] = nil
38 | end)
39 |
40 | RegisterNetEvent('playernames:init')
41 | AddEventHandler('playernames:init', function()
42 | reconfigure(source)
43 | activePlayers[source] = true
44 | end)
45 |
46 | detectUpdates()
47 |
--------------------------------------------------------------------------------
/resources/[gameplay]/chat/webpack.config.js:
--------------------------------------------------------------------------------
1 | const HtmlWebpackPlugin = require('html-webpack-plugin');
2 | const HtmlWebpackInlineSourcePlugin = require('html-webpack-inline-source-plugin');
3 | const VueLoaderPlugin = require('vue-loader/lib/plugin');
4 | const CopyPlugin = require('copy-webpack-plugin');
5 |
6 | module.exports = {
7 | mode: 'production',
8 | entry: './html/main.ts',
9 | module: {
10 | rules: [
11 | {
12 | test: /\.ts$/,
13 | loader: 'ts-loader',
14 | exclude: /node_modules/,
15 | options: {
16 | appendTsSuffixTo: [/\.vue$/],
17 | }
18 | },
19 | {
20 | test: /\.vue$/,
21 | loader: 'vue-loader'
22 | },
23 | ]
24 | },
25 | plugins: [
26 | new VueLoaderPlugin(),
27 | new HtmlWebpackPlugin({
28 | inlineSource: '.(js|css)$',
29 | template: './html/index.html',
30 | filename: 'ui.html'
31 | }),
32 | new HtmlWebpackInlineSourcePlugin(),
33 | new CopyPlugin([
34 | { from: 'html/index.css', to: 'index.css' }
35 | ]),
36 | ],
37 | resolve: {
38 | extensions: [ '.ts', '.js' ]
39 | },
40 | output: {
41 | filename: 'chat.js',
42 | path: __dirname + '/dist/'
43 | },
44 | //devtool: 'inline-source-map'
45 | };
--------------------------------------------------------------------------------
/resources/[system]/runcode/web/nui.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | runcode nui
4 |
5 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/resources/[gameplay]/[examples]/ped-money-drops/client.lua:
--------------------------------------------------------------------------------
1 | AddEventHandler('gameEventTriggered', function(eventName, args)
2 | if eventName == 'CEventNetworkEntityDamage' then
3 | local victim = args[1]
4 | local culprit = args[2]
5 | local isDead = args[4] == 1
6 |
7 | if isDead then
8 | local origCoords = GetEntityCoords(victim)
9 | local pickup = CreatePickupRotate(`PICKUP_MONEY_VARIABLE`, origCoords.x, origCoords.y, origCoords.z - 0.7, 0.0, 0.0, 0.0, 512, 0, false, 0)
10 | local netId = PedToNet(victim)
11 |
12 | local undoStuff = { false }
13 |
14 | CreateThread(function()
15 | local self = PlayerPedId()
16 |
17 | while not undoStuff[1] do
18 | Wait(50)
19 |
20 | if #(GetEntityCoords(self) - origCoords) < 2.5 and HasPickupBeenCollected(pickup) then
21 | TriggerServerEvent('money:tryPickup', netId)
22 |
23 | RemovePickup(pickup)
24 | break
25 | end
26 | end
27 |
28 | undoStuff[1] = true
29 | end)
30 |
31 | SetTimeout(15000, function()
32 | if not undoStuff[1] then
33 | RemovePickup(pickup)
34 | undoStuff[1] = true
35 | end
36 | end)
37 |
38 | TriggerServerEvent('money:allowPickupNear', netId)
39 | end
40 | end
41 | end)
--------------------------------------------------------------------------------
/resources/[gameplay]/playernames/template/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014 - 2017 Aapo Talvensaari
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without modification,
5 | are permitted provided that the following conditions are met:
6 |
7 | * Redistributions of source code must retain the above copyright notice, this
8 | list of conditions and the following disclaimer.
9 |
10 | * Redistributions in binary form must reproduce the above copyright notice, this
11 | list of conditions and the following disclaimer in the documentation and/or
12 | other materials provided with the distribution.
13 |
14 | * Neither the name of the {organization} nor the names of its
15 | contributors may be used to endorse or promote products derived from
16 | this software without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 |
--------------------------------------------------------------------------------
/resources/[system]/runcode/runcode_ui.lua:
--------------------------------------------------------------------------------
1 | local openData
2 |
3 | RegisterNetEvent('runcode:openUi')
4 |
5 | AddEventHandler('runcode:openUi', function(options)
6 | openData = {
7 | type = 'open',
8 | options = options,
9 | url = 'http://' .. GetCurrentServerEndpoint() .. '/' .. GetCurrentResourceName() .. '/',
10 | res = GetCurrentResourceName()
11 | }
12 |
13 | SendNuiMessage(json.encode(openData))
14 | end)
15 |
16 | RegisterNUICallback('getOpenData', function(args, cb)
17 | cb(openData)
18 | end)
19 |
20 | RegisterNUICallback('doOk', function(args, cb)
21 | SendNuiMessage(json.encode({
22 | type = 'ok'
23 | }))
24 |
25 | SetNuiFocus(true, true)
26 |
27 | cb('ok')
28 | end)
29 |
30 | RegisterNUICallback('doClose', function(args, cb)
31 | SendNuiMessage(json.encode({
32 | type = 'close'
33 | }))
34 |
35 | SetNuiFocus(false, false)
36 |
37 | cb('ok')
38 | end)
39 |
40 | local rcCbs = {}
41 | local id = 1
42 |
43 | RegisterNUICallback('runCodeInBand', function(args, cb)
44 | id = id + 1
45 |
46 | rcCbs[id] = cb
47 |
48 | TriggerServerEvent('runcode:runInBand', id, args)
49 | end)
50 |
51 | RegisterNetEvent('runcode:inBandResult')
52 |
53 | AddEventHandler('runcode:inBandResult', function(id, result)
54 | if rcCbs[id] then
55 | local cb = rcCbs[id]
56 | rcCbs[id] = nil
57 |
58 | cb(result)
59 | end
60 | end)
61 |
62 | AddEventHandler('onResourceStop', function(resourceName)
63 | if resourceName == GetCurrentResourceName() then
64 | SetNuiFocus(false, false)
65 | end
66 | end)
--------------------------------------------------------------------------------
/resources/[system]/[builders]/webpack/webpack_runner.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 | const path = require('path');
3 | const fs = require('fs');
4 |
5 | function getStat(path) {
6 | try {
7 | const stat = fs.statSync(path);
8 |
9 | return stat ? {
10 | mtime: stat.mtimeMs,
11 | size: stat.size,
12 | inode: stat.ino,
13 | } : null;
14 | } catch {
15 | return null;
16 | }
17 | }
18 |
19 | class SaveStatePlugin {
20 | constructor(inp) {
21 | this.cache = [];
22 | this.cachePath = inp.cachePath;
23 | }
24 |
25 | apply(compiler) {
26 | compiler.hooks.afterCompile.tap('SaveStatePlugin', (compilation) => {
27 | for (const file of compilation.fileDependencies) {
28 | this.cache.push({
29 | name: file,
30 | stats: getStat(file)
31 | });
32 | }
33 | });
34 |
35 | compiler.hooks.done.tap('SaveStatePlugin', (stats) => {
36 | if (stats.hasErrors()) {
37 | return;
38 | }
39 |
40 | fs.writeFile(this.cachePath, JSON.stringify(this.cache), () => {
41 |
42 | });
43 | });
44 | }
45 | }
46 |
47 | module.exports = (inp, callback) => {
48 | const config = require(inp.configPath);
49 |
50 | config.context = inp.resourcePath;
51 |
52 | if (config.output && config.output.path) {
53 | config.output.path = path.resolve(inp.resourcePath, config.output.path);
54 | }
55 |
56 | if (!config.plugins) {
57 | config.plugins = [];
58 | }
59 |
60 | config.plugins.push(new SaveStatePlugin(inp));
61 |
62 | webpack(config, (err, stats) => {
63 | if (err) {
64 | callback(err);
65 | return;
66 | }
67 |
68 | if (stats.hasErrors()) {
69 | callback(null, stats.toJson());
70 | return;
71 | }
72 |
73 | callback(null, {});
74 | });
75 | };
--------------------------------------------------------------------------------
/resources/[gameplay]/chat/html/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/resources/[gameplay]/chat/html/vendor/latofonts.css:
--------------------------------------------------------------------------------
1 | /* latin-ext */
2 | @font-face {
3 | font-family: 'Lato';
4 | font-style: normal;
5 | font-weight: 300;
6 | src: local('Lato Light'), local('Lato-Light'), url(fonts/LatoLight.woff2);
7 | unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
8 | }
9 | /* latin */
10 | @font-face {
11 | font-family: 'Lato';
12 | font-style: normal;
13 | font-weight: 300;
14 | src: local('Lato Light'), local('Lato-Light'), url(fonts/LatoLight2.woff2);
15 | unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
16 | }
17 | /* latin-ext */
18 | @font-face {
19 | font-family: 'Lato';
20 | font-style: normal;
21 | font-weight: 400;
22 | src: local('Lato Regular'), local('Lato-Regular'), url(fonts/LatoRegular.woff2);
23 | unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
24 | }
25 | /* latin */
26 | @font-face {
27 | font-family: 'Lato';
28 | font-style: normal;
29 | font-weight: 400;
30 | src: local('Lato Regular'), local('Lato-Regular'), url(fonts/LatoRegular2.woff2);
31 | unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
32 | }
33 | /* latin-ext */
34 | @font-face {
35 | font-family: 'Lato';
36 | font-style: normal;
37 | font-weight: 700;
38 | src: local('Lato Bold'), local('Lato-Bold'), url(fonts/LatoBold.woff2);
39 | unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
40 | }
41 | /* latin */
42 | @font-face {
43 | font-family: 'Lato';
44 | font-style: normal;
45 | font-weight: 700;
46 | src: local('Lato Bold'), local('Lato-Bold'), url(fonts/LatoBold2.woff2);
47 | unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
48 | }
49 |
--------------------------------------------------------------------------------
/resources/[gameplay]/chat/html/Suggestions.ts:
--------------------------------------------------------------------------------
1 | import CONFIG from './config';
2 | import Vue, { PropType } from 'vue';
3 |
4 | export interface Suggestion {
5 | name: string;
6 | help: string;
7 | params: string[];
8 |
9 | disabled: boolean;
10 | }
11 |
12 | export default Vue.component('suggestions', {
13 | props: {
14 | message: {
15 | type: String
16 | },
17 |
18 | suggestions: {
19 | type: Array as PropType
20 | }
21 | },
22 | data() {
23 | return {};
24 | },
25 | computed: {
26 | currentSuggestions(): Suggestion[] {
27 | if (this.message === '') {
28 | return [];
29 | }
30 | const currentSuggestions = this.suggestions.filter((s) => {
31 | if (!s.name.startsWith(this.message)) {
32 | const suggestionSplitted = s.name.split(' ');
33 | const messageSplitted = this.message.split(' ');
34 | for (let i = 0; i < messageSplitted.length; i += 1) {
35 | if (i >= suggestionSplitted.length) {
36 | return i < suggestionSplitted.length + s.params.length;
37 | }
38 | if (suggestionSplitted[i] !== messageSplitted[i]) {
39 | return false;
40 | }
41 | }
42 | }
43 | return true;
44 | }).slice(0, CONFIG.suggestionLimit);
45 |
46 | currentSuggestions.forEach((s) => {
47 | // eslint-disable-next-line no-param-reassign
48 | s.disabled = !s.name.startsWith(this.message);
49 |
50 | s.params.forEach((p, index) => {
51 | const wType = (index === s.params.length - 1) ? '.' : '\\S';
52 | const regex = new RegExp(`${s.name} (?:\\w+ ){${index}}(?:${wType}*)$`, 'g');
53 |
54 | // eslint-disable-next-line no-param-reassign
55 | // @ts-ignore
56 | p.disabled = this.message.match(regex) == null;
57 | });
58 | });
59 | return currentSuggestions;
60 | },
61 | },
62 | methods: {},
63 | });
64 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # cfx-server-data
2 | _The data repository for Cfx.re servers_
3 |
4 | ## Deprecation notice
5 | This repository itself is considered finalized/immutable and is currently archived. Pull requests or issue reports were not merged for a *while*, using the contents of the repository should still be fine (and this shouldn't change until mentioned otherwise).
6 |
7 | In the future, relevant parts of this repository will be moved to [citizenfx/fivem](https://github.com/citizenfx/fivem) to be bundled with actual server builds, and the examples (+ legacy API-like resources) will be moved to a new repository.
8 |
9 | If there's any critical issue in the code in this repository, posting on the [Cfx.re forums](https://forum.cfx.re/) may work.
10 |
11 | ## Usage
12 | 1. Make sure to `git clone`. Don't "Download ZIP", as that'll make it _much_ harder to update to newer versions.
13 | 2. Put custom resources in `resources/[local]/` if you don't want to be affected by any random messups.
14 |
15 | ### Advanced usage
16 | You can also consider using the repository as a submodule + symlink for your own Git repository:
17 |
18 | **Linux**:
19 | ```
20 | $ git submodule add https://github.com/citizenfx/cfx-server-data.git vendor/server-data
21 | $ ln -s ../vendor/server-data/resources/ 'resources/[base]/'
22 | ```
23 |
24 | **Windows**:
25 | ```
26 | > git submodule add https://github.com/citizenfx/cfx-server-data.git vendor/server-data
27 | > mklink /d resources\[base] ..\vendor\server-data\resources
28 | ```
29 |
30 | ## Policy
31 | You can make pull requests to propose changes that benefit _everyone_. Add new useful resources, change/improve
32 | existing ones - anything goes, as long as you make sure to:
33 |
34 | 1. Not break existing users/APIs.
35 | 2. Not change default behavior without a toggle.
36 | 3. Use best practices (convars over config files, native commands wherever possible, etc.)
37 |
38 | Modifying or rewriting existing resources in this repository for local use only is _strongly_ discouraged.
39 |
--------------------------------------------------------------------------------
/resources/[system]/sessionmanager/server/host_lock.lua:
--------------------------------------------------------------------------------
1 | -- whitelist c2s events
2 | RegisterServerEvent('hostingSession')
3 | RegisterServerEvent('hostedSession')
4 |
5 | -- event handler for pre-session 'acquire'
6 | local currentHosting
7 | local hostReleaseCallbacks = {}
8 |
9 | -- TODO: add a timeout for the hosting lock to be held
10 | -- TODO: add checks for 'fraudulent' conflict cases of hosting attempts (typically whenever the host can not be reached)
11 | AddEventHandler('hostingSession', function()
12 | -- if the lock is currently held, tell the client to await further instruction
13 | if currentHosting then
14 | TriggerClientEvent('sessionHostResult', source, 'wait')
15 |
16 | -- register a callback for when the lock is freed
17 | table.insert(hostReleaseCallbacks, function()
18 | TriggerClientEvent('sessionHostResult', source, 'free')
19 | end)
20 |
21 | return
22 | end
23 |
24 | -- if the current host was last contacted less than a second ago
25 | if GetHostId() then
26 | if GetPlayerLastMsg(GetHostId()) < 1000 then
27 | TriggerClientEvent('sessionHostResult', source, 'conflict')
28 |
29 | return
30 | end
31 | end
32 |
33 | hostReleaseCallbacks = {}
34 |
35 | currentHosting = source
36 |
37 | TriggerClientEvent('sessionHostResult', source, 'go')
38 |
39 | -- set a timeout of 5 seconds
40 | SetTimeout(5000, function()
41 | if not currentHosting then
42 | return
43 | end
44 |
45 | currentHosting = nil
46 |
47 | for _, cb in ipairs(hostReleaseCallbacks) do
48 | cb()
49 | end
50 | end)
51 | end)
52 |
53 | AddEventHandler('hostedSession', function()
54 | -- check if the client is the original locker
55 | if currentHosting ~= source then
56 | -- TODO: drop client as they're clearly lying
57 | print(currentHosting, '~=', source)
58 | return
59 | end
60 |
61 | -- free the host lock (call callbacks and remove the lock value)
62 | for _, cb in ipairs(hostReleaseCallbacks) do
63 | cb()
64 | end
65 |
66 | currentHosting = nil
67 | end)
68 |
69 | EnableEnhancedHostSupport(true)
--------------------------------------------------------------------------------
/resources/[gameplay]/playernames/playernames_api.lua:
--------------------------------------------------------------------------------
1 | local ids = {}
2 |
3 | local function getTriggerFunction(key)
4 | return function(id, ...)
5 | -- if on the client, it's easy
6 | if not IsDuplicityVersion() then
7 | TriggerEvent('playernames:configure', GetPlayerServerId(id), key, ...)
8 | else
9 | -- if on the server, save configuration
10 | if not ids[id] then
11 | ids[id] = {}
12 | end
13 |
14 | -- save the setting
15 | ids[id][key] = table.pack(...)
16 |
17 | -- broadcast to clients
18 | TriggerClientEvent('playernames:configure', -1, id, key, ...)
19 | end
20 | end
21 | end
22 |
23 | if IsDuplicityVersion() then
24 | function reconfigure(source)
25 | for id, data in pairs(ids) do
26 | for key, args in pairs(data) do
27 | TriggerClientEvent('playernames:configure', source, id, key, table.unpack(args))
28 | end
29 | end
30 | end
31 |
32 | AddEventHandler('playerDropped', function()
33 | ids[source] = nil
34 | end)
35 | end
36 |
37 | setComponentColor = getTriggerFunction('setc')
38 | setComponentAlpha = getTriggerFunction('seta')
39 | setComponentVisibility = getTriggerFunction('tglc')
40 | setWantedLevel = getTriggerFunction('setw')
41 | setHealthBarColor = getTriggerFunction('sehc')
42 | setNameTemplate = getTriggerFunction('tpl')
43 | setName = getTriggerFunction('name')
44 |
45 | if not io then
46 | io = { write = nil, open = nil }
47 | end
48 |
49 | local template = load(LoadResourceFile(GetCurrentResourceName(), 'template/template.lua'))()
50 |
51 | function formatPlayerNameTag(i, templateStr)
52 | --return ('%s <%d>'):format(GetPlayerName(i), GetPlayerServerId(i))
53 | local str = ''
54 |
55 | template.print = function(txt)
56 | str = str .. txt
57 | end
58 |
59 | local context = {
60 | name = GetPlayerName(i),
61 | i = i,
62 | global = _G
63 | }
64 |
65 | if IsDuplicityVersion() then
66 | context.id = i
67 | else
68 | context.id = GetPlayerServerId(i)
69 | end
70 |
71 | TriggerEvent('playernames:extendContext', i, function(k, v)
72 | context[k] = v
73 | end)
74 |
75 | template.render(templateStr, context, nil, true)
76 |
77 | template.print = print
78 |
79 | return str
80 | end
--------------------------------------------------------------------------------
/resources/[system]/baseevents/vehiclechecker.lua:
--------------------------------------------------------------------------------
1 | local isInVehicle = false
2 | local isEnteringVehicle = false
3 | local currentVehicle = 0
4 | local currentSeat = 0
5 |
6 | Citizen.CreateThread(function()
7 | while true do
8 | Citizen.Wait(0)
9 |
10 | local ped = PlayerPedId()
11 |
12 | if not isInVehicle and not IsPlayerDead(PlayerId()) then
13 | if DoesEntityExist(GetVehiclePedIsTryingToEnter(ped)) and not isEnteringVehicle then
14 | -- trying to enter a vehicle!
15 | local vehicle = GetVehiclePedIsTryingToEnter(ped)
16 | local seat = GetSeatPedIsTryingToEnter(ped)
17 | local netId = VehToNet(vehicle)
18 | isEnteringVehicle = true
19 | TriggerServerEvent('baseevents:enteringVehicle', vehicle, seat, GetDisplayNameFromVehicleModel(GetEntityModel(vehicle)), netId)
20 | elseif not DoesEntityExist(GetVehiclePedIsTryingToEnter(ped)) and not IsPedInAnyVehicle(ped, true) and isEnteringVehicle then
21 | -- vehicle entering aborted
22 | TriggerServerEvent('baseevents:enteringAborted')
23 | isEnteringVehicle = false
24 | elseif IsPedInAnyVehicle(ped, false) then
25 | -- suddenly appeared in a vehicle, possible teleport
26 | isEnteringVehicle = false
27 | isInVehicle = true
28 | currentVehicle = GetVehiclePedIsUsing(ped)
29 | currentSeat = GetPedVehicleSeat(ped)
30 | local model = GetEntityModel(currentVehicle)
31 | local name = GetDisplayNameFromVehicleModel()
32 | local netId = VehToNet(currentVehicle)
33 | TriggerServerEvent('baseevents:enteredVehicle', currentVehicle, currentSeat, GetDisplayNameFromVehicleModel(GetEntityModel(currentVehicle)), netId)
34 | end
35 | elseif isInVehicle then
36 | if not IsPedInAnyVehicle(ped, false) or IsPlayerDead(PlayerId()) then
37 | -- bye, vehicle
38 | local model = GetEntityModel(currentVehicle)
39 | local name = GetDisplayNameFromVehicleModel()
40 | local netId = VehToNet(currentVehicle)
41 | TriggerServerEvent('baseevents:leftVehicle', currentVehicle, currentSeat, GetDisplayNameFromVehicleModel(GetEntityModel(currentVehicle)), netId)
42 | isInVehicle = false
43 | currentVehicle = 0
44 | currentSeat = 0
45 | end
46 | end
47 | Citizen.Wait(50)
48 | end
49 | end)
50 |
51 | function GetPedVehicleSeat(ped)
52 | local vehicle = GetVehiclePedIsIn(ped, false)
53 | for i=-2,GetVehicleMaxNumberOfPassengers(vehicle) do
54 | if(GetPedInVehicleSeat(vehicle, i) == ped) then return i end
55 | end
56 | return -2
57 | end
58 |
--------------------------------------------------------------------------------
/resources/[test]/example-loadscreen/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
Free Mode
9 | Not Algonquin
10 |
11 |
12 |
13 |
Intel
14 |
15 |
16 |
The Statue of Happiness has no heart. You do.
17 |
18 |
22 |
23 |
24 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/resources/[managers]/mapmanager/mapmanager_shared.lua:
--------------------------------------------------------------------------------
1 | -- shared logic file for map manager - don't call any subsystem-specific functions here
2 | mapFiles = {}
3 |
4 | function addMap(file, owningResource)
5 | if not mapFiles[owningResource] then
6 | mapFiles[owningResource] = {}
7 | end
8 |
9 | table.insert(mapFiles[owningResource], file)
10 | end
11 |
12 | undoCallbacks = {}
13 |
14 | function loadMap(res)
15 | if mapFiles[res] then
16 | for _, file in ipairs(mapFiles[res]) do
17 | parseMap(file, res)
18 | end
19 | end
20 | end
21 |
22 | function unloadMap(res)
23 | if undoCallbacks[res] then
24 | for _, cb in ipairs(undoCallbacks[res]) do
25 | cb()
26 | end
27 |
28 | undoCallbacks[res] = nil
29 | mapFiles[res] = nil
30 | end
31 | end
32 |
33 | function parseMap(file, owningResource)
34 | if not undoCallbacks[owningResource] then
35 | undoCallbacks[owningResource] = {}
36 | end
37 |
38 | local env = {
39 | math = math, pairs = pairs, ipairs = ipairs, next = next, tonumber = tonumber, tostring = tostring,
40 | type = type, table = table, string = string, _G = env,
41 | vector3 = vector3, quat = quat, vec = vec, vector2 = vector2
42 | }
43 |
44 | TriggerEvent('getMapDirectives', function(key, cb, undocb)
45 | env[key] = function(...)
46 | local state = {}
47 |
48 | state.add = function(k, v)
49 | state[k] = v
50 | end
51 |
52 | local result = cb(state, ...)
53 | local args = table.pack(...)
54 |
55 | table.insert(undoCallbacks[owningResource], function()
56 | undocb(state)
57 | end)
58 |
59 | return result
60 | end
61 | end)
62 |
63 | local mt = {
64 | __index = function(t, k)
65 | if rawget(t, k) ~= nil then return rawget(t, k) end
66 |
67 | -- as we're not going to return nothing here (to allow unknown directives to be ignored)
68 | local f = function()
69 | return f
70 | end
71 |
72 | return function() return f end
73 | end
74 | }
75 |
76 | setmetatable(env, mt)
77 |
78 | local fileData = LoadResourceFile(owningResource, file)
79 | local mapFunction, err = load(fileData, file, 't', env)
80 |
81 | if not mapFunction then
82 | Citizen.Trace("Couldn't load map " .. file .. ": " .. err .. " (type of fileData: " .. type(fileData) .. ")\n")
83 | return
84 | end
85 |
86 | mapFunction()
87 | end
--------------------------------------------------------------------------------
/resources/[system]/[builders]/yarn/yarn_builder.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const fs = require('fs');
3 | const child_process = require('child_process');
4 | let buildingInProgress = false;
5 | let currentBuildingModule = '';
6 |
7 | const initCwd = process.cwd();
8 | const trimOutput = (data) => {
9 | return `[yarn]\t` + data.toString().replace(/\s+$/, '');
10 | }
11 |
12 | const yarnBuildTask = {
13 | shouldBuild(resourceName) {
14 | try {
15 | const resourcePath = GetResourcePath(resourceName);
16 |
17 | const packageJson = path.resolve(resourcePath, 'package.json');
18 | const yarnLock = path.resolve(resourcePath, '.yarn.installed');
19 |
20 | const packageStat = fs.statSync(packageJson);
21 |
22 | try {
23 | const yarnStat = fs.statSync(yarnLock);
24 |
25 | if (packageStat.mtimeMs > yarnStat.mtimeMs) {
26 | return true;
27 | }
28 | } catch (e) {
29 | // no yarn.installed, but package.json - install time!
30 | return true;
31 | }
32 | } catch (e) {
33 |
34 | }
35 |
36 | return false;
37 | },
38 |
39 | build(resourceName, cb) {
40 | (async () => {
41 | while (buildingInProgress && currentBuildingModule !== resourceName) {
42 | console.log(`yarn is currently busy: we are waiting to compile ${resourceName}`);
43 | await sleep(3000);
44 | }
45 | buildingInProgress = true;
46 | currentBuildingModule = resourceName;
47 | const proc = child_process.fork(
48 | require.resolve('./yarn_cli.js'),
49 | ['install', '--ignore-scripts', '--cache-folder', path.join(initCwd, 'cache', 'yarn-cache'), '--mutex', 'file:' + path.join(initCwd, 'cache', 'yarn-mutex')],
50 | {
51 | cwd: path.resolve(GetResourcePath(resourceName)),
52 | stdio: 'pipe',
53 | });
54 | proc.stdout.on('data', (data) => console.log(trimOutput(data)));
55 | proc.stderr.on('data', (data) => console.error(trimOutput(data)));
56 | proc.on('exit', (code, signal) => {
57 | setImmediate(() => {
58 | if (code != 0 || signal) {
59 | buildingInProgress = false;
60 | currentBuildingModule = '';
61 | cb(false, 'yarn failed!');
62 | return;
63 | }
64 |
65 | const resourcePath = GetResourcePath(resourceName);
66 | const yarnLock = path.resolve(resourcePath, '.yarn.installed');
67 | fs.writeFileSync(yarnLock, '');
68 |
69 | buildingInProgress = false;
70 | currentBuildingModule = '';
71 | cb(true);
72 | });
73 | });
74 | })();
75 | }
76 | };
77 |
78 | function sleep(ms) {
79 | return new Promise(resolve => setTimeout(resolve, ms));
80 | }
81 | RegisterResourceBuildTaskFactory('yarn', () => yarnBuildTask);
82 |
--------------------------------------------------------------------------------
/resources/[test]/example-loadscreen/keks.css:
--------------------------------------------------------------------------------
1 | body
2 | {
3 | margin: 0px;
4 | padding: 0px;
5 | }
6 |
7 | .backdrop
8 | {
9 | position: relative;
10 | top: 0px;
11 | left: 0px;
12 | width: 100%;
13 | height: 100%;
14 |
15 | background-image: url(loadscreen.jpg);
16 | background-size: 100% 100%;
17 | }
18 |
19 | .bottom
20 | {
21 | position: absolute;
22 | bottom: 0px;
23 | width: 100%;
24 | height: 100%;
25 | }
26 |
27 | #gradient
28 | {
29 | position: absolute;
30 | bottom: 0px;
31 | width: 100%;
32 |
33 | height: 25%;
34 |
35 | background: linear-gradient(to bottom, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 1) 100%);
36 | }
37 |
38 | @font-face {
39 | font-family: 'BankGothic';
40 | src: url('bankgothic.ttf') format('truetype');
41 | font-weight: normal;
42 | font-style: normal;
43 | }
44 |
45 | h1, h2 {
46 | position: relative;
47 | background: transparent;
48 | z-index: 0;
49 | }
50 | /* add a single stroke */
51 | h1:before, h2:before {
52 | content: attr(title);
53 | position: absolute;
54 | -webkit-text-stroke: 0.1em #000;
55 | left: 0;
56 | z-index: -1;
57 | }
58 |
59 |
60 | .letni
61 | {
62 | position: absolute;
63 | left: 5%;
64 | right: 5%;
65 | bottom: 10%;
66 |
67 | z-index: 5;
68 |
69 | color: #fff;
70 |
71 | font-family: "Segoe UI";
72 | }
73 |
74 | .letni p
75 | {
76 | font-size: 22px;
77 |
78 | margin-left: 3px;
79 |
80 | margin-top: 0px;
81 | }
82 |
83 | .letni h2, .letni h3
84 | {
85 | font-family: BankGothic;
86 |
87 | text-transform: uppercase;
88 |
89 | font-size: 50px;
90 |
91 | margin: 0px;
92 |
93 | display: inline-block;
94 | }
95 |
96 | .top
97 | {
98 | color: #fff;
99 |
100 | position: absolute;
101 | top: 7%;
102 | left: 5%;
103 | right: 5%;
104 | }
105 |
106 | .top h1
107 | {
108 | font-family: BankGothic;
109 | font-size: 60px;
110 |
111 | margin: 0px;
112 | }
113 |
114 | .top h2
115 | {
116 | font-family: BankGothic;
117 | font-size: 40px;
118 |
119 | margin: 0px;
120 |
121 | color: #ddd;
122 | }
123 |
124 | .loadbar
125 | {
126 | width: 100%;
127 | background-color: rgba(140, 140, 140, .9);
128 | height: 20px;
129 |
130 | margin-left: 2px;
131 | margin-right: 3px;
132 |
133 | margin-top: 5px;
134 | margin-bottom: 5px;
135 |
136 | overflow: hidden;
137 |
138 | position: relative;
139 |
140 | display: block;
141 | }
142 |
143 | .thingy
144 | {
145 | width: 10%;
146 | background-color: #eee;
147 | height: 20px;
148 |
149 | position: absolute;
150 | left: 10%;
151 | }
--------------------------------------------------------------------------------
/resources/[system]/rconlog/rconlog_server.lua:
--------------------------------------------------------------------------------
1 | RconLog({ msgType = 'serverStart', hostname = 'lovely', maxplayers = 32 })
2 |
3 | RegisterServerEvent('rlPlayerActivated')
4 |
5 | local names = {}
6 |
7 | AddEventHandler('rlPlayerActivated', function()
8 | RconLog({ msgType = 'playerActivated', netID = source, name = GetPlayerName(source), guid = GetPlayerIdentifiers(source)[1], ip = GetPlayerEP(source) })
9 |
10 | names[source] = { name = GetPlayerName(source), id = source }
11 |
12 | if GetHostId() then
13 | TriggerClientEvent('rlUpdateNames', GetHostId())
14 | end
15 | end)
16 |
17 | RegisterServerEvent('rlUpdateNamesResult')
18 |
19 | AddEventHandler('rlUpdateNamesResult', function(res)
20 | if source ~= tonumber(GetHostId()) then
21 | print('bad guy')
22 | return
23 | end
24 |
25 | for id, data in pairs(res) do
26 | if data then
27 | if data.name then
28 | if not names[id] then
29 | names[id] = data
30 | end
31 |
32 | if names[id].name ~= data.name or names[id].id ~= data.id then
33 | names[id] = data
34 |
35 | RconLog({ msgType = 'playerRenamed', netID = id, name = data.name })
36 | end
37 | end
38 | else
39 | names[id] = nil
40 | end
41 | end
42 | end)
43 |
44 | AddEventHandler('playerDropped', function()
45 | RconLog({ msgType = 'playerDropped', netID = source, name = GetPlayerName(source) })
46 |
47 | names[source] = nil
48 | end)
49 |
50 | AddEventHandler('chatMessage', function(netID, name, message)
51 | RconLog({ msgType = 'chatMessage', netID = netID, name = name, message = message, guid = GetPlayerIdentifiers(netID)[1] })
52 | end)
53 |
54 | -- NOTE: DO NOT USE THIS METHOD FOR HANDLING COMMANDS
55 | -- This resource has not been updated to use newer methods such as RegisterCommand.
56 | AddEventHandler('rconCommand', function(commandName, args)
57 | if commandName == 'status' then
58 | for netid, data in pairs(names) do
59 | local guid = GetPlayerIdentifiers(netid)
60 |
61 | if guid and guid[1] and data then
62 | local ping = GetPlayerPing(netid)
63 |
64 | RconPrint(netid .. ' ' .. guid[1] .. ' ' .. data.name .. ' ' .. GetPlayerEP(netid) .. ' ' .. ping .. "\n")
65 | end
66 | end
67 |
68 | CancelEvent()
69 | elseif commandName:lower() == 'clientkick' then
70 | local playerId = table.remove(args, 1)
71 | local msg = table.concat(args, ' ')
72 |
73 | DropPlayer(playerId, msg)
74 |
75 | CancelEvent()
76 | elseif commandName:lower() == 'tempbanclient' then
77 | local playerId = table.remove(args, 1)
78 | local msg = table.concat(args, ' ')
79 |
80 | TempBanPlayer(playerId, msg)
81 |
82 | CancelEvent()
83 | end
84 | end)
85 |
--------------------------------------------------------------------------------
/resources/[gameplay]/chat-theme-gtao/style.css:
--------------------------------------------------------------------------------
1 | * {
2 | font-family: inherit;
3 | }
4 |
5 | .chat-window {
6 | --size: calc(((2.7vh * 1.2)) * 6);
7 |
8 | position: absolute;
9 | right: calc(2.77vh);
10 | top: calc(50% - (var(--size) / 2));
11 | height: var(--size) !important;
12 |
13 | background: inherit !important;
14 |
15 | text-align: right;
16 |
17 | left: auto;
18 |
19 | user-select: none;
20 | }
21 |
22 | @font-face {
23 | font-family: 'Font2';
24 | src: url(https://runtime.fivem.net/temp/ChaletLondonNineteenSixty.otf?a);
25 | }
26 |
27 | @font-face {
28 | font-family: 'Font2_cond';
29 | src: url(https://runtime.fivem.net/temp/chaletcomprime-colognesixty-webfont.ttf?a);
30 | }
31 |
32 | .msg {
33 | font-family: Font2, sans-serif;
34 | color: #fff;
35 |
36 | font-size: calc(1.8vh); /* 13px in 720p, calc'd by width */
37 | filter: url(#svgDropShadowFilter);
38 |
39 | line-height: calc(2.7vh * 1.2);
40 |
41 | margin-bottom: 0;
42 | }
43 |
44 | .chat-messages {
45 | margin: 0;
46 | height: 100%;
47 | }
48 |
49 | .msg > span > span > b {
50 | font-family: Font2_cond, sans-serif;
51 | font-weight: normal;
52 |
53 | vertical-align: baseline;
54 | padding-right: 11px;
55 |
56 | line-height: 1;
57 |
58 | font-size: calc(2.7vh);
59 | }
60 |
61 | .msg > span > span > span {
62 | vertical-align: baseline;
63 | }
64 |
65 | .msg i:first-of-type {
66 | font-style: normal;
67 | color: #c0c0c0;
68 | }
69 |
70 | .chat-input {
71 | position: absolute;
72 | right: calc(2.77vh);
73 | bottom: calc(2.77vh);
74 |
75 | background: inherit !important;
76 |
77 | text-align: right;
78 |
79 | top: auto;
80 | left: auto;
81 |
82 | height: auto;
83 |
84 | font-family: Font2, sans-serif;
85 | }
86 |
87 | .chat-input > div {
88 | background-color: rgba(0, 0, 0, .6) !important;
89 | border: calc(0.28vh / 2) solid rgba(180, 180, 180, .6);
90 | outline: calc(0.28vh / 2) solid rgba(0, 0, 0, .8); /* to replace margin-background */
91 | padding: calc(0.28vh / 2);
92 | }
93 |
94 | .chat-input .prefix {
95 | margin: 0;
96 | margin-left: 0.7%;
97 | margin-top: -0.1%;
98 | line-height: 2.8vh;
99 | }
100 |
101 | .chat-input .prefix.any {
102 | opacity: 0.8;
103 | }
104 |
105 | .chat-input .prefix.any:before {
106 | content: '[';
107 | }
108 |
109 | .chat-input .prefix.any:after {
110 | content: ']';
111 | }
112 |
113 | .chat-input > div + div {
114 | position: absolute;
115 | bottom: calc(1.65vh + 0.28vh + 0.28vh + 0.28vh + (0.28vh / 2));
116 | width: 99.6%;
117 |
118 | text-align: left;
119 | }
120 |
121 | .suggestions {
122 | border: calc(0.28vh / 2) solid rgba(180, 180, 180, .6);
123 | background: transparent;
124 | }
125 |
126 | textarea {
127 | background: transparent;
128 | padding: 0.5vh;
129 | }
130 |
131 | @media screen and (min-aspect-ratio: 21/9) {
132 | .chat-window, .chat-input {
133 | right: calc(12.8vw);
134 | }
135 | }
136 |
137 | @media screen and (min-aspect-ratio: 32/9) {
138 | .chat-window, .chat-input {
139 | right: calc(25vw);
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/resources/[system]/baseevents/deathevents.lua:
--------------------------------------------------------------------------------
1 | Citizen.CreateThread(function()
2 | local isDead = false
3 | local hasBeenDead = false
4 | local diedAt
5 |
6 | while true do
7 | Wait(0)
8 |
9 | local player = PlayerId()
10 |
11 | if NetworkIsPlayerActive(player) then
12 | local ped = PlayerPedId()
13 |
14 | if IsPedFatallyInjured(ped) and not isDead then
15 | isDead = true
16 | if not diedAt then
17 | diedAt = GetGameTimer()
18 | end
19 |
20 | local killer, killerweapon = NetworkGetEntityKillerOfPlayer(player)
21 | local killerentitytype = GetEntityType(killer)
22 | local killertype = -1
23 | local killerinvehicle = false
24 | local killervehiclename = ''
25 | local killervehicleseat = 0
26 | if killerentitytype == 1 then
27 | killertype = GetPedType(killer)
28 | if IsPedInAnyVehicle(killer, false) == 1 then
29 | killerinvehicle = true
30 | killervehiclename = GetDisplayNameFromVehicleModel(GetEntityModel(GetVehiclePedIsUsing(killer)))
31 | killervehicleseat = GetPedVehicleSeat(killer)
32 | else killerinvehicle = false
33 | end
34 | end
35 |
36 | local killerid = GetPlayerByEntityID(killer)
37 | if killer ~= ped and killerid ~= nil and NetworkIsPlayerActive(killerid) then killerid = GetPlayerServerId(killerid)
38 | else killerid = -1
39 | end
40 |
41 | if killer == ped or killer == -1 then
42 | TriggerEvent('baseevents:onPlayerDied', killertype, { table.unpack(GetEntityCoords(ped)) })
43 | TriggerServerEvent('baseevents:onPlayerDied', killertype, { table.unpack(GetEntityCoords(ped)) })
44 | hasBeenDead = true
45 | else
46 | TriggerEvent('baseevents:onPlayerKilled', killerid, {killertype=killertype, weaponhash = killerweapon, killerinveh=killerinvehicle, killervehseat=killervehicleseat, killervehname=killervehiclename, killerpos={table.unpack(GetEntityCoords(ped))}})
47 | TriggerServerEvent('baseevents:onPlayerKilled', killerid, {killertype=killertype, weaponhash = killerweapon, killerinveh=killerinvehicle, killervehseat=killervehicleseat, killervehname=killervehiclename, killerpos={table.unpack(GetEntityCoords(ped))}})
48 | hasBeenDead = true
49 | end
50 | elseif not IsPedFatallyInjured(ped) then
51 | isDead = false
52 | diedAt = nil
53 | end
54 |
55 | -- check if the player has to respawn in order to trigger an event
56 | if not hasBeenDead and diedAt ~= nil and diedAt > 0 then
57 | TriggerEvent('baseevents:onPlayerWasted', { table.unpack(GetEntityCoords(ped)) })
58 | TriggerServerEvent('baseevents:onPlayerWasted', { table.unpack(GetEntityCoords(ped)) })
59 |
60 | hasBeenDead = true
61 | elseif hasBeenDead and diedAt ~= nil and diedAt <= 0 then
62 | hasBeenDead = false
63 | end
64 | end
65 | end
66 | end)
67 |
68 | function GetPlayerByEntityID(id)
69 | for i=0,32 do
70 | if(NetworkIsPlayerActive(i) and GetPlayerPed(i) == id) then return i end
71 | end
72 | return nil
73 | end
74 |
--------------------------------------------------------------------------------
/resources/[gameplay]/chat/html/index.css:
--------------------------------------------------------------------------------
1 | .color-0{color: #ffffff;}
2 | .color-1{color: #ff4444;}
3 | .color-2{color: #99cc00;}
4 | .color-3{color: #ffbb33;}
5 | .color-4{color: #0099cc;}
6 | .color-5{color: #33b5e5;}
7 | .color-6{color: #aa66cc;}
8 | .color-8{color: #cc0000;}
9 | .color-9{color: #cc0068;}
10 |
11 | .gameColor-w{color: #ffffff;}
12 | .gameColor-r{color: #ff4444;}
13 | .gameColor-g{color: #99cc00;}
14 | .gameColor-y{color: #ffbb33;}
15 | .gameColor-b{color: #33b5e5;}
16 |
17 | /* todo: more game colors */
18 |
19 | * {
20 | font-family: 'Lato', sans-serif;
21 | margin: 0;
22 | padding: 0;
23 | }
24 |
25 | .no-grow {
26 | flex-grow: 0;
27 | }
28 |
29 | em {
30 | font-style: normal;
31 | }
32 |
33 | #app {
34 | font-family: 'Lato', Helvetica, Arial, sans-serif;
35 | -webkit-font-smoothing: antialiased;
36 | -moz-osx-font-smoothing: grayscale;
37 | color: white;
38 | }
39 |
40 | .chat-window {
41 | position: absolute;
42 | top: 1.5%;
43 | left: 0.8%;
44 | width: 38%;
45 | height: 22%;
46 | max-width: 1000px;
47 | background-color: rgba(52, 73, 94, 0.7);
48 | -webkit-animation-duration: 2s;
49 | }
50 |
51 |
52 | .chat-messages {
53 | position: relative;
54 | height: 95%;
55 | font-size: 1.8vh;
56 | margin: 1%;
57 |
58 | overflow-x: hidden;
59 | overflow-y: hidden;
60 | }
61 |
62 |
63 | .chat-input {
64 | font-size: 1.65vh;
65 | position: absolute;
66 |
67 | top: 23.8%;
68 | left: 0.8%;
69 | width: 38%;
70 | max-width: 1000px;
71 | box-sizing: border-box;
72 | }
73 |
74 | .chat-input > div.input {
75 | position: relative;
76 | display: flex;
77 | align-items: stretch;
78 | width: 100%;
79 | background-color: rgba(44, 62, 80, 1.0);
80 | }
81 |
82 | .chat-hide-state {
83 | text-transform: uppercase;
84 | margin-left: 0.05vw;
85 | font-size: 1.65vh;
86 | }
87 |
88 | .prefix {
89 | font-size: 1.8vh;
90 | /*position: absolute;
91 | top: 0%;*/
92 | height: 100%;
93 | vertical-align: middle;
94 | line-height: calc(1vh + 1vh + 1.85vh);
95 | padding-left: 0.5vh;
96 | text-transform: uppercase;
97 | font-weight: bold;
98 | display: inline-block;
99 | }
100 |
101 | textarea {
102 | font-size: 1.65vh;
103 | line-height: 1.85vh;
104 | display: block;
105 | box-sizing: content-box;
106 | padding: 1vh;
107 | padding-left: 0.5vh;
108 | color: white;
109 | border-width: 0;
110 | height: 3.15%;
111 | overflow: hidden;
112 | text-overflow: ellipsis;
113 | flex: 1;
114 | background-color: transparent;
115 | }
116 |
117 | textarea:focus, input:focus {
118 | outline: none;
119 | }
120 |
121 | .msg {
122 | margin-bottom: 0.28%;
123 | }
124 |
125 | .multiline {
126 | margin-left: 4%;
127 | text-indent: -1.2rem;
128 | white-space: pre-line;
129 | }
130 |
131 | .suggestions {
132 | list-style-type: none;
133 | padding: 0.5%;
134 | padding-left: 1.4%;
135 | font-size: 1.65vh;
136 | box-sizing: border-box;
137 | color: white;
138 | background-color: rgba(44, 62, 80, 1.0);
139 | width: 100%;
140 | }
141 |
142 | .help {
143 | color: #b0bbbd;
144 | }
145 |
146 | .disabled {
147 | color: #b0bbbd;
148 | }
149 |
150 | .suggestion {
151 | margin-bottom: 0.5%;
152 | }
153 |
154 | .hidden {
155 | opacity: 0;
156 | }
157 |
158 | .hidden.animated {
159 | transition: opacity 1s;
160 | }
--------------------------------------------------------------------------------
/resources/[managers]/mapmanager/mapmanager_client.lua:
--------------------------------------------------------------------------------
1 | local maps = {}
2 | local gametypes = {}
3 |
4 | AddEventHandler('onClientResourceStart', function(res)
5 | -- parse metadata for this resource
6 |
7 | -- map files
8 | local num = GetNumResourceMetadata(res, 'map')
9 |
10 | if num > 0 then
11 | for i = 0, num-1 do
12 | local file = GetResourceMetadata(res, 'map', i)
13 |
14 | if file then
15 | addMap(file, res)
16 | end
17 | end
18 | end
19 |
20 | -- resource type data
21 | local type = GetResourceMetadata(res, 'resource_type', 0)
22 |
23 | if type then
24 | local extraData = GetResourceMetadata(res, 'resource_type_extra', 0)
25 |
26 | if extraData then
27 | extraData = json.decode(extraData)
28 | else
29 | extraData = {}
30 | end
31 |
32 | if type == 'map' then
33 | maps[res] = extraData
34 | elseif type == 'gametype' then
35 | gametypes[res] = extraData
36 | end
37 | end
38 |
39 | -- handle starting
40 | loadMap(res)
41 |
42 | -- defer this to the next game tick to work around a lack of dependencies
43 | Citizen.CreateThread(function()
44 | Citizen.Wait(15)
45 |
46 | if maps[res] then
47 | TriggerEvent('onClientMapStart', res)
48 | elseif gametypes[res] then
49 | TriggerEvent('onClientGameTypeStart', res)
50 | end
51 | end)
52 | end)
53 |
54 | AddEventHandler('onResourceStop', function(res)
55 | if maps[res] then
56 | TriggerEvent('onClientMapStop', res)
57 | elseif gametypes[res] then
58 | TriggerEvent('onClientGameTypeStop', res)
59 | end
60 |
61 | unloadMap(res)
62 | end)
63 |
64 | AddEventHandler('getMapDirectives', function(add)
65 | if not CreateScriptVehicleGenerator then
66 | return
67 | end
68 |
69 | add('vehicle_generator', function(state, name)
70 | return function(opts)
71 | local x, y, z, heading
72 | local color1, color2
73 |
74 | if opts.x then
75 | x = opts.x
76 | y = opts.y
77 | z = opts.z
78 | else
79 | x = opts[1]
80 | y = opts[2]
81 | z = opts[3]
82 | end
83 |
84 | heading = opts.heading or 1.0
85 | color1 = opts.color1 or -1
86 | color2 = opts.color2 or -1
87 |
88 | CreateThread(function()
89 | local hash = GetHashKey(name)
90 | RequestModel(hash)
91 |
92 | while not HasModelLoaded(hash) do
93 | Wait(0)
94 | end
95 |
96 | local carGen = CreateScriptVehicleGenerator(x, y, z, heading, 5.0, 3.0, hash, color1, color2, -1, -1, true, false, false, true, true, -1)
97 | SetScriptVehicleGenerator(carGen, true)
98 | SetAllVehicleGeneratorsActive(true)
99 |
100 | state.add('cargen', carGen)
101 | end)
102 | end
103 | end, function(state, arg)
104 | Citizen.Trace("deleting car gen " .. tostring(state.cargen) .. "\n")
105 |
106 | DeleteScriptVehicleGenerator(state.cargen)
107 | end)
108 | end)
109 |
--------------------------------------------------------------------------------
/resources/[gameplay]/chat-theme-gtao/shadow.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | var Filters = {}
3 |
4 | var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
5 | svg.setAttribute("style", "display:block;width:0px;height:0px");
6 | var defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
7 |
8 | var blurFilter = document.createElementNS("http://www.w3.org/2000/svg", "filter");
9 | blurFilter.setAttribute("id", "svgBlurFilter");
10 | var feGaussianFilter = document.createElementNS("http://www.w3.org/2000/svg", "feGaussianBlur");
11 | feGaussianFilter.setAttribute("stdDeviation", "0 0");
12 | blurFilter.appendChild(feGaussianFilter);
13 | defs.appendChild(blurFilter);
14 | Filters._svgBlurFilter = feGaussianFilter;
15 |
16 | // Drop Shadow Filter
17 | var dropShadowFilter = document.createElementNS("http://www.w3.org/2000/svg", "filter");
18 | dropShadowFilter.setAttribute("id", "svgDropShadowFilter");
19 | var feGaussianFilter = document.createElementNS("http://www.w3.org/2000/svg", "feGaussianBlur");
20 | feGaussianFilter.setAttribute("in", "SourceAlpha");
21 | feGaussianFilter.setAttribute("stdDeviation", "3");
22 | dropShadowFilter.appendChild(feGaussianFilter);
23 | Filters._svgDropshadowFilterBlur = feGaussianFilter;
24 |
25 | var feOffset = document.createElementNS("http://www.w3.org/2000/svg", "feOffset");
26 | feOffset.setAttribute("dx", "0");
27 | feOffset.setAttribute("dy", "0");
28 | feOffset.setAttribute("result", "offsetblur");
29 | dropShadowFilter.appendChild(feOffset);
30 | Filters._svgDropshadowFilterOffset = feOffset;
31 |
32 | var feFlood = document.createElementNS("http://www.w3.org/2000/svg", "feFlood");
33 | feFlood.setAttribute("flood-color", "rgba(0,0,0,1)");
34 | dropShadowFilter.appendChild(feFlood);
35 | Filters._svgDropshadowFilterFlood = feFlood;
36 |
37 | var feComposite = document.createElementNS("http://www.w3.org/2000/svg", "feComposite");
38 | feComposite.setAttribute("in2", "offsetblur");
39 | feComposite.setAttribute("operator", "in");
40 | dropShadowFilter.appendChild(feComposite);
41 | var feComposite = document.createElementNS("http://www.w3.org/2000/svg", "feComposite");
42 | feComposite.setAttribute("in2", "SourceAlpha");
43 | feComposite.setAttribute("operator", "out");
44 | feComposite.setAttribute("result", "outer");
45 | dropShadowFilter.appendChild(feComposite);
46 |
47 | var feMerge = document.createElementNS("http://www.w3.org/2000/svg", "feMerge");
48 | var feMergeNode = document.createElementNS("http://www.w3.org/2000/svg", "feMergeNode");
49 | feMerge.appendChild(feMergeNode);
50 | var feMergeNode = document.createElementNS("http://www.w3.org/2000/svg", "feMergeNode");
51 | feMerge.appendChild(feMergeNode);
52 | Filters._svgDropshadowMergeNode = feMergeNode;
53 | dropShadowFilter.appendChild(feMerge);
54 | defs.appendChild(dropShadowFilter);
55 | svg.appendChild(defs);
56 | document.documentElement.appendChild(svg);
57 |
58 | const blurScale = 1;
59 | const scale = (document.body.clientWidth / 1280);
60 |
61 | Filters._svgDropshadowFilterBlur.setAttribute("stdDeviation",
62 | 1 * blurScale + " " +
63 | 1 * blurScale
64 | );
65 | Filters._svgDropshadowFilterOffset.setAttribute("dx",
66 | String(Math.cos(45 * Math.PI / 180) * 1 * scale));
67 | Filters._svgDropshadowFilterOffset.setAttribute("dy",
68 | String(Math.sin(45 * Math.PI / 180) * 1 * scale));
69 | Filters._svgDropshadowFilterFlood.setAttribute("flood-color",
70 | 'rgba(0, 0, 0, 1)');
71 | Filters._svgDropshadowMergeNode.setAttribute("in",
72 | "SourceGraphic");
73 |
74 | })();
--------------------------------------------------------------------------------
/resources/[gameplay]/[examples]/money/server.lua:
--------------------------------------------------------------------------------
1 | local playerData = exports['cfx.re/playerData.v1alpha1']
2 |
3 | local validMoneyTypes = {
4 | bank = true,
5 | cash = true,
6 | }
7 |
8 | local function getMoneyForId(playerId, moneyType)
9 | return GetResourceKvpInt(('money:%s:%s'):format(playerId, moneyType)) / 100.0
10 | end
11 |
12 | local function setMoneyForId(playerId, moneyType, money)
13 | local s = playerData:getPlayerById(playerId)
14 |
15 | TriggerEvent('money:updated', {
16 | dbId = playerId,
17 | source = s,
18 | moneyType = moneyType,
19 | money = money
20 | })
21 |
22 | return SetResourceKvpInt(('money:%s:%s'):format(playerId, moneyType), math.tointeger(money * 100.0))
23 | end
24 |
25 | local function addMoneyForId(playerId, moneyType, amount)
26 | local curMoney = getMoneyForId(playerId, moneyType)
27 | curMoney += amount
28 |
29 | if curMoney >= 0 then
30 | setMoneyForId(playerId, moneyType, curMoney)
31 | return true, curMoney
32 | end
33 |
34 | return false, 0
35 | end
36 |
37 | exports('addMoney', function(playerIdx, moneyType, amount)
38 | amount = tonumber(amount)
39 |
40 | if amount <= 0 or amount > (1 << 30) then
41 | return false
42 | end
43 |
44 | if not validMoneyTypes[moneyType] then
45 | return false
46 | end
47 |
48 | local playerId = playerData:getPlayerId(playerIdx)
49 | local success, money = addMoneyForId(playerId, moneyType, amount)
50 |
51 | if success then
52 | Player(playerIdx).state['money_' .. moneyType] = money
53 | end
54 |
55 | return true
56 | end)
57 |
58 | exports('removeMoney', function(playerIdx, moneyType, amount)
59 | amount = tonumber(amount)
60 |
61 | if amount <= 0 or amount > (1 << 30) then
62 | return false
63 | end
64 |
65 | if not validMoneyTypes[moneyType] then
66 | return false
67 | end
68 |
69 | local playerId = playerData:getPlayerId(playerIdx)
70 | local success, money = addMoneyForId(playerId, moneyType, -amount)
71 |
72 | if success then
73 | Player(playerIdx).state['money_' .. moneyType] = money
74 | end
75 |
76 | return success
77 | end)
78 |
79 | exports('getMoney', function(playerIdx, moneyType)
80 | local playerId = playerData:getPlayerId(playerIdx)
81 | return getMoneyForId(playerId, moneyType)
82 | end)
83 |
84 | -- player display bits
85 | AddEventHandler('money:updated', function(data)
86 | if data.source then
87 | TriggerClientEvent('money:displayUpdate', data.source, data.moneyType, data.money)
88 | end
89 | end)
90 |
91 | RegisterNetEvent('money:requestDisplay')
92 |
93 | AddEventHandler('money:requestDisplay', function()
94 | local source = source
95 | local playerId = playerData:getPlayerId(source)
96 |
97 | for type, _ in pairs(validMoneyTypes) do
98 | local amount = getMoneyForId(playerId, type)
99 | TriggerClientEvent('money:displayUpdate', source, type, amount)
100 |
101 | Player(source).state['money_' .. type] = amount
102 | end
103 | end)
104 |
105 | RegisterCommand('earn', function(source, args)
106 | local type = args[1]
107 | local amount = tonumber(args[2])
108 |
109 | exports['money']:addMoney(source, type, amount)
110 | end, true)
111 |
112 | RegisterCommand('spend', function(source, args)
113 | local type = args[1]
114 | local amount = tonumber(args[2])
115 |
116 | if not exports['money']:removeMoney(source, type, amount) then
117 | print('you are broke??')
118 | end
119 | end, true)
--------------------------------------------------------------------------------
/resources/[gameplay]/chat/html/Message.ts:
--------------------------------------------------------------------------------
1 | import CONFIG from './config';
2 | import Vue, { PropType } from 'vue';
3 |
4 | export default Vue.component('message', {
5 | data() {
6 | return {};
7 | },
8 | computed: {
9 | textEscaped(): string {
10 | let s = this.template ? this.template : this.templates[this.templateId];
11 |
12 | //This hack is required to preserve backwards compatability
13 | if (!this.template && this.templateId == CONFIG.defaultTemplateId
14 | && this.args.length == 1) {
15 | s = this.templates[CONFIG.defaultAltTemplateId] //Swap out default template :/
16 | }
17 |
18 | s = s.replace(`@default`, this.templates[this.templateId]);
19 |
20 | s = s.replace(/{(\d+)}/g, (match, number) => {
21 | const argEscaped = this.args[number] != undefined ? this.escape(this.args[number]) : match;
22 | if (number == 0 && this.color) {
23 | //color is deprecated, use templates or ^1 etc.
24 | return this.colorizeOld(argEscaped);
25 | }
26 | return argEscaped;
27 | });
28 |
29 | // format variant args
30 | s = s.replace(/\{\{([a-zA-Z0-9_\-]+?)\}\}/g, (match, id) => {
31 | const argEscaped = this.params[id] != undefined ? this.escape(this.params[id]) : match;
32 | return argEscaped;
33 | });
34 |
35 | return this.colorize(s);
36 | },
37 | },
38 | methods: {
39 | colorizeOld(str: string): string {
40 | return `${str}`
41 | },
42 | colorize(str: string): string {
43 | let s = "" + colorTrans(str) + "";
44 |
45 | const styleDict: {[ key: string ]: string} = {
46 | '*': 'font-weight: bold;',
47 | '_': 'text-decoration: underline;',
48 | '~': 'text-decoration: line-through;',
49 | '=': 'text-decoration: underline line-through;',
50 | 'r': 'text-decoration: none;font-weight: normal;',
51 | };
52 |
53 | const styleRegex = /\^(\_|\*|\=|\~|\/|r)(.*?)(?=$|\^r|<\/em>)/;
54 | while (s.match(styleRegex)) { //Any better solution would be appreciated :P
55 | s = s.replace(styleRegex, (str, style, inner) => `${inner}`)
56 | }
57 | return s.replace(/]*><\/span[^>]*>/g, '');
58 |
59 | function colorTrans(str: string) {
60 | return str
61 | .replace(/\^([0-9])/g, (str, color) => ``)
62 | .replace(/\^#([0-9A-F]{3,6})/gi, (str, color) => ``)
63 | .replace(/~([a-z])~/g, (str, color) => ``);
64 | }
65 | },
66 | escape(unsafe: string): string {
67 | return String(unsafe)
68 | .replace(/&/g, '&')
69 | .replace(//g, '>')
71 | .replace(/"/g, '"')
72 | .replace(/'/g, ''');
73 | },
74 | },
75 | props: {
76 | templates: {
77 | type: Object as PropType<{ [key: string]: string }>,
78 | },
79 | args: {
80 | type: Array as PropType,
81 | },
82 | params: {
83 | type: Object as PropType<{ [ key: string]: string }>,
84 | },
85 | template: {
86 | type: String,
87 | default: null,
88 | },
89 | templateId: {
90 | type: String,
91 | default: CONFIG.defaultTemplateId,
92 | },
93 | multiline: {
94 | type: Boolean,
95 | default: false,
96 | },
97 | color: { //deprecated
98 | type: Array as PropType,
99 | default: null,
100 | },
101 | },
102 | });
103 |
--------------------------------------------------------------------------------
/resources/[gameplay]/[examples]/money-fountain/server.lua:
--------------------------------------------------------------------------------
1 | -- track down what we've added to global state
2 | local sentState = {}
3 |
4 | -- money system
5 | local ms = exports['money']
6 |
7 | -- get the fountain content from storage
8 | local function getMoneyForId(fountainId)
9 | return GetResourceKvpInt(('money:%s'):format(fountainId)) / 100.0
10 | end
11 |
12 | -- set the fountain content in storage + state
13 | local function setMoneyForId(fountainId, money)
14 | GlobalState['fountain_' .. fountainId] = math.tointeger(money)
15 |
16 | return SetResourceKvpInt(('money:%s'):format(fountainId), math.tointeger(money * 100.0))
17 | end
18 |
19 | -- get the nearest fountain to the player + ID
20 | local function getMoneyFountain(id, source)
21 | local coords = GetEntityCoords(GetPlayerPed(source))
22 |
23 | for _, v in pairs(moneyFountains) do
24 | if v.id == id then
25 | if #(v.coords - coords) < 2.5 then
26 | return v
27 | end
28 | end
29 | end
30 |
31 | return nil
32 | end
33 |
34 | -- generic function for events
35 | local function handleFountainStuff(source, id, pickup)
36 | -- if near the fountain we specify
37 | local fountain = getMoneyFountain(id, source)
38 |
39 | if fountain then
40 | -- and we can actually use the fountain already
41 | local player = Player(source)
42 |
43 | local nextUse = player.state['fountain_nextUse']
44 | if not nextUse then
45 | nextUse = 0
46 | end
47 |
48 | -- GetGameTimer ~ GetNetworkTime on client
49 | if nextUse <= GetGameTimer() then
50 | -- not rate limited
51 | local success = false
52 | local money = getMoneyForId(fountain.id)
53 |
54 | -- decide the op
55 | if pickup then
56 | -- if the fountain is rich enough to get the per-use amount
57 | if money >= fountain.amount then
58 | -- give the player money
59 | if ms:addMoney(source, 'cash', fountain.amount) then
60 | money -= fountain.amount
61 | success = true
62 | end
63 | end
64 | else
65 | -- if the player is rich enough
66 | if ms:removeMoney(source, 'cash', fountain.amount) then
67 | -- add to the fountain
68 | money += fountain.amount
69 | success = true
70 | end
71 | end
72 |
73 | -- save it and set the player's cooldown
74 | if success then
75 | setMoneyForId(fountain.id, money)
76 | player.state['fountain_nextUse'] = GetGameTimer() + GetConvarInt('moneyFountain_cooldown', 5000)
77 | end
78 | end
79 | end
80 | end
81 |
82 | -- event for picking up fountain->player
83 | RegisterNetEvent('money_fountain:tryPickup')
84 | AddEventHandler('money_fountain:tryPickup', function(id)
85 | handleFountainStuff(source, id, true)
86 | end)
87 |
88 | -- event for donating player->fountain
89 | RegisterNetEvent('money_fountain:tryPlace')
90 | AddEventHandler('money_fountain:tryPlace', function(id)
91 | handleFountainStuff(source, id, false)
92 | end)
93 |
94 | -- listener: if a new fountain is added, set its current money in state
95 | CreateThread(function()
96 | while true do
97 | Wait(500)
98 |
99 | for _, fountain in pairs(moneyFountains) do
100 | if not sentState[fountain.id] then
101 | GlobalState['fountain_' .. fountain.id] = math.tointeger(getMoneyForId(fountain.id))
102 |
103 | sentState[fountain.id] = true
104 | end
105 | end
106 | end
107 | end)
--------------------------------------------------------------------------------
/resources/[system]/sessionmanager-rdr3/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | "@citizenfx/protobufjs@6.8.8":
6 | version "6.8.8"
7 | resolved "https://registry.yarnpkg.com/@citizenfx/protobufjs/-/protobufjs-6.8.8.tgz#d12edcada06182f3785dea1d35eebf0ebe4fd2c3"
8 | integrity sha512-RBJvHPWNwguEPxV+ALbCZBXAEQf2byP5KtYrYl36Mbb0+Ch7MxpOm+764S+YqYWj/A/iNSW3jXglfJ9hlxjBLA==
9 | dependencies:
10 | "@protobufjs/aspromise" "^1.1.2"
11 | "@protobufjs/base64" "^1.1.2"
12 | "@protobufjs/codegen" "^2.0.4"
13 | "@protobufjs/eventemitter" "^1.1.0"
14 | "@protobufjs/fetch" "^1.1.0"
15 | "@protobufjs/float" "^1.0.2"
16 | "@protobufjs/inquire" "^1.1.0"
17 | "@protobufjs/path" "^1.1.2"
18 | "@protobufjs/pool" "^1.1.0"
19 | "@protobufjs/utf8" "^1.1.0"
20 | "@types/long" "^4.0.0"
21 | "@types/node" "^10.1.0"
22 | long "^4.0.0"
23 |
24 | "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2":
25 | version "1.1.2"
26 | resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf"
27 | integrity sha1-m4sMxmPWaafY9vXQiToU00jzD78=
28 |
29 | "@protobufjs/base64@^1.1.2":
30 | version "1.1.2"
31 | resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735"
32 | integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==
33 |
34 | "@protobufjs/codegen@^2.0.4":
35 | version "2.0.4"
36 | resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb"
37 | integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==
38 |
39 | "@protobufjs/eventemitter@^1.1.0":
40 | version "1.1.0"
41 | resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70"
42 | integrity sha1-NVy8mLr61ZePntCV85diHx0Ga3A=
43 |
44 | "@protobufjs/fetch@^1.1.0":
45 | version "1.1.0"
46 | resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45"
47 | integrity sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=
48 | dependencies:
49 | "@protobufjs/aspromise" "^1.1.1"
50 | "@protobufjs/inquire" "^1.1.0"
51 |
52 | "@protobufjs/float@^1.0.2":
53 | version "1.0.2"
54 | resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1"
55 | integrity sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=
56 |
57 | "@protobufjs/inquire@^1.1.0":
58 | version "1.1.0"
59 | resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089"
60 | integrity sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=
61 |
62 | "@protobufjs/path@^1.1.2":
63 | version "1.1.2"
64 | resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d"
65 | integrity sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=
66 |
67 | "@protobufjs/pool@^1.1.0":
68 | version "1.1.0"
69 | resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54"
70 | integrity sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=
71 |
72 | "@protobufjs/utf8@^1.1.0":
73 | version "1.1.0"
74 | resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570"
75 | integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=
76 |
77 | "@types/long@^4.0.0":
78 | version "4.0.1"
79 | resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9"
80 | integrity sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==
81 |
82 | "@types/node@^10.1.0":
83 | version "10.17.58"
84 | resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.58.tgz#10682f6016fd866725c36d22ce6bbbd029bf4545"
85 | integrity sha512-Dn5RBxLohjdHFj17dVVw3rtrZAeXeWg+LQfvxDIW/fdPkSiuQk7h3frKMYtsQhtIW42wkErDcy9UMVxhGW4O7w==
86 |
87 | long@^4.0.0:
88 | version "4.0.0"
89 | resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28"
90 | integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==
91 |
--------------------------------------------------------------------------------
/resources/[system]/sessionmanager-rdr3/rline.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package rline;
3 |
4 | message RpcErrorData {
5 | string ErrorCodeString = 1;
6 | int32 ErrorCode = 2;
7 | string DomainString = 3;
8 | int32 DomainCode = 4;
9 | bytes DataEx = 5;
10 | };
11 |
12 | message RpcError {
13 | int32 ErrorCode = 1;
14 | string ErrorMessage = 2;
15 | RpcErrorData Data = 3;
16 | };
17 |
18 | message RpcHeader {
19 | string RequestId = 1;
20 | string MethodName = 2;
21 | RpcError Error = 3;
22 | string srcTid = 4;
23 | };
24 |
25 | message RpcMessage {
26 | RpcHeader Header = 1;
27 | bytes Content = 2;
28 | };
29 |
30 | message RpcResponseContainer {
31 | bytes Content = 1;
32 | };
33 |
34 | message RpcResponseMessage {
35 | RpcHeader Header = 1;
36 | RpcResponseContainer Container = 2;
37 | };
38 |
39 | message TokenStuff {
40 | string tkn = 1;
41 | };
42 |
43 | message InitSessionResponse {
44 | bytes sesid = 1;
45 | TokenStuff token = 2;
46 | };
47 |
48 | message MpGamerHandleDto {
49 | string gh = 1;
50 | };
51 |
52 | message MpPeerAddressDto {
53 | string addr = 1;
54 | };
55 |
56 | message InitPlayer2_Parameters {
57 | MpGamerHandleDto gh = 1;
58 | MpPeerAddressDto peerAddress = 2;
59 | int32 discriminator = 3;
60 | int32 seamlessType = 4;
61 | uint32 connectionReason = 5;
62 | };
63 |
64 | message InitPlayerResult {
65 | uint32 code = 1;
66 | };
67 |
68 | message Restriction {
69 | int32 u1 = 1;
70 | int32 u2 = 2;
71 | int32 u3 = 3;
72 | }
73 |
74 | message GetRestrictionsData {
75 | repeated Restriction restriction = 1;
76 | repeated string unk2 = 2;
77 | };
78 |
79 | message GetRestrictionsResult {
80 | GetRestrictionsData data = 1;
81 | };
82 |
83 | message PlayerIdSto {
84 | int32 acctId = 1;
85 | int32 platId = 2;
86 | };
87 |
88 | message MpSessionRequestIdDto {
89 | PlayerIdSto requestor = 1;
90 | int32 index = 2;
91 | int32 hash = 3;
92 | };
93 |
94 | message QueueForSession_Seamless_Parameters {
95 | MpSessionRequestIdDto requestId = 1;
96 | uint32 optionFlags = 2;
97 | int32 x = 3;
98 | int32 y = 4;
99 | };
100 |
101 | message QueueForSessionResult {
102 | uint32 code = 1;
103 | };
104 |
105 | message QueueEntered_Parameters {
106 | uint32 queueGroup = 1;
107 | MpSessionRequestIdDto requestId = 2;
108 | uint32 optionFlags = 3;
109 | };
110 |
111 | message GuidDto {
112 | fixed64 a = 1;
113 | fixed64 b = 2;
114 | };
115 |
116 | message MpTransitionIdDto {
117 | GuidDto value = 1;
118 | };
119 |
120 | message MpSessionIdDto {
121 | GuidDto value = 1;
122 | };
123 |
124 | message SessionSubcommandEnterSession {
125 | int32 index = 1;
126 | int32 hindex = 2;
127 | uint32 sessionFlags = 3;
128 | uint32 mode = 4;
129 | int32 size = 5;
130 | int32 teamIndex = 6;
131 | MpTransitionIdDto transitionId = 7;
132 | uint32 sessionManagerType = 8;
133 | int32 slotCount = 9;
134 | };
135 |
136 | message SessionSubcommandLeaveSession {
137 | uint32 reason = 1;
138 | };
139 |
140 | message SessionSubcommandAddPlayer {
141 | PlayerIdSto id = 1;
142 | MpGamerHandleDto gh = 2;
143 | MpPeerAddressDto addr = 3;
144 | int32 index = 4;
145 | };
146 |
147 | message SessionSubcommandRemovePlayer {
148 | PlayerIdSto id = 1;
149 | };
150 |
151 | message SessionSubcommandHostChanged {
152 | int32 index = 1;
153 | };
154 |
155 | message SessionCommand {
156 | uint32 cmd = 1;
157 | string cmdname = 2;
158 | SessionSubcommandEnterSession EnterSession = 3;
159 | SessionSubcommandLeaveSession LeaveSession = 4;
160 | SessionSubcommandAddPlayer AddPlayer = 5;
161 | SessionSubcommandRemovePlayer RemovePlayer = 6;
162 | SessionSubcommandHostChanged HostChanged = 7;
163 | };
164 |
165 | message scmds_Parameters {
166 | MpSessionIdDto sid = 1;
167 | int32 ncmds = 2;
168 | repeated SessionCommand cmds = 3;
169 | };
170 |
171 | message UriType {
172 | string url = 1;
173 | };
174 |
175 | message TransitionReady_PlayerQueue_Parameters {
176 | UriType serverUri = 1;
177 | uint32 serverSandbox = 2;
178 | MpTransitionIdDto id = 3;
179 | uint32 sessionType = 4;
180 | MpSessionRequestIdDto requestId = 5;
181 | MpSessionIdDto transferId = 6;
182 | };
183 |
184 | message TransitionToSession_Parameters {
185 | MpTransitionIdDto id = 1;
186 | float x = 2;
187 | float y = 3;
188 | };
189 |
190 | message TransitionToSessionResult {
191 | uint32 code = 1;
192 | };
--------------------------------------------------------------------------------
/resources/[gamemodes]/[maps]/fivem-map-skater/map.lua:
--------------------------------------------------------------------------------
1 | spawnpoint 'a_m_y_skater_01' { x = -802.311, y = 175.056, z = 72.8446 }
2 | spawnpoint 'a_m_y_skater_02' { x = -9.96562, y = -1438.54, z = 31.1015 }
3 | spawnpoint 'a_m_y_skater_01' { x = 0.916756, y = 528.485, z = 174.628 }
4 | spawnpoint 'a_m_y_skater_02' { x = 1975.86, y = 3821.03, z = 33.4501 }
5 | spawnpoint 'a_m_y_skater_01' { x = -181.615, y = 852.8, z = 232.701 }
6 | spawnpoint 'a_m_y_skater_02' { x = 657.723, y = 457.342, z = 144.641 }
7 | spawnpoint 'a_m_y_skater_01' { x = 134.387, y = 1150.31, z = 231.594 }
8 | spawnpoint 'a_m_y_skater_02' { x = 726.14, y = 1196.91, z = 326.262 }
9 | spawnpoint 'a_m_y_skater_01' { x = 740.792, y = 1283.62, z = 360.297 }
10 | spawnpoint 'a_m_y_skater_02' { x = -437.009, y = 1059.59, z = 327.331 }
11 | spawnpoint 'a_m_y_skater_01' { x = -428.771, y = 1596.8, z = 356.338 }
12 | spawnpoint 'a_m_y_skater_02' { x = -1348.78, y = 723.87, z = 186.45 }
13 | spawnpoint 'a_m_y_skater_01' { x = -1543.24, y = 830.069, z = 182.132 }
14 | spawnpoint 'a_m_y_skater_02' { x = -2150.48, y = 222.019, z = 184.602 }
15 | spawnpoint 'a_m_y_skater_01' { x = -3032.13, y = 22.2157, z = 10.1184 }
16 | spawnpoint 'a_m_y_skater_02' { x = 3063.97, y = 5608.88, z = 209.245 }
17 | spawnpoint 'a_m_y_skater_01' { x = -2614.35, y = 1872.49, z = 167.32 }
18 | spawnpoint 'a_m_y_skater_02' { x = -1873.94, y = 2088.73, z = 140.994 }
19 | spawnpoint 'a_m_y_skater_01' { x = -597.177, y = 2092.16, z = 131.413 }
20 | spawnpoint 'a_m_y_skater_02' { x = 967.126, y = 2226.99, z = 54.0588 }
21 | spawnpoint 'a_m_y_skater_01' { x = -338.043, y = 2829, z = 56.0871 }
22 | spawnpoint 'a_m_y_skater_02' { x = 1082.25, y = -696.921, z = 58.0099 }
23 | spawnpoint 'a_m_y_skater_01' { x = 1658.31, y = -13.9234, z = 169.992 }
24 | spawnpoint 'a_m_y_skater_02' { x = 2522.98, y = -384.436, z = 92.9928 }
25 | spawnpoint 'a_m_y_skater_01' { x = 2826.27, y = -656.489, z = 1.87841 }
26 | spawnpoint 'a_m_y_skater_02' { x = 2851.12, y = 1467.5, z = 24.5554 }
27 | spawnpoint 'a_m_y_skater_01' { x = 2336.33, y = 2535.39, z = 46.5177 }
28 | spawnpoint 'a_m_y_skater_02' { x = 2410.46, y = 3077.88, z = 48.1529 }
29 | spawnpoint 'a_m_y_skater_01' { x = 2451.15, y = 3768.37, z = 41.3477 }
30 | spawnpoint 'a_m_y_skater_02' { x = 3337.78, y = 5174.8, z = 18.2108 }
31 | spawnpoint 'a_m_y_skater_01' { x = -1119.33, y = 4978.52, z = 186.26 }
32 | spawnpoint 'a_m_y_skater_02' { x = 2877.3, y = 5911.57, z = 369.618 }
33 | spawnpoint 'a_m_y_skater_01' { x = 2942.1, y = 5306.73, z = 101.52 }
34 | spawnpoint 'a_m_y_skater_02' { x = 2211.29, y = 5577.94, z = 53.872 }
35 | spawnpoint 'a_m_y_skater_01' { x = 1602.39, y = 6623.02, z = 15.8417 }
36 | spawnpoint 'a_m_y_skater_02' { x = 66.0113, y = 7203.58, z = 3.16 }
37 | spawnpoint 'a_m_y_skater_01' { x = -219.201, y = 6562.82, z = 10.9706 }
38 | spawnpoint 'a_m_y_skater_02' { x = -45.1562, y = 6301.64, z = 31.6114 }
39 | spawnpoint 'a_m_y_skater_01' { x = -1004.77, y = 4854.32, z = 274.606 }
40 | spawnpoint 'a_m_y_skater_02' { x = -1580.01, y = 5173.3, z = 19.5813 }
41 | spawnpoint 'a_m_y_skater_01' { x = -1467.95, y = 5416.2, z = 23.5959 }
42 | spawnpoint 'a_m_y_skater_02' { x = -2359.31, y = 3243.83, z = 92.9037 }
43 | spawnpoint 'a_m_y_skater_01' { x = -2612.96, y = 3555.03, z = 4.85649 }
44 | spawnpoint 'a_m_y_skater_02' { x = -2083.27, y = 2616.94, z = 3.08396 }
45 | spawnpoint 'a_m_y_skater_01' { x = -524.471, y = 4195, z = 193.731 }
46 | spawnpoint 'a_m_y_skater_02' { x = -840.713, y = 4183.18, z = 215.29 }
47 | spawnpoint 'a_m_y_skater_01' { x = -1576.24, y = 2103.87, z = 67.576 }
48 | spawnpoint 'a_m_y_skater_02' { x = -1634.37, y = 209.816, z = 60.6413 }
49 | spawnpoint 'a_m_y_skater_01' { x = -1495.07, y = 142.697, z = 55.6527 }
50 | spawnpoint 'a_m_y_skater_02' { x = -1715.41, y = -197.722, z = 57.698 }
51 | spawnpoint 'a_m_y_skater_01' { x = -1181.07, y = -505.544, z = 35.5661 }
52 | spawnpoint 'a_m_y_skater_02' { x = -1712.37, y = -1082.91, z = 13.0801 }
53 | spawnpoint 'a_m_y_skater_01' { x = -1352.43, y = -1542.75, z = 4.42268 }
54 | spawnpoint 'a_m_y_skater_02' { x = -1756.89, y = 427.531, z = 127.685 }
55 | spawnpoint 'a_m_y_skater_01' { x = 3060.2, y = 2113.2, z = 1.6613 }
56 | spawnpoint 'a_m_y_skater_02' { x = 501.646, y = 5604.53, z = 797.91 }
57 | spawnpoint 'a_m_y_skater_01' { x = 714.109, y = 4151.15, z = 35.7792 }
58 | spawnpoint 'a_m_y_skater_02' { x = -103.651, y = -967.93, z = 296.52 }
59 | spawnpoint 'a_m_y_skater_01' { x = -265.333, y = -2419.35, z = 122.366 }
60 | spawnpoint 'a_m_y_skater_02' { x = 1788.25, y = 3890.34, z = 34.3849 }
61 |
62 | --
--------------------------------------------------------------------------------
/resources/[gamemodes]/[maps]/fivem-map-hipster/map.lua:
--------------------------------------------------------------------------------
1 | vehicle_generator "airtug" { -54.26639938354492, -1679.548828125, 28.4414, heading = 228.2736053466797 }
2 |
3 | spawnpoint 'a_m_y_hipster_01' { x = -802.311, y = 175.056, z = 72.8446 }
4 | spawnpoint 'a_m_y_hipster_02' { x = -9.96562, y = -1438.54, z = 31.1015 }
5 | spawnpoint 'a_m_y_hipster_01' { x = 0.916756, y = 528.485, z = 174.628 }
6 | spawnpoint 'a_m_y_hipster_01' { x = -181.615, y = 852.8, z = 232.701 }
7 | spawnpoint 'a_m_y_hipster_02' { x = 657.723, y = 457.342, z = 144.641 }
8 | spawnpoint 'a_m_y_hipster_01' { x = 134.387, y = 1150.31, z = 231.594 }
9 | spawnpoint 'a_m_y_hipster_02' { x = 726.14, y = 1196.91, z = 326.262 }
10 | spawnpoint 'a_m_y_hipster_01' { x = 740.792, y = 1283.62, z = 360.297 }
11 | spawnpoint 'a_m_y_hipster_02' { x = -437.009, y = 1059.59, z = 327.331 }
12 | spawnpoint 'a_m_y_hipster_01' { x = -428.771, y = 1596.8, z = 356.338 }
13 | spawnpoint 'a_m_y_hipster_02' { x = -1348.78, y = 723.87, z = 186.45 }
14 | spawnpoint 'a_m_y_hipster_01' { x = -1543.24, y = 830.069, z = 182.132 }
15 | spawnpoint 'a_m_y_hipster_02' { x = -2150.48, y = 222.019, z = 184.602 }
16 | spawnpoint 'a_m_y_hipster_01' { x = -3032.13, y = 22.2157, z = 10.1184 }
17 | spawnpoint 'a_m_y_hipster_02' { x = 3063.97, y = 5608.88, z = 209.245 }
18 | spawnpoint 'a_m_y_hipster_01' { x = -2614.35, y = 1872.49, z = 167.32 }
19 | spawnpoint 'a_m_y_hipster_02' { x = -1873.94, y = 2088.73, z = 140.994 }
20 | spawnpoint 'a_m_y_hipster_01' { x = -597.177, y = 2092.16, z = 131.413 }
21 | spawnpoint 'a_m_y_hipster_02' { x = 967.126, y = 2226.99, z = 54.0588 }
22 | spawnpoint 'a_m_y_hipster_01' { x = -338.043, y = 2829, z = 56.0871 }
23 | spawnpoint 'a_m_y_hipster_02' { x = 1082.25, y = -696.921, z = 58.0099 }
24 | spawnpoint 'a_m_y_hipster_01' { x = 1658.31, y = -13.9234, z = 169.992 }
25 | spawnpoint 'a_m_y_hipster_02' { x = 2522.98, y = -384.436, z = 92.9928 }
26 | spawnpoint 'a_m_y_hipster_01' { x = 2826.27, y = -656.489, z = 1.87841 }
27 | spawnpoint 'a_m_y_hipster_02' { x = 2851.12, y = 1467.5, z = 24.5554 }
28 | spawnpoint 'a_m_y_hipster_01' { x = 2336.33, y = 2535.39, z = 46.5177 }
29 | spawnpoint 'a_m_y_hipster_02' { x = 2410.46, y = 3077.88, z = 48.1529 }
30 | spawnpoint 'a_m_y_hipster_01' { x = 2451.15, y = 3768.37, z = 41.3477 }
31 | spawnpoint 'a_m_y_hipster_02' { x = 3337.78, y = 5174.8, z = 18.2108 }
32 | spawnpoint 'a_m_y_hipster_01' { x = -1119.33, y = 4978.52, z = 186.26 }
33 | spawnpoint 'a_m_y_hipster_02' { x = 2877.3, y = 5911.57, z = 369.618 }
34 | spawnpoint 'a_m_y_hipster_01' { x = 2942.1, y = 5306.73, z = 101.52 }
35 | spawnpoint 'a_m_y_hipster_02' { x = 2211.29, y = 5577.94, z = 53.872 }
36 | spawnpoint 'a_m_y_hipster_01' { x = 1602.39, y = 6623.02, z = 15.8417 }
37 | spawnpoint 'a_m_y_hipster_02' { x = 66.0113, y = 7203.58, z = 3.16 }
38 | spawnpoint 'a_m_y_hipster_01' { x = -219.201, y = 6562.82, z = 10.9706 }
39 | spawnpoint 'a_m_y_hipster_02' { x = -45.1562, y = 6301.64, z = 31.6114 }
40 | spawnpoint 'a_m_y_hipster_01' { x = -1004.77, y = 4854.32, z = 274.606 }
41 | spawnpoint 'a_m_y_hipster_02' { x = -1580.01, y = 5173.3, z = 19.5813 }
42 | spawnpoint 'a_m_y_hipster_01' { x = -1467.95, y = 5416.2, z = 23.5959 }
43 | spawnpoint 'a_m_y_hipster_02' { x = -2359.31, y = 3243.83, z = 92.9037 }
44 | spawnpoint 'a_m_y_hipster_01' { x = -2612.96, y = 3555.03, z = 4.85649 }
45 | spawnpoint 'a_m_y_hipster_02' { x = -2083.27, y = 2616.94, z = 3.08396 }
46 | spawnpoint 'a_m_y_hipster_01' { x = -524.471, y = 4195, z = 193.731 }
47 | spawnpoint 'a_m_y_hipster_02' { x = -840.713, y = 4183.18, z = 215.29 }
48 | spawnpoint 'a_m_y_hipster_01' { x = -1576.24, y = 2103.87, z = 67.576 }
49 | spawnpoint 'a_m_y_hipster_02' { x = -1634.37, y = 209.816, z = 60.6413 }
50 | spawnpoint 'a_m_y_hipster_01' { x = -1495.07, y = 142.697, z = 55.6527 }
51 | spawnpoint 'a_m_y_hipster_02' { x = -1715.41, y = -197.722, z = 57.698 }
52 | spawnpoint 'a_m_y_hipster_01' { x = -1181.07, y = -505.544, z = 35.5661 }
53 | spawnpoint 'a_m_y_hipster_02' { x = -1712.37, y = -1082.91, z = 13.0801 }
54 | spawnpoint 'a_m_y_hipster_01' { x = -1352.43, y = -1542.75, z = 4.42268 }
55 | spawnpoint 'a_m_y_hipster_02' { x = -1756.89, y = 427.531, z = 127.685 }
56 | spawnpoint 'a_m_y_hipster_01' { x = 3060.2, y = 2113.2, z = 1.6613 }
57 | spawnpoint 'a_m_y_hipster_02' { x = 501.646, y = 5604.53, z = 797.91 }
58 | spawnpoint 'a_m_y_hipster_01' { x = 714.109, y = 4151.15, z = 35.7792 }
59 | spawnpoint 'a_m_y_hipster_02' { x = -103.651, y = -967.93, z = 296.52 }
60 | spawnpoint 'a_m_y_hipster_01' { x = -265.333, y = -2419.35, z = 122.366 }
61 | spawnpoint 'a_m_y_hipster_02' { x = 1788.25, y = 3890.34, z = 34.3849 }
62 |
63 | --
64 |
--------------------------------------------------------------------------------
/resources/[system]/runcode/runcode_web.lua:
--------------------------------------------------------------------------------
1 | local cachedFiles = {}
2 |
3 | local function sendFile(res, fileName)
4 | if cachedFiles[fileName] then
5 | res.send(cachedFiles[fileName])
6 | return
7 | end
8 |
9 | local fileData = LoadResourceFile(GetCurrentResourceName(), 'web/' .. fileName)
10 |
11 | if not fileData then
12 | res.writeHead(404)
13 | res.send('Not found.')
14 | return
15 | end
16 |
17 | cachedFiles[fileName] = fileData
18 | res.send(fileData)
19 | end
20 |
21 | local codeId = 1
22 | local codes = {}
23 |
24 | local attempts = 0
25 | local lastAttempt
26 |
27 | local function handleRunCode(data, res)
28 | if not data.lang then
29 | data.lang = 'lua'
30 | end
31 |
32 | if not data.client or data.client == '' then
33 | CreateThread(function()
34 | local result, err = RunCode(data.lang, data.code)
35 |
36 | res.send(json.encode({
37 | result = result,
38 | error = err
39 | }))
40 | end)
41 | else
42 | codes[codeId] = {
43 | timeout = GetGameTimer() + 1000,
44 | res = res
45 | }
46 |
47 | TriggerClientEvent('runcode:gotSnippet', tonumber(data.client), codeId, data.lang, data.code)
48 |
49 | codeId = codeId + 1
50 | end
51 | end
52 |
53 | RegisterNetEvent('runcode:runInBand')
54 |
55 | AddEventHandler('runcode:runInBand', function(id, data)
56 | local s = source
57 | local privs = GetPrivs(s)
58 |
59 | local res = {
60 | send = function(str)
61 | TriggerClientEvent('runcode:inBandResult', s, id, str)
62 | end
63 | }
64 |
65 | if (not data.client or data.client == '') and not privs.canServer then
66 | res.send(json.encode({ error = 'Insufficient permissions.'}))
67 | return
68 | end
69 |
70 | if (data.client and data.client ~= '') and not privs.canClient then
71 | if privs.canSelf then
72 | data.client = s
73 | else
74 | res.send(json.encode({ error = 'Insufficient permissions.'}))
75 | return
76 | end
77 | end
78 |
79 | SaveResourceFile(GetCurrentResourceName(), 'data.json', json.encode({
80 | lastSnippet = data.code,
81 | lastLang = data.lang or 'lua'
82 | }), -1)
83 |
84 | handleRunCode(data, res)
85 | end)
86 |
87 | local function handlePost(req, res)
88 | req.setDataHandler(function(body)
89 | local data = json.decode(body)
90 |
91 | if not data or not data.password or not data.code then
92 | res.send(json.encode({ error = 'Bad request.'}))
93 | return
94 | end
95 |
96 | if GetConvar('rcon_password', '') == '' then
97 | res.send(json.encode({ error = 'The server has an empty rcon_password.'}))
98 | return
99 | end
100 |
101 | if attempts > 5 or data.password ~= GetConvar('rcon_password', '') then
102 | attempts = attempts + 1
103 | lastAttempt = GetGameTimer()
104 |
105 | res.send(json.encode({ error = 'Bad password.'}))
106 | return
107 | end
108 |
109 | handleRunCode(data, res)
110 | end)
111 | end
112 |
113 | CreateThread(function()
114 | while true do
115 | Wait(1000)
116 |
117 | if attempts > 0 and (GetGameTimer() - lastAttempt) > 5000 then
118 | attempts = 0
119 | lastAttempt = 0
120 | end
121 | end
122 | end)
123 |
124 | local function returnCode(id, res, err)
125 | if not codes[id] then
126 | return
127 | end
128 |
129 | local code = codes[id]
130 | codes[id] = nil
131 |
132 | local gotFrom
133 |
134 | if source then
135 | gotFrom = GetPlayerName(source) .. ' [' .. tostring(source) .. ']'
136 | end
137 |
138 | code.res.send(json.encode({
139 | result = res,
140 | error = err,
141 | from = gotFrom
142 | }))
143 | end
144 |
145 | CreateThread(function()
146 | while true do
147 | Wait(100)
148 |
149 | for k, v in ipairs(codes) do
150 | if GetGameTimer() > v.timeout then
151 | source = nil
152 | returnCode(k, '', 'Timed out waiting on the target client.')
153 | end
154 | end
155 | end
156 | end)
157 |
158 | RegisterNetEvent('runcode:gotResult')
159 | AddEventHandler('runcode:gotResult', returnCode)
160 |
161 | SetHttpHandler(function(req, res)
162 | local path = req.path
163 |
164 | if req.method == 'POST' then
165 | return handlePost(req, res)
166 | end
167 |
168 | -- client shortcuts
169 | if req.path == '/clients' then
170 | local clientList = {}
171 |
172 | for _, id in ipairs(GetPlayers()) do
173 | table.insert(clientList, { GetPlayerName(id), id })
174 | end
175 |
176 | res.send(json.encode({
177 | clients = clientList
178 | }))
179 |
180 | return
181 | end
182 |
183 | -- should this be the index?
184 | if req.path == '/' then
185 | path = 'index.html'
186 | end
187 |
188 | -- remove any '..' from the path
189 | path = path:gsub("%.%.", "")
190 |
191 | return sendFile(res, path)
192 | end)
--------------------------------------------------------------------------------
/resources/[gameplay]/[examples]/money-fountain/client.lua:
--------------------------------------------------------------------------------
1 | -- add text entries for all the help types we have
2 | AddTextEntry('FOUNTAIN_HELP', 'This fountain currently contains $~1~.~n~Press ~INPUT_PICKUP~ to obtain $~1~.~n~Press ~INPUT_DETONATE~ to place $~1~.')
3 | AddTextEntry('FOUNTAIN_HELP_DRAINED', 'This fountain currently contains $~1~.~n~Press ~INPUT_DETONATE~ to place $~1~.')
4 | AddTextEntry('FOUNTAIN_HELP_BROKE', 'This fountain currently contains $~1~.~n~Press ~INPUT_PICKUP~ to obtain $~1~.')
5 | AddTextEntry('FOUNTAIN_HELP_BROKE_N_DRAINED', 'This fountain currently contains $~1~.')
6 | AddTextEntry('FOUNTAIN_HELP_INUSE', 'This fountain currently contains $~1~.~n~You can use it again in ~a~.')
7 |
8 | -- upvalue aliases so that we will be fast if far away
9 | local Wait = Wait
10 | local GetEntityCoords = GetEntityCoords
11 | local PlayerPedId = PlayerPedId
12 |
13 | -- timer, don't tick as frequently if we're far from any money fountain
14 | local relevanceTimer = 500
15 |
16 | CreateThread(function()
17 | local pressing = false
18 |
19 | while true do
20 | Wait(relevanceTimer)
21 |
22 | local coords = GetEntityCoords(PlayerPedId())
23 |
24 | for _, data in pairs(moneyFountains) do
25 | -- if we're near this fountain
26 | local dist = #(coords - data.coords)
27 |
28 | -- near enough to draw
29 | if dist < 40 then
30 | -- ensure per-frame tick
31 | relevanceTimer = 0
32 |
33 | DrawMarker(29, data.coords.x, data.coords.y, data.coords.z, 0, 0, 0, 0.0, 0, 0, 1.0, 1.0, 1.0, 0, 150, 0, 120, false, true, 2, false, nil, nil, false)
34 | else
35 | -- put the relevance timer back to the way it was
36 | relevanceTimer = 500
37 | end
38 |
39 | -- near enough to use
40 | if dist < 1 then
41 | -- are we able to use it? if not, display appropriate help
42 | local player = LocalPlayer
43 | local nextUse = player.state['fountain_nextUse']
44 |
45 | -- GetNetworkTime is synced for everyone
46 | if nextUse and nextUse >= GetNetworkTime() then
47 | BeginTextCommandDisplayHelp('FOUNTAIN_HELP_INUSE')
48 | AddTextComponentInteger(GlobalState['fountain_' .. data.id])
49 | AddTextComponentSubstringTime(math.tointeger(nextUse - GetNetworkTime()), 2 | 4) -- seconds (2), minutes (4)
50 | EndTextCommandDisplayHelp(0, false, false, 1000)
51 | else
52 | -- handle inputs for pickup/place
53 | if not pressing then
54 | if IsControlPressed(0, 38 --[[ INPUT_PICKUP ]]) then
55 | TriggerServerEvent('money_fountain:tryPickup', data.id)
56 | pressing = true
57 | elseif IsControlPressed(0, 47 --[[ INPUT_DETONATE ]]) then
58 | TriggerServerEvent('money_fountain:tryPlace', data.id)
59 | pressing = true
60 | end
61 | else
62 | if not IsControlPressed(0, 38 --[[ INPUT_PICKUP ]]) and
63 | not IsControlPressed(0, 47 --[[ INPUT_DETONATE ]]) then
64 | pressing = false
65 | end
66 | end
67 |
68 | -- decide the appropriate help message
69 | local youCanSpend = (player.state['money_cash'] or 0) >= data.amount
70 | local fountainCanSpend = GlobalState['fountain_' .. data.id] >= data.amount
71 |
72 | local helpName
73 |
74 | if youCanSpend and fountainCanSpend then
75 | helpName = 'FOUNTAIN_HELP'
76 | elseif youCanSpend and not fountainCanSpend then
77 | helpName = 'FOUNTAIN_HELP_DRAINED'
78 | elseif not youCanSpend and fountainCanSpend then
79 | helpName = 'FOUNTAIN_HELP_BROKE'
80 | else
81 | helpName = 'FOUNTAIN_HELP_BROKE_N_DRAINED'
82 | end
83 |
84 | -- and print it
85 | BeginTextCommandDisplayHelp(helpName)
86 | AddTextComponentInteger(GlobalState['fountain_' .. data.id])
87 |
88 | if fountainCanSpend then
89 | AddTextComponentInteger(data.amount)
90 | end
91 |
92 | if youCanSpend then
93 | AddTextComponentInteger(data.amount)
94 | end
95 |
96 | EndTextCommandDisplayHelp(0, false, false, 1000)
97 | end
98 | end
99 | end
100 | end
101 | end)
--------------------------------------------------------------------------------
/resources/[system]/[builders]/webpack/webpack_builder.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 | const workerFarm = require('worker-farm');
4 | const async = require('async');
5 | let buildingInProgress = false;
6 | let currentBuildingModule = '';
7 |
8 | // some modules will not like the custom stack trace logic
9 | const ops = Error.prepareStackTrace;
10 | Error.prepareStackTrace = undefined;
11 |
12 | const webpackBuildTask = {
13 | shouldBuild(resourceName) {
14 | const numMetaData = GetNumResourceMetadata(resourceName, 'webpack_config');
15 |
16 | if (numMetaData > 0) {
17 | for (let i = 0; i < numMetaData; i++) {
18 | const configName = GetResourceMetadata(resourceName, 'webpack_config');
19 |
20 | if (shouldBuild(configName)) {
21 | return true;
22 | }
23 | }
24 | }
25 |
26 | return false;
27 |
28 | function loadCache(config) {
29 | const cachePath = `cache/${resourceName}/${config.replace(/\//g, '_')}.json`;
30 |
31 | try {
32 | return JSON.parse(fs.readFileSync(cachePath, {encoding: 'utf8'}));
33 | } catch {
34 | return null;
35 | }
36 | }
37 |
38 | function shouldBuild(config) {
39 | const cache = loadCache(config);
40 |
41 | if (!cache) {
42 | return true;
43 | }
44 |
45 | for (const file of cache) {
46 | const stats = getStat(file.name);
47 |
48 | if (!stats ||
49 | stats.mtime !== file.stats.mtime ||
50 | stats.size !== file.stats.size ||
51 | stats.inode !== file.stats.inode) {
52 | return true;
53 | }
54 | }
55 |
56 | return false;
57 | }
58 |
59 | function getStat(path) {
60 | try {
61 | const stat = fs.statSync(path);
62 |
63 | return stat ? {
64 | mtime: stat.mtimeMs,
65 | size: stat.size,
66 | inode: stat.ino,
67 | } : null;
68 | } catch {
69 | return null;
70 | }
71 | }
72 | },
73 |
74 | build(resourceName, cb) {
75 | let buildWebpack = async () => {
76 | let error = null;
77 | const configs = [];
78 | const promises = [];
79 | const numMetaData = GetNumResourceMetadata(resourceName, 'webpack_config');
80 |
81 | for (let i = 0; i < numMetaData; i++) {
82 | configs.push(GetResourceMetadata(resourceName, 'webpack_config', i));
83 | }
84 |
85 | for (const configName of configs) {
86 | const configPath = GetResourcePath(resourceName) + '/' + configName;
87 |
88 | const cachePath = `cache/${resourceName}/${configName.replace(/\//g, '_')}.json`;
89 |
90 | try {
91 | fs.mkdirSync(path.dirname(cachePath));
92 | } catch {
93 | }
94 |
95 | const config = require(configPath);
96 |
97 | const workers = workerFarm(require.resolve('./webpack_runner'));
98 |
99 | if (config) {
100 | const resourcePath = path.resolve(GetResourcePath(resourceName));
101 |
102 | while (buildingInProgress) {
103 | console.log(`webpack is busy: we are waiting to compile ${resourceName} (${configName})`);
104 | await sleep(3000);
105 | }
106 |
107 | console.log(`${resourceName}: started building ${configName}`);
108 |
109 | buildingInProgress = true;
110 | currentBuildingModule = resourceName;
111 |
112 | promises.push(new Promise((resolve, reject) => {
113 | workers({
114 | configPath,
115 | resourcePath,
116 | cachePath
117 | }, (err, outp) => {
118 | workerFarm.end(workers);
119 |
120 | if (err) {
121 | console.error(err.stack || err);
122 | if (err.details) {
123 | console.error(err.details);
124 | }
125 |
126 | buildingInProgress = false;
127 | currentBuildingModule = '';
128 | currentBuildingScript = '';
129 | reject("worker farm webpack errored out");
130 | return;
131 | }
132 |
133 | if (outp.errors) {
134 | for (const error of outp.errors) {
135 | console.log(error);
136 | }
137 | buildingInProgress = false;
138 | currentBuildingModule = '';
139 | currentBuildingScript = '';
140 | reject("webpack got an error");
141 | return;
142 | }
143 |
144 | console.log(`${resourceName}: built ${configName}`);
145 | buildingInProgress = false;
146 | resolve();
147 | });
148 | }));
149 | }
150 | }
151 |
152 | try {
153 | await Promise.all(promises);
154 | } catch (e) {
155 | error = e.toString();
156 | }
157 |
158 | buildingInProgress = false;
159 | currentBuildingModule = '';
160 |
161 | if (error) {
162 | cb(false, error);
163 | } else cb(true);
164 | };
165 | buildWebpack().then();
166 | }
167 | };
168 |
169 | function sleep(ms) {
170 | return new Promise(resolve => setTimeout(resolve, ms));
171 | }
172 |
173 | RegisterResourceBuildTaskFactory('z_webpack', () => webpackBuildTask);
174 |
--------------------------------------------------------------------------------
/resources/[gameplay]/playernames/playernames_cl.lua:
--------------------------------------------------------------------------------
1 | local mpGamerTags = {}
2 | local mpGamerTagSettings = {}
3 |
4 | local gtComponent = {
5 | GAMER_NAME = 0,
6 | CREW_TAG = 1,
7 | healthArmour = 2,
8 | BIG_TEXT = 3,
9 | AUDIO_ICON = 4,
10 | MP_USING_MENU = 5,
11 | MP_PASSIVE_MODE = 6,
12 | WANTED_STARS = 7,
13 | MP_DRIVER = 8,
14 | MP_CO_DRIVER = 9,
15 | MP_TAGGED = 10,
16 | GAMER_NAME_NEARBY = 11,
17 | ARROW = 12,
18 | MP_PACKAGES = 13,
19 | INV_IF_PED_FOLLOWING = 14,
20 | RANK_TEXT = 15,
21 | MP_TYPING = 16
22 | }
23 |
24 | local function makeSettings()
25 | return {
26 | alphas = {},
27 | colors = {},
28 | healthColor = false,
29 | toggles = {},
30 | wantedLevel = false
31 | }
32 | end
33 |
34 | local templateStr
35 |
36 | function updatePlayerNames()
37 | -- re-run this function the next frame
38 | SetTimeout(0, updatePlayerNames)
39 |
40 | -- return if no template string is set
41 | if not templateStr then
42 | return
43 | end
44 |
45 | -- get local coordinates to compare to
46 | local localCoords = GetEntityCoords(PlayerPedId())
47 |
48 | -- for each valid player index
49 | for _, i in ipairs(GetActivePlayers()) do
50 | -- if the player exists
51 | if i ~= PlayerId() then
52 | -- get their ped
53 | local ped = GetPlayerPed(i)
54 | local pedCoords = GetEntityCoords(ped)
55 |
56 | -- make a new settings list if needed
57 | if not mpGamerTagSettings[i] then
58 | mpGamerTagSettings[i] = makeSettings()
59 | end
60 |
61 | -- check the ped, because changing player models may recreate the ped
62 | -- also check gamer tag activity in case the game deleted the gamer tag
63 | if not mpGamerTags[i] or mpGamerTags[i].ped ~= ped or not IsMpGamerTagActive(mpGamerTags[i].tag) then
64 | local nameTag = formatPlayerNameTag(i, templateStr)
65 |
66 | -- remove any existing tag
67 | if mpGamerTags[i] then
68 | RemoveMpGamerTag(mpGamerTags[i].tag)
69 | end
70 |
71 | -- store the new tag
72 | mpGamerTags[i] = {
73 | tag = CreateMpGamerTag(GetPlayerPed(i), nameTag, false, false, '', 0),
74 | ped = ped
75 | }
76 | end
77 |
78 | -- store the tag in a local
79 | local tag = mpGamerTags[i].tag
80 |
81 | -- should the player be renamed? this is set by events
82 | if mpGamerTagSettings[i].rename then
83 | SetMpGamerTagName(tag, formatPlayerNameTag(i, templateStr))
84 | mpGamerTagSettings[i].rename = nil
85 | end
86 |
87 | -- check distance
88 | local distance = #(pedCoords - localCoords)
89 |
90 | -- show/hide based on nearbyness/line-of-sight
91 | -- nearby checks are primarily to prevent a lot of LOS checks
92 | if distance < 250 and HasEntityClearLosToEntity(PlayerPedId(), ped, 17) then
93 | SetMpGamerTagVisibility(tag, gtComponent.GAMER_NAME, true)
94 | SetMpGamerTagVisibility(tag, gtComponent.healthArmour, IsPlayerTargettingEntity(PlayerId(), ped))
95 | SetMpGamerTagVisibility(tag, gtComponent.AUDIO_ICON, NetworkIsPlayerTalking(i))
96 |
97 | SetMpGamerTagAlpha(tag, gtComponent.AUDIO_ICON, 255)
98 | SetMpGamerTagAlpha(tag, gtComponent.healthArmour, 255)
99 |
100 | -- override settings
101 | local settings = mpGamerTagSettings[i]
102 |
103 | for k, v in pairs(settings.toggles) do
104 | SetMpGamerTagVisibility(tag, gtComponent[k], v)
105 | end
106 |
107 | for k, v in pairs(settings.alphas) do
108 | SetMpGamerTagAlpha(tag, gtComponent[k], v)
109 | end
110 |
111 | for k, v in pairs(settings.colors) do
112 | SetMpGamerTagColour(tag, gtComponent[k], v)
113 | end
114 |
115 | if settings.wantedLevel then
116 | SetMpGamerTagWantedLevel(tag, settings.wantedLevel)
117 | end
118 |
119 | if settings.healthColor then
120 | SetMpGamerTagHealthBarColour(tag, settings.healthColor)
121 | end
122 | else
123 | SetMpGamerTagVisibility(tag, gtComponent.GAMER_NAME, false)
124 | SetMpGamerTagVisibility(tag, gtComponent.healthArmour, false)
125 | SetMpGamerTagVisibility(tag, gtComponent.AUDIO_ICON, false)
126 | end
127 | elseif mpGamerTags[i] then
128 | RemoveMpGamerTag(mpGamerTags[i].tag)
129 |
130 | mpGamerTags[i] = nil
131 | end
132 | end
133 | end
134 |
135 | local function getSettings(id)
136 | local i = GetPlayerFromServerId(tonumber(id))
137 |
138 | if not mpGamerTagSettings[i] then
139 | mpGamerTagSettings[i] = makeSettings()
140 | end
141 |
142 | return mpGamerTagSettings[i]
143 | end
144 |
145 | RegisterNetEvent('playernames:configure')
146 |
147 | AddEventHandler('playernames:configure', function(id, key, ...)
148 | local args = table.pack(...)
149 |
150 | if key == 'tglc' then
151 | getSettings(id).toggles[args[1]] = args[2]
152 | elseif key == 'seta' then
153 | getSettings(id).alphas[args[1]] = args[2]
154 | elseif key == 'setc' then
155 | getSettings(id).colors[args[1]] = args[2]
156 | elseif key == 'setw' then
157 | getSettings(id).wantedLevel = args[1]
158 | elseif key == 'sehc' then
159 | getSettings(id).healthColor = args[1]
160 | elseif key == 'rnme' then
161 | getSettings(id).rename = true
162 | elseif key == 'name' then
163 | getSettings(id).serverName = args[1]
164 | getSettings(id).rename = true
165 | elseif key == 'tpl' then
166 | for _, v in pairs(mpGamerTagSettings) do
167 | v.rename = true
168 | end
169 |
170 | templateStr = args[1]
171 | end
172 | end)
173 |
174 | AddEventHandler('playernames:extendContext', function(i, cb)
175 | cb('serverName', getSettings(GetPlayerServerId(i)).serverName)
176 | end)
177 |
178 | AddEventHandler('onResourceStop', function(name)
179 | if name == GetCurrentResourceName() then
180 | for _, v in pairs(mpGamerTags) do
181 | RemoveMpGamerTag(v.tag)
182 | end
183 | end
184 | end)
185 |
186 | SetTimeout(0, function()
187 | TriggerServerEvent('playernames:init')
188 | end)
189 |
190 | -- run this function every frame
191 | SetTimeout(0, updatePlayerNames)
192 |
--------------------------------------------------------------------------------
/resources/[gameplay]/player-data/server.lua:
--------------------------------------------------------------------------------
1 | --- player-data is a basic resource to showcase player identifier storage
2 | --
3 | -- it works in a fairly simple way: a set of identifiers is assigned to an account ID, and said
4 | -- account ID is then returned/added as state bag
5 | --
6 | -- it also implements the `cfx.re/playerData.v1alpha1` spec, which is exposed through the following:
7 | -- - getPlayerId(source: string)
8 | -- - getPlayerById(dbId: string)
9 | -- - getPlayerIdFromIdentifier(identifier: string)
10 | -- - setting `cfx.re/playerData@id` state bag field on the player
11 |
12 | -- identifiers that we'll ignore (e.g. IP) as they're low-trust/high-variance
13 | local identifierBlocklist = {
14 | ip = true
15 | }
16 |
17 | -- function to check if the identifier is blocked
18 | local function isIdentifierBlocked(identifier)
19 | -- Lua pattern to correctly split
20 | local idType = identifier:match('([^:]+):')
21 |
22 | -- ensure it's a boolean
23 | return identifierBlocklist[idType] or false
24 | end
25 |
26 | -- our database schema, in hierarchical KVS syntax:
27 | -- player:
28 | -- :
29 | -- identifier:
30 | -- : 'true'
31 | -- identifier:
32 | -- :
33 |
34 | -- list of player indices to data
35 | local players = {}
36 |
37 | -- list of player DBIDs to player indices
38 | local playersById = {}
39 |
40 | -- a sequence field using KVS
41 | local function incrementId()
42 | local nextId = GetResourceKvpInt('nextId')
43 | nextId = nextId + 1
44 | SetResourceKvpInt('nextId', nextId)
45 |
46 | return nextId
47 | end
48 |
49 | -- gets the ID tied to an identifier in the schema, or nil
50 | local function getPlayerIdFromIdentifier(identifier)
51 | local str = GetResourceKvpString(('identifier:%s'):format(identifier))
52 |
53 | if not str then
54 | return nil
55 | end
56 |
57 | return msgpack.unpack(str).id
58 | end
59 |
60 | -- stores the identifier + adds to a logging list
61 | local function setPlayerIdFromIdentifier(identifier, id)
62 | local str = ('identifier:%s'):format(identifier)
63 | SetResourceKvp(str, msgpack.pack({ id = id }))
64 | SetResourceKvp(('player:%s:identifier:%s'):format(id, identifier), 'true')
65 | end
66 |
67 | -- stores any new identifiers for this player ID
68 | local function storeIdentifiers(playerIdx, newId)
69 | for _, identifier in ipairs(GetPlayerIdentifiers(playerIdx)) do
70 | if not isIdentifierBlocked(identifier) then
71 | -- TODO: check if the player already has an identifier of this type
72 | setPlayerIdFromIdentifier(identifier, newId)
73 | end
74 | end
75 | end
76 |
77 | -- registers a new player (increments sequence, stores data, returns ID)
78 | local function registerPlayer(playerIdx)
79 | local newId = incrementId()
80 | storeIdentifiers(playerIdx, newId)
81 |
82 | return newId
83 | end
84 |
85 | -- initializes a player's data set
86 | local function setupPlayer(playerIdx)
87 | -- try getting the oldest-known identity from all the player's identifiers
88 | local defaultId = 0xFFFFFFFFFF
89 | local lowestId = defaultId
90 |
91 | for _, identifier in ipairs(GetPlayerIdentifiers(playerIdx)) do
92 | if not isIdentifierBlocked(identifier) then
93 | local dbId = getPlayerIdFromIdentifier(identifier)
94 |
95 | if dbId then
96 | if dbId < lowestId then
97 | lowestId = dbId
98 | end
99 | end
100 | end
101 | end
102 |
103 | -- if this is the default ID, register. if not, update
104 | local playerId
105 |
106 | if lowestId == defaultId then
107 | playerId = registerPlayer(playerIdx)
108 | else
109 | storeIdentifiers(playerIdx, lowestId)
110 | playerId = lowestId
111 | end
112 |
113 | -- add state bag field
114 | if Player then
115 | Player(playerIdx).state['cfx.re/playerData@id'] = playerId
116 | end
117 |
118 | -- and add to our caching tables
119 | players[playerIdx] = {
120 | dbId = playerId
121 | }
122 |
123 | playersById[tostring(playerId)] = playerIdx
124 | end
125 |
126 | -- we want to add a player pretty early
127 | AddEventHandler('playerConnecting', function()
128 | local playerIdx = tostring(source)
129 | setupPlayer(playerIdx)
130 | end)
131 |
132 | -- and migrate them to a 'joining' ID where possible
133 | RegisterNetEvent('playerJoining')
134 |
135 | AddEventHandler('playerJoining', function(oldIdx)
136 | -- resource restart race condition
137 | local oldPlayer = players[tostring(oldIdx)]
138 |
139 | if oldPlayer then
140 | players[tostring(source)] = oldPlayer
141 | players[tostring(oldIdx)] = nil
142 | else
143 | setupPlayer(tostring(source))
144 | end
145 | end)
146 |
147 | -- remove them if they're dropped
148 | AddEventHandler('playerDropped', function()
149 | local player = players[tostring(source)]
150 |
151 | if player then
152 | playersById[tostring(player.dbId)] = nil
153 | end
154 |
155 | players[tostring(source)] = nil
156 | end)
157 |
158 | -- and when the resource is restarted, set up all players that are on right now
159 | for _, player in ipairs(GetPlayers()) do
160 | setupPlayer(player)
161 | end
162 |
163 | -- also a quick command to get the current state
164 | RegisterCommand('playerData', function(source, args)
165 | if not args[1] then
166 | print('Usage:')
167 | print('\tplayerData getId : gets identifiers for ID')
168 | print('\tplayerData getIdentifier : gets ID for identifier')
169 |
170 | return
171 | end
172 |
173 | if args[1] == 'getId' then
174 | local prefix = ('player:%s:identifier:'):format(args[2])
175 | local handle = StartFindKvp(prefix)
176 | local key
177 |
178 | repeat
179 | key = FindKvp(handle)
180 |
181 | if key then
182 | print('result:', key:sub(#prefix + 1))
183 | end
184 | until not key
185 |
186 | EndFindKvp(handle)
187 | elseif args[1] == 'getIdentifier' then
188 | print('result:', getPlayerIdFromIdentifier(args[2]))
189 | end
190 | end, true)
191 |
192 | -- COMPATIBILITY for server versions that don't export provide
193 | local function getExportEventName(resource, name)
194 | return string.format('__cfx_export_%s_%s', resource, name)
195 | end
196 |
197 | function AddExport(name, fn)
198 | if not Citizen.Traits or not Citizen.Traits.ProvidesExports then
199 | AddEventHandler(getExportEventName('cfx.re/playerData.v1alpha1', name), function(setCB)
200 | setCB(fn)
201 | end)
202 | end
203 |
204 | exports(name, fn)
205 | end
206 |
207 | -- exports
208 | AddExport('getPlayerIdFromIdentifier', getPlayerIdFromIdentifier)
209 |
210 | AddExport('getPlayerId', function(playerIdx)
211 | local player = players[tostring(playerIdx)]
212 |
213 | if not player then
214 | return nil
215 | end
216 |
217 | return player.dbId
218 | end)
219 |
220 | AddExport('getPlayerById', function(playerId)
221 | return playersById[tostring(playerId)]
222 | end)
--------------------------------------------------------------------------------
/resources/[gameplay]/chat/cl_chat.lua:
--------------------------------------------------------------------------------
1 | local isRDR = not TerraingridActivate and true or false
2 |
3 | local chatInputActive = false
4 | local chatInputActivating = false
5 | local chatLoaded = false
6 |
7 | RegisterNetEvent('chatMessage')
8 | RegisterNetEvent('chat:addTemplate')
9 | RegisterNetEvent('chat:addMessage')
10 | RegisterNetEvent('chat:addSuggestion')
11 | RegisterNetEvent('chat:addSuggestions')
12 | RegisterNetEvent('chat:addMode')
13 | RegisterNetEvent('chat:removeMode')
14 | RegisterNetEvent('chat:removeSuggestion')
15 | RegisterNetEvent('chat:clear')
16 |
17 | -- internal events
18 | RegisterNetEvent('__cfx_internal:serverPrint')
19 |
20 | RegisterNetEvent('_chat:messageEntered')
21 |
22 | --deprecated, use chat:addMessage
23 | AddEventHandler('chatMessage', function(author, color, text)
24 | local args = { text }
25 | if author ~= "" then
26 | table.insert(args, 1, author)
27 | end
28 | SendNUIMessage({
29 | type = 'ON_MESSAGE',
30 | message = {
31 | color = color,
32 | multiline = true,
33 | args = args
34 | }
35 | })
36 | end)
37 |
38 | AddEventHandler('__cfx_internal:serverPrint', function(msg)
39 | print(msg)
40 |
41 | SendNUIMessage({
42 | type = 'ON_MESSAGE',
43 | message = {
44 | templateId = 'print',
45 | multiline = true,
46 | args = { msg },
47 | mode = '_global'
48 | }
49 | })
50 | end)
51 |
52 | -- addMessage
53 | local addMessage = function(message)
54 | if type(message) == 'string' then
55 | message = {
56 | args = { message }
57 | }
58 | end
59 |
60 | SendNUIMessage({
61 | type = 'ON_MESSAGE',
62 | message = message
63 | })
64 | end
65 |
66 | exports('addMessage', addMessage)
67 | AddEventHandler('chat:addMessage', addMessage)
68 |
69 | -- addSuggestion
70 | local addSuggestion = function(name, help, params)
71 | SendNUIMessage({
72 | type = 'ON_SUGGESTION_ADD',
73 | suggestion = {
74 | name = name,
75 | help = help,
76 | params = params or nil
77 | }
78 | })
79 | end
80 |
81 | exports('addSuggestion', addSuggestion)
82 | AddEventHandler('chat:addSuggestion', addSuggestion)
83 |
84 | AddEventHandler('chat:addSuggestions', function(suggestions)
85 | for _, suggestion in ipairs(suggestions) do
86 | SendNUIMessage({
87 | type = 'ON_SUGGESTION_ADD',
88 | suggestion = suggestion
89 | })
90 | end
91 | end)
92 |
93 | AddEventHandler('chat:removeSuggestion', function(name)
94 | SendNUIMessage({
95 | type = 'ON_SUGGESTION_REMOVE',
96 | name = name
97 | })
98 | end)
99 |
100 | AddEventHandler('chat:addMode', function(mode)
101 | SendNUIMessage({
102 | type = 'ON_MODE_ADD',
103 | mode = mode
104 | })
105 | end)
106 |
107 | AddEventHandler('chat:removeMode', function(name)
108 | SendNUIMessage({
109 | type = 'ON_MODE_REMOVE',
110 | name = name
111 | })
112 | end)
113 |
114 | AddEventHandler('chat:addTemplate', function(id, html)
115 | SendNUIMessage({
116 | type = 'ON_TEMPLATE_ADD',
117 | template = {
118 | id = id,
119 | html = html
120 | }
121 | })
122 | end)
123 |
124 | AddEventHandler('chat:clear', function(name)
125 | SendNUIMessage({
126 | type = 'ON_CLEAR'
127 | })
128 | end)
129 |
130 | RegisterNUICallback('chatResult', function(data, cb)
131 | chatInputActive = false
132 | SetNuiFocus(false)
133 |
134 | if not data.canceled then
135 | local id = PlayerId()
136 |
137 | --deprecated
138 | local r, g, b = 0, 0x99, 255
139 |
140 | if data.message:sub(1, 1) == '/' then
141 | ExecuteCommand(data.message:sub(2))
142 | else
143 | TriggerServerEvent('_chat:messageEntered', GetPlayerName(id), { r, g, b }, data.message, data.mode)
144 | end
145 | end
146 |
147 | cb('ok')
148 | end)
149 |
150 | local function refreshCommands()
151 | if GetRegisteredCommands then
152 | local registeredCommands = GetRegisteredCommands()
153 |
154 | local suggestions = {}
155 |
156 | for _, command in ipairs(registeredCommands) do
157 | if IsAceAllowed(('command.%s'):format(command.name)) and command.name ~= 'toggleChat' then
158 | table.insert(suggestions, {
159 | name = '/' .. command.name,
160 | help = ''
161 | })
162 | end
163 | end
164 |
165 | TriggerEvent('chat:addSuggestions', suggestions)
166 | end
167 | end
168 |
169 | local function refreshThemes()
170 | local themes = {}
171 |
172 | for resIdx = 0, GetNumResources() - 1 do
173 | local resource = GetResourceByFindIndex(resIdx)
174 |
175 | if GetResourceState(resource) == 'started' then
176 | local numThemes = GetNumResourceMetadata(resource, 'chat_theme')
177 |
178 | if numThemes > 0 then
179 | local themeName = GetResourceMetadata(resource, 'chat_theme')
180 | local themeData = json.decode(GetResourceMetadata(resource, 'chat_theme_extra') or 'null')
181 |
182 | if themeName and themeData then
183 | themeData.baseUrl = 'nui://' .. resource .. '/'
184 | themes[themeName] = themeData
185 | end
186 | end
187 | end
188 | end
189 |
190 | SendNUIMessage({
191 | type = 'ON_UPDATE_THEMES',
192 | themes = themes
193 | })
194 | end
195 |
196 | AddEventHandler('onClientResourceStart', function(resName)
197 | Wait(500)
198 |
199 | refreshCommands()
200 | refreshThemes()
201 | end)
202 |
203 | AddEventHandler('onClientResourceStop', function(resName)
204 | Wait(500)
205 |
206 | refreshCommands()
207 | refreshThemes()
208 | end)
209 |
210 | RegisterNUICallback('loaded', function(data, cb)
211 | TriggerServerEvent('chat:init')
212 |
213 | refreshCommands()
214 | refreshThemes()
215 |
216 | chatLoaded = true
217 |
218 | cb('ok')
219 | end)
220 |
221 | local CHAT_HIDE_STATES = {
222 | SHOW_WHEN_ACTIVE = 0,
223 | ALWAYS_SHOW = 1,
224 | ALWAYS_HIDE = 2
225 | }
226 |
227 | local kvpEntry = GetResourceKvpString('hideState')
228 | local chatHideState = kvpEntry and tonumber(kvpEntry) or CHAT_HIDE_STATES.SHOW_WHEN_ACTIVE
229 | local isFirstHide = true
230 |
231 | if not isRDR then
232 | if RegisterKeyMapping then
233 | RegisterKeyMapping('toggleChat', 'Toggle chat', 'keyboard', 'l')
234 | end
235 |
236 | RegisterCommand('toggleChat', function()
237 | if chatHideState == CHAT_HIDE_STATES.SHOW_WHEN_ACTIVE then
238 | chatHideState = CHAT_HIDE_STATES.ALWAYS_SHOW
239 | elseif chatHideState == CHAT_HIDE_STATES.ALWAYS_SHOW then
240 | chatHideState = CHAT_HIDE_STATES.ALWAYS_HIDE
241 | elseif chatHideState == CHAT_HIDE_STATES.ALWAYS_HIDE then
242 | chatHideState = CHAT_HIDE_STATES.SHOW_WHEN_ACTIVE
243 | end
244 |
245 | isFirstHide = false
246 |
247 | SetResourceKvp('hideState', tostring(chatHideState))
248 | end, false)
249 | end
250 |
251 | Citizen.CreateThread(function()
252 | SetTextChatEnabled(false)
253 | SetNuiFocus(false)
254 |
255 | local lastChatHideState = -1
256 | local origChatHideState = -1
257 |
258 | while true do
259 | Wait(0)
260 |
261 | if not chatInputActive then
262 | if IsControlPressed(0, isRDR and `INPUT_MP_TEXT_CHAT_ALL` or 245) --[[ INPUT_MP_TEXT_CHAT_ALL ]] then
263 | chatInputActive = true
264 | chatInputActivating = true
265 |
266 | SendNUIMessage({
267 | type = 'ON_OPEN'
268 | })
269 | end
270 | end
271 |
272 | if chatInputActivating then
273 | if not IsControlPressed(0, isRDR and `INPUT_MP_TEXT_CHAT_ALL` or 245) then
274 | SetNuiFocus(true)
275 |
276 | chatInputActivating = false
277 | end
278 | end
279 |
280 | if chatLoaded then
281 | local forceHide = IsScreenFadedOut() or IsPauseMenuActive()
282 | local wasForceHide = false
283 |
284 | if chatHideState ~= CHAT_HIDE_STATES.ALWAYS_HIDE then
285 | if forceHide then
286 | origChatHideState = chatHideState
287 | chatHideState = CHAT_HIDE_STATES.ALWAYS_HIDE
288 | end
289 | elseif not forceHide and origChatHideState ~= -1 then
290 | chatHideState = origChatHideState
291 | origChatHideState = -1
292 | wasForceHide = true
293 | end
294 |
295 | if chatHideState ~= lastChatHideState then
296 | lastChatHideState = chatHideState
297 |
298 | SendNUIMessage({
299 | type = 'ON_SCREEN_STATE_CHANGE',
300 | hideState = chatHideState,
301 | fromUserInteraction = not forceHide and not isFirstHide and not wasForceHide
302 | })
303 |
304 | isFirstHide = false
305 | end
306 | end
307 | end
308 | end)
309 |
--------------------------------------------------------------------------------
/resources/[gameplay]/chat/sv_chat.lua:
--------------------------------------------------------------------------------
1 | RegisterServerEvent('chat:init')
2 | RegisterServerEvent('chat:addTemplate')
3 | RegisterServerEvent('chat:addMessage')
4 | RegisterServerEvent('chat:addSuggestion')
5 | RegisterServerEvent('chat:removeSuggestion')
6 | RegisterServerEvent('_chat:messageEntered')
7 | RegisterServerEvent('chat:clear')
8 | RegisterServerEvent('__cfx_internal:commandFallback')
9 |
10 | -- this is a built-in event, but somehow needs to be registered
11 | RegisterNetEvent('playerJoining')
12 |
13 | exports('addMessage', function(target, message)
14 | if not message then
15 | message = target
16 | target = -1
17 | end
18 |
19 | if not target or not message then return end
20 |
21 | TriggerClientEvent('chat:addMessage', target, message)
22 | end)
23 |
24 | local hooks = {}
25 | local hookIdx = 1
26 |
27 | exports('registerMessageHook', function(hook)
28 | local resource = GetInvokingResource()
29 | hooks[hookIdx + 1] = {
30 | fn = hook,
31 | resource = resource
32 | }
33 |
34 | hookIdx = hookIdx + 1
35 | end)
36 |
37 | local modes = {}
38 |
39 | local function getMatchingPlayers(seObject)
40 | local players = GetPlayers()
41 | local retval = {}
42 |
43 | for _, v in ipairs(players) do
44 | if IsPlayerAceAllowed(v, seObject) then
45 | retval[#retval + 1] = v
46 | end
47 | end
48 |
49 | return retval
50 | end
51 |
52 | exports('registerMode', function(modeData)
53 | if not modeData.name or not modeData.displayName or not modeData.cb then
54 | return false
55 | end
56 |
57 | local resource = GetInvokingResource()
58 |
59 | modes[modeData.name] = modeData
60 | modes[modeData.name].resource = resource
61 |
62 | local clObj = {
63 | name = modeData.name,
64 | displayName = modeData.displayName,
65 | color = modeData.color or '#fff',
66 | isChannel = modeData.isChannel,
67 | isGlobal = modeData.isGlobal,
68 | }
69 |
70 | if not modeData.seObject then
71 | TriggerClientEvent('chat:addMode', -1, clObj)
72 | else
73 | for _, v in ipairs(getMatchingPlayers(modeData.seObject)) do
74 | TriggerClientEvent('chat:addMode', v, clObj)
75 | end
76 | end
77 |
78 | return true
79 | end)
80 |
81 | local function unregisterHooks(resource)
82 | local toRemove = {}
83 |
84 | for k, v in pairs(hooks) do
85 | if v.resource == resource then
86 | table.insert(toRemove, k)
87 | end
88 | end
89 |
90 | for _, v in ipairs(toRemove) do
91 | hooks[v] = nil
92 | end
93 |
94 | toRemove = {}
95 |
96 | for k, v in pairs(modes) do
97 | if v.resource == resource then
98 | table.insert(toRemove, k)
99 | end
100 | end
101 |
102 | for _, v in ipairs(toRemove) do
103 | TriggerClientEvent('chat:removeMode', -1, {
104 | name = v
105 | })
106 |
107 | modes[v] = nil
108 | end
109 | end
110 |
111 | local function routeMessage(source, author, message, mode, fromConsole)
112 | if source >= 1 then
113 | author = GetPlayerName(source)
114 | end
115 |
116 | local outMessage = {
117 | color = { 255, 255, 255 },
118 | multiline = true,
119 | args = { message },
120 | mode = mode
121 | }
122 |
123 | if author ~= "" then
124 | outMessage.args = { author, message }
125 | end
126 |
127 | if mode and modes[mode] then
128 | local modeData = modes[mode]
129 |
130 | if modeData.seObject and not IsPlayerAceAllowed(source, modeData.seObject) then
131 | return
132 | end
133 | end
134 |
135 | local messageCanceled = false
136 | local routingTarget = -1
137 |
138 | local hookRef = {
139 | updateMessage = function(t)
140 | -- shallow merge
141 | for k, v in pairs(t) do
142 | if k == 'template' then
143 | outMessage['template'] = v:gsub('%{%}', outMessage['template'] or '@default')
144 | elseif k == 'params' then
145 | if not outMessage.params then
146 | outMessage.params = {}
147 | end
148 |
149 | for pk, pv in pairs(v) do
150 | outMessage.params[pk] = pv
151 | end
152 | else
153 | outMessage[k] = v
154 | end
155 | end
156 | end,
157 |
158 | cancel = function()
159 | messageCanceled = true
160 | end,
161 |
162 | setSeObject = function(object)
163 | routingTarget = getMatchingPlayers(object)
164 | end,
165 |
166 | setRouting = function(target)
167 | routingTarget = target
168 | end
169 | }
170 |
171 | for _, hook in pairs(hooks) do
172 | if hook.fn then
173 | hook.fn(source, outMessage, hookRef)
174 | end
175 | end
176 |
177 | if modes[mode] then
178 | local m = modes[mode]
179 |
180 | m.cb(source, outMessage, hookRef)
181 | end
182 |
183 | if messageCanceled then
184 | return
185 | end
186 |
187 | TriggerEvent('chatMessage', source, #outMessage.args > 1 and outMessage.args[1] or '', outMessage.args[#outMessage.args])
188 |
189 | if not WasEventCanceled() then
190 | if type(routingTarget) ~= 'table' then
191 | TriggerClientEvent('chat:addMessage', routingTarget, outMessage)
192 | else
193 | for _, id in ipairs(routingTarget) do
194 | TriggerClientEvent('chat:addMessage', id, outMessage)
195 | end
196 | end
197 | end
198 |
199 | if not fromConsole then
200 | print(author .. '^7' .. (modes[mode] and (' (' .. modes[mode].displayName .. ')') or '') .. ': ' .. message .. '^7')
201 | end
202 | end
203 |
204 | AddEventHandler('_chat:messageEntered', function(author, color, message, mode)
205 | if not message or not author then
206 | return
207 | end
208 |
209 | local source = source
210 |
211 | routeMessage(source, author, message, mode)
212 | end)
213 |
214 | AddEventHandler('__cfx_internal:commandFallback', function(command)
215 | local name = GetPlayerName(source)
216 |
217 | -- route the message as if it were a /command
218 | routeMessage(source, name, '/' .. command, nil, true)
219 |
220 | CancelEvent()
221 | end)
222 |
223 | -- player join messages
224 | AddEventHandler('playerJoining', function()
225 | if GetConvarInt('chat_showJoins', 1) == 0 then
226 | return
227 | end
228 |
229 | TriggerClientEvent('chatMessage', -1, '', { 255, 255, 255 }, '^2* ' .. GetPlayerName(source) .. ' joined.')
230 | end)
231 |
232 | AddEventHandler('playerDropped', function(reason)
233 | if GetConvarInt('chat_showQuits', 1) == 0 then
234 | return
235 | end
236 |
237 | TriggerClientEvent('chatMessage', -1, '', { 255, 255, 255 }, '^2* ' .. GetPlayerName(source) ..' left (' .. reason .. ')')
238 | end)
239 |
240 | RegisterCommand('say', function(source, args, rawCommand)
241 | routeMessage(source, (source == 0) and 'console' or GetPlayerName(source), rawCommand:sub(5), nil, true)
242 | end)
243 |
244 | -- command suggestions for clients
245 | local function refreshCommands(player)
246 | if GetRegisteredCommands then
247 | local registeredCommands = GetRegisteredCommands()
248 |
249 | local suggestions = {}
250 |
251 | for _, command in ipairs(registeredCommands) do
252 | if IsPlayerAceAllowed(player, ('command.%s'):format(command.name)) then
253 | table.insert(suggestions, {
254 | name = '/' .. command.name,
255 | help = ''
256 | })
257 | end
258 | end
259 |
260 | TriggerClientEvent('chat:addSuggestions', player, suggestions)
261 | end
262 | end
263 |
264 | AddEventHandler('chat:init', function()
265 | local source = source
266 | refreshCommands(source)
267 |
268 | for _, modeData in pairs(modes) do
269 | local clObj = {
270 | name = modeData.name,
271 | displayName = modeData.displayName,
272 | color = modeData.color or '#fff',
273 | isChannel = modeData.isChannel,
274 | isGlobal = modeData.isGlobal,
275 | }
276 |
277 | if not modeData.seObject or IsPlayerAceAllowed(source, modeData.seObject) then
278 | TriggerClientEvent('chat:addMode', source, clObj)
279 | end
280 | end
281 | end)
282 |
283 | AddEventHandler('onServerResourceStart', function(resName)
284 | Wait(500)
285 |
286 | for _, player in ipairs(GetPlayers()) do
287 | refreshCommands(player)
288 | end
289 | end)
290 |
291 | AddEventHandler('onResourceStop', function(resName)
292 | unregisterHooks(resName)
293 | end)
--------------------------------------------------------------------------------
/resources/[system]/sessionmanager-rdr3/sm_server.js:
--------------------------------------------------------------------------------
1 | const protobuf = require("@citizenfx/protobufjs");
2 |
3 | const playerDatas = {};
4 | let slotsUsed = 0;
5 |
6 | function assignSlotId() {
7 | for (let i = 0; i < 32; i++) {
8 | if (!(slotsUsed & (1 << i))) {
9 | slotsUsed |= (1 << i);
10 | return i;
11 | }
12 | }
13 |
14 | return -1;
15 | }
16 |
17 | let hostIndex = -1;
18 | const isOneSync = GetConvar("onesync", "off") !== "off";
19 |
20 | protobuf.load(GetResourcePath(GetCurrentResourceName()) + "/rline.proto", function(err, root) {
21 | if (err) {
22 | console.log(err);
23 | return;
24 | }
25 |
26 | const RpcMessage = root.lookupType("rline.RpcMessage");
27 | const RpcResponseMessage = root.lookupType("rline.RpcResponseMessage");
28 | const InitSessionResponse = root.lookupType("rline.InitSessionResponse");
29 | const InitPlayer2_Parameters = root.lookupType("rline.InitPlayer2_Parameters");
30 | const InitPlayerResult = root.lookupType("rline.InitPlayerResult");
31 | const GetRestrictionsResult = root.lookupType("rline.GetRestrictionsResult");
32 | const QueueForSession_Seamless_Parameters = root.lookupType("rline.QueueForSession_Seamless_Parameters");
33 | const QueueForSessionResult = root.lookupType("rline.QueueForSessionResult");
34 | const QueueEntered_Parameters = root.lookupType("rline.QueueEntered_Parameters");
35 | const TransitionReady_PlayerQueue_Parameters = root.lookupType("rline.TransitionReady_PlayerQueue_Parameters");
36 | const TransitionToSession_Parameters = root.lookupType("rline.TransitionToSession_Parameters");
37 | const TransitionToSessionResult = root.lookupType("rline.TransitionToSessionResult");
38 | const scmds_Parameters = root.lookupType("rline.scmds_Parameters");
39 |
40 | function toArrayBuffer(buf) {
41 | var ab = new ArrayBuffer(buf.length);
42 | var view = new Uint8Array(ab);
43 | for (var i = 0; i < buf.length; ++i) {
44 | view[i] = buf[i];
45 | }
46 | return ab;
47 | }
48 |
49 | function emitMsg(target, data) {
50 | emitNet('__cfx_internal:pbRlScSession', target, toArrayBuffer(data));
51 | }
52 |
53 | function emitSessionCmds(target, cmd, cmdname, msg) {
54 | const stuff = {};
55 | stuff[cmdname] = msg;
56 |
57 | emitMsg(target, RpcMessage.encode({
58 | Header: {
59 | MethodName: 'scmds'
60 | },
61 | Content: scmds_Parameters.encode({
62 | sid: {
63 | value: {
64 | a: 2,
65 | b: 2
66 | }
67 | },
68 | ncmds: 1,
69 | cmds: [
70 | {
71 | cmd,
72 | cmdname,
73 | ...stuff
74 | }
75 | ]
76 | }).finish()
77 | }).finish());
78 | }
79 |
80 | function emitAddPlayer(target, msg) {
81 | emitSessionCmds(target, 2, 'AddPlayer', msg);
82 | }
83 |
84 | function emitRemovePlayer(target, msg) {
85 | emitSessionCmds(target, 3, 'RemovePlayer', msg);
86 | }
87 |
88 | function emitHostChanged(target, msg) {
89 | emitSessionCmds(target, 5, 'HostChanged', msg);
90 | }
91 |
92 | onNet('playerDropped', () => {
93 | if (isOneSync) {
94 | return;
95 | }
96 |
97 | try {
98 | const oData = playerDatas[source];
99 | delete playerDatas[source];
100 |
101 | if (oData && hostIndex === oData.slot) {
102 | const pda = Object.entries(playerDatas);
103 |
104 | if (pda.length > 0) {
105 | hostIndex = pda[0][1].slot | 0; // TODO: actually use <=31 slot index *and* check for id
106 |
107 | for (const [ id, data ] of Object.entries(playerDatas)) {
108 | emitHostChanged(id, {
109 | index: hostIndex
110 | });
111 | }
112 | } else {
113 | hostIndex = -1;
114 | }
115 | }
116 |
117 | if (!oData) {
118 | return;
119 | }
120 |
121 | if (oData.slot > -1) {
122 | slotsUsed &= ~(1 << oData.slot);
123 | }
124 |
125 | for (const [ id, data ] of Object.entries(playerDatas)) {
126 | emitRemovePlayer(id, {
127 | id: oData.id
128 | });
129 | }
130 | } catch (e) {
131 | console.log(e);
132 | console.log(e.stack);
133 | }
134 | });
135 |
136 | function makeResponse(type, data) {
137 | return {
138 | Header: {
139 | },
140 | Container: {
141 | Content: type.encode(data).finish()
142 | }
143 | };
144 | }
145 |
146 | const handlers = {
147 | async InitSession(source, data) {
148 | return makeResponse(InitSessionResponse, {
149 | sesid: Buffer.alloc(16),
150 | /*token: {
151 | tkn: 'ACSTOKEN token="meow",signature="meow"'
152 | }*/
153 | });
154 | },
155 |
156 | async InitPlayer2(source, data) {
157 | const req = InitPlayer2_Parameters.decode(data);
158 |
159 | if (!isOneSync) {
160 | playerDatas[source] = {
161 | gh: req.gh,
162 | peerAddress: req.peerAddress,
163 | discriminator: req.discriminator,
164 | slot: -1
165 | };
166 | }
167 |
168 | return makeResponse(InitPlayerResult, {
169 | code: 0
170 | });
171 | },
172 |
173 | async GetRestrictions(source, data) {
174 | return makeResponse(GetRestrictionsResult, {
175 | data: {
176 |
177 | }
178 | });
179 | },
180 |
181 | async ConfirmSessionEntered(source, data) {
182 | return {};
183 | },
184 |
185 | async TransitionToSession(source, data) {
186 | const req = TransitionToSession_Parameters.decode(data);
187 |
188 | return makeResponse(TransitionToSessionResult, {
189 | code: 1 // in this message, 1 is success
190 | });
191 | },
192 |
193 | async QueueForSession_Seamless(source, data) {
194 | const req = QueueForSession_Seamless_Parameters.decode(data);
195 |
196 | if (!isOneSync) {
197 | playerDatas[source].req = req.requestId;
198 | playerDatas[source].id = req.requestId.requestor;
199 | playerDatas[source].slot = assignSlotId();
200 | }
201 |
202 | setTimeout(() => {
203 | emitMsg(source, RpcMessage.encode({
204 | Header: {
205 | MethodName: 'QueueEntered'
206 | },
207 | Content: QueueEntered_Parameters.encode({
208 | queueGroup: 69,
209 | requestId: req.requestId,
210 | optionFlags: req.optionFlags
211 | }).finish()
212 | }).finish());
213 |
214 | if (isOneSync) {
215 | hostIndex = 16
216 | } else if (hostIndex === -1) {
217 | hostIndex = playerDatas[source].slot | 0;
218 | }
219 |
220 | emitMsg(source, RpcMessage.encode({
221 | Header: {
222 | MethodName: 'TransitionReady_PlayerQueue'
223 | },
224 | Content: TransitionReady_PlayerQueue_Parameters.encode({
225 | serverUri: {
226 | url: ''
227 | },
228 | requestId: req.requestId,
229 | id: {
230 | value: {
231 | a: 2,
232 | b: 0
233 | }
234 | },
235 | serverSandbox: 0xD656C677,
236 | sessionType: 3,
237 | transferId: {
238 | value: {
239 | a: 2,
240 | b: 2
241 | }
242 | },
243 | }).finish()
244 | }).finish());
245 |
246 | setTimeout(() => {
247 | emitSessionCmds(source, 0, 'EnterSession', {
248 | index: (isOneSync) ? 16 : playerDatas[source].slot | 0,
249 | hindex: hostIndex,
250 | sessionFlags: 0,
251 | mode: 0,
252 | size: (isOneSync) ? 0 : Object.entries(playerDatas).filter(a => a[1].id).length,
253 | //size: 2,
254 | //size: Object.entries(playerDatas).length,
255 | teamIndex: 0,
256 | transitionId: {
257 | value: {
258 | a: 2,
259 | b: 0
260 | }
261 | },
262 | sessionManagerType: 0,
263 | slotCount: 32
264 | });
265 | }, 50);
266 |
267 | if (!isOneSync) {
268 | setTimeout(() => {
269 | // tell player about everyone, and everyone about player
270 | const meData = playerDatas[source];
271 |
272 | const aboutMe = {
273 | id: meData.id,
274 | gh: meData.gh,
275 | addr: meData.peerAddress,
276 | index: playerDatas[source].slot | 0
277 | };
278 |
279 | for (const [ id, data ] of Object.entries(playerDatas)) {
280 | if (id == source || !data.id) continue;
281 |
282 | emitAddPlayer(source, {
283 | id: data.id,
284 | gh: data.gh,
285 | addr: data.peerAddress,
286 | index: data.slot | 0
287 | });
288 |
289 | emitAddPlayer(id, aboutMe);
290 | }
291 | }, 150);
292 | }
293 | }, 250);
294 |
295 | return makeResponse(QueueForSessionResult, {
296 | code: 1
297 | });
298 | },
299 | };
300 |
301 | async function handleMessage(source, method, data) {
302 | if (handlers[method]) {
303 | return await handlers[method](source, data);
304 | }
305 |
306 | return {};
307 | }
308 |
309 | onNet('__cfx_internal:pbRlScSession', async (data) => {
310 | const s = source;
311 |
312 | try {
313 | const message = RpcMessage.decode(new Uint8Array(data));
314 | const response = await handleMessage(s, message.Header.MethodName, message.Content);
315 |
316 | if (!response || !response.Header) {
317 | return;
318 | }
319 |
320 | response.Header.RequestId = message.Header.RequestId;
321 |
322 | emitMsg(s, RpcResponseMessage.encode(response).finish());
323 | } catch (e) {
324 | console.log(e);
325 | console.log(e.stack);
326 | }
327 | });
328 | });
--------------------------------------------------------------------------------
/resources/[managers]/mapmanager/mapmanager_server.lua:
--------------------------------------------------------------------------------
1 | -- loosely based on MTA's https://code.google.com/p/mtasa-resources/source/browse/trunk/%5Bmanagers%5D/mapmanager/mapmanager_main.lua
2 |
3 | local maps = {}
4 | local gametypes = {}
5 |
6 | local function refreshResources()
7 | local numResources = GetNumResources()
8 |
9 | for i = 0, numResources - 1 do
10 | local resource = GetResourceByFindIndex(i)
11 |
12 | if GetNumResourceMetadata(resource, 'resource_type') > 0 then
13 | local type = GetResourceMetadata(resource, 'resource_type', 0)
14 | local params = json.decode(GetResourceMetadata(resource, 'resource_type_extra', 0))
15 |
16 | local valid = false
17 |
18 | local games = GetNumResourceMetadata(resource, 'game')
19 | if games > 0 then
20 | for j = 0, games - 1 do
21 | local game = GetResourceMetadata(resource, 'game', j)
22 |
23 | if game == GetConvar('gamename', 'gta5') or game == 'common' then
24 | valid = true
25 | end
26 | end
27 | end
28 |
29 | if valid then
30 | if type == 'map' then
31 | maps[resource] = params
32 | elseif type == 'gametype' then
33 | gametypes[resource] = params
34 | end
35 | end
36 | end
37 | end
38 | end
39 |
40 | AddEventHandler('onResourceListRefresh', function()
41 | refreshResources()
42 | end)
43 |
44 | refreshResources()
45 |
46 | AddEventHandler('onResourceStarting', function(resource)
47 | local num = GetNumResourceMetadata(resource, 'map')
48 |
49 | if num then
50 | for i = 0, num-1 do
51 | local file = GetResourceMetadata(resource, 'map', i)
52 |
53 | if file then
54 | addMap(file, resource)
55 | end
56 | end
57 | end
58 |
59 | if maps[resource] then
60 | if getCurrentMap() and getCurrentMap() ~= resource then
61 | if doesMapSupportGameType(getCurrentGameType(), resource) then
62 | print("Changing map from " .. getCurrentMap() .. " to " .. resource)
63 |
64 | changeMap(resource)
65 | else
66 | -- check if there's only one possible game type for the map
67 | local map = maps[resource]
68 | local count = 0
69 | local gt
70 |
71 | for type, flag in pairs(map.gameTypes) do
72 | if flag then
73 | count = count + 1
74 | gt = type
75 | end
76 | end
77 |
78 | if count == 1 then
79 | print("Changing map from " .. getCurrentMap() .. " to " .. resource .. " (gt " .. gt .. ")")
80 |
81 | changeGameType(gt)
82 | changeMap(resource)
83 | end
84 | end
85 |
86 | CancelEvent()
87 | end
88 | elseif gametypes[resource] then
89 | if getCurrentGameType() and getCurrentGameType() ~= resource then
90 | print("Changing gametype from " .. getCurrentGameType() .. " to " .. resource)
91 |
92 | changeGameType(resource)
93 |
94 | CancelEvent()
95 | end
96 | end
97 | end)
98 |
99 | math.randomseed(GetInstanceId())
100 |
101 | local currentGameType = nil
102 | local currentMap = nil
103 |
104 | AddEventHandler('onResourceStart', function(resource)
105 | if maps[resource] then
106 | if not getCurrentGameType() then
107 | for gt, _ in pairs(maps[resource].gameTypes) do
108 | changeGameType(gt)
109 | break
110 | end
111 | end
112 |
113 | if getCurrentGameType() and not getCurrentMap() then
114 | if doesMapSupportGameType(currentGameType, resource) then
115 | if TriggerEvent('onMapStart', resource, maps[resource]) then
116 | if maps[resource].name then
117 | print('Started map ' .. maps[resource].name)
118 | SetMapName(maps[resource].name)
119 | else
120 | print('Started map ' .. resource)
121 | SetMapName(resource)
122 | end
123 |
124 | currentMap = resource
125 | else
126 | currentMap = nil
127 | end
128 | end
129 | end
130 | elseif gametypes[resource] then
131 | if not getCurrentGameType() then
132 | if TriggerEvent('onGameTypeStart', resource, gametypes[resource]) then
133 | currentGameType = resource
134 |
135 | local gtName = gametypes[resource].name or resource
136 |
137 | SetGameType(gtName)
138 |
139 | print('Started gametype ' .. gtName)
140 |
141 | SetTimeout(50, function()
142 | if not currentMap then
143 | local possibleMaps = {}
144 |
145 | for map, data in pairs(maps) do
146 | if data.gameTypes[currentGameType] then
147 | table.insert(possibleMaps, map)
148 | end
149 | end
150 |
151 | if #possibleMaps > 0 then
152 | local rnd = math.random(#possibleMaps)
153 | changeMap(possibleMaps[rnd])
154 | end
155 | end
156 | end)
157 | else
158 | currentGameType = nil
159 | end
160 | end
161 | end
162 |
163 | -- handle starting
164 | loadMap(resource)
165 | end)
166 |
167 | local function handleRoundEnd()
168 | local possibleMaps = {}
169 |
170 | for map, data in pairs(maps) do
171 | if data.gameTypes[currentGameType] then
172 | table.insert(possibleMaps, map)
173 | end
174 | end
175 |
176 | if #possibleMaps > 1 then
177 | local mapname = currentMap
178 |
179 | while mapname == currentMap do
180 | local rnd = math.random(#possibleMaps)
181 | mapname = possibleMaps[rnd]
182 | end
183 |
184 | changeMap(mapname)
185 | elseif #possibleMaps > 0 then
186 | local rnd = math.random(#possibleMaps)
187 | changeMap(possibleMaps[rnd])
188 | end
189 | end
190 |
191 | AddEventHandler('mapmanager:roundEnded', function()
192 | -- set a timeout as we don't want to return to a dead environment
193 | SetTimeout(50, handleRoundEnd) -- not a closure as to work around some issue in neolua?
194 | end)
195 |
196 | function roundEnded()
197 | SetTimeout(50, handleRoundEnd)
198 | end
199 |
200 | AddEventHandler('onResourceStop', function(resource)
201 | if resource == currentGameType then
202 | TriggerEvent('onGameTypeStop', resource)
203 |
204 | currentGameType = nil
205 |
206 | if currentMap then
207 | StopResource(currentMap)
208 | end
209 | elseif resource == currentMap then
210 | TriggerEvent('onMapStop', resource)
211 |
212 | currentMap = nil
213 | end
214 |
215 | -- unload the map
216 | unloadMap(resource)
217 | end)
218 |
219 | AddEventHandler('rconCommand', function(commandName, args)
220 | if commandName == 'map' then
221 | if #args ~= 1 then
222 | RconPrint("usage: map [mapname]\n")
223 | end
224 |
225 | if not maps[args[1]] then
226 | RconPrint('no such map ' .. args[1] .. "\n")
227 | CancelEvent()
228 |
229 | return
230 | end
231 |
232 | if currentGameType == nil or not doesMapSupportGameType(currentGameType, args[1]) then
233 | local map = maps[args[1]]
234 | local count = 0
235 | local gt
236 |
237 | for type, flag in pairs(map.gameTypes) do
238 | if flag then
239 | count = count + 1
240 | gt = type
241 | end
242 | end
243 |
244 | if count == 1 then
245 | print("Changing map from " .. getCurrentMap() .. " to " .. args[1] .. " (gt " .. gt .. ")")
246 |
247 | changeGameType(gt)
248 | changeMap(args[1])
249 |
250 | RconPrint('map ' .. args[1] .. "\n")
251 | else
252 | RconPrint('map ' .. args[1] .. ' does not support ' .. currentGameType .. "\n")
253 | end
254 |
255 | CancelEvent()
256 |
257 | return
258 | end
259 |
260 | changeMap(args[1])
261 |
262 | RconPrint('map ' .. args[1] .. "\n")
263 |
264 | CancelEvent()
265 | elseif commandName == 'gametype' then
266 | if #args ~= 1 then
267 | RconPrint("usage: gametype [name]\n")
268 | end
269 |
270 | if not gametypes[args[1]] then
271 | RconPrint('no such gametype ' .. args[1] .. "\n")
272 | CancelEvent()
273 |
274 | return
275 | end
276 |
277 | changeGameType(args[1])
278 |
279 | RconPrint('gametype ' .. args[1] .. "\n")
280 |
281 | CancelEvent()
282 | end
283 | end)
284 |
285 | function getCurrentGameType()
286 | return currentGameType
287 | end
288 |
289 | function getCurrentMap()
290 | return currentMap
291 | end
292 |
293 | function getMaps()
294 | return maps
295 | end
296 |
297 | function changeGameType(gameType)
298 | if currentMap and not doesMapSupportGameType(gameType, currentMap) then
299 | StopResource(currentMap)
300 | end
301 |
302 | if currentGameType then
303 | StopResource(currentGameType)
304 | end
305 |
306 | StartResource(gameType)
307 | end
308 |
309 | function changeMap(map)
310 | if currentMap then
311 | StopResource(currentMap)
312 | end
313 |
314 | StartResource(map)
315 | end
316 |
317 | function doesMapSupportGameType(gameType, map)
318 | if not gametypes[gameType] then
319 | return false
320 | end
321 |
322 | if not maps[map] then
323 | return false
324 | end
325 |
326 | if not maps[map].gameTypes then
327 | return true
328 | end
329 |
330 | return maps[map].gameTypes[gameType]
331 | end
332 |
--------------------------------------------------------------------------------
/resources/[gameplay]/chat/html/vendor/flexboxgrid.6.3.1.min.css:
--------------------------------------------------------------------------------
1 | .container,.container-fluid{margin-right:auto;margin-left:auto}.container-fluid{padding-right:2rem;padding-left:2rem}.row{box-sizing:border-box;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-flex:0;-ms-flex:0 1 auto;flex:0 1 auto;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-.5rem;margin-left:-.5rem}.row.reverse{-webkit-box-orient:horizontal;-webkit-box-direction:reverse;-ms-flex-direction:row-reverse;flex-direction:row-reverse}.col.reverse{-webkit-box-orient:vertical;-webkit-box-direction:reverse;-ms-flex-direction:column-reverse;flex-direction:column-reverse}.col-xs,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-offset-0,.col-xs-offset-1,.col-xs-offset-10,.col-xs-offset-11,.col-xs-offset-12,.col-xs-offset-2,.col-xs-offset-3,.col-xs-offset-4,.col-xs-offset-5,.col-xs-offset-6,.col-xs-offset-7,.col-xs-offset-8,.col-xs-offset-9{box-sizing:border-box;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;padding-right:.5rem;padding-left:.5rem}.col-xs{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-preferred-size:0;flex-basis:0;max-width:100%}.col-xs-1{-ms-flex-preferred-size:8.33333333%;flex-basis:8.33333333%;max-width:8.33333333%}.col-xs-2{-ms-flex-preferred-size:16.66666667%;flex-basis:16.66666667%;max-width:16.66666667%}.col-xs-3{-ms-flex-preferred-size:25%;flex-basis:25%;max-width:25%}.col-xs-4{-ms-flex-preferred-size:33.33333333%;flex-basis:33.33333333%;max-width:33.33333333%}.col-xs-5{-ms-flex-preferred-size:41.66666667%;flex-basis:41.66666667%;max-width:41.66666667%}.col-xs-6{-ms-flex-preferred-size:50%;flex-basis:50%;max-width:50%}.col-xs-7{-ms-flex-preferred-size:58.33333333%;flex-basis:58.33333333%;max-width:58.33333333%}.col-xs-8{-ms-flex-preferred-size:66.66666667%;flex-basis:66.66666667%;max-width:66.66666667%}.col-xs-9{-ms-flex-preferred-size:75%;flex-basis:75%;max-width:75%}.col-xs-10{-ms-flex-preferred-size:83.33333333%;flex-basis:83.33333333%;max-width:83.33333333%}.col-xs-11{-ms-flex-preferred-size:91.66666667%;flex-basis:91.66666667%;max-width:91.66666667%}.col-xs-12{-ms-flex-preferred-size:100%;flex-basis:100%;max-width:100%}.col-xs-offset-0{margin-left:0}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-11{margin-left:91.66666667%}.start-xs{-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;text-align:start}.center-xs{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;text-align:center}.end-xs{-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end;text-align:end}.top-xs{-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start}.middle-xs{-webkit-box-align:center;-ms-flex-align:center;align-items:center}.bottom-xs{-webkit-box-align:end;-ms-flex-align:end;align-items:flex-end}.around-xs{-ms-flex-pack:distribute;justify-content:space-around}.between-xs{-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.first-xs{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.last-xs{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}@media only screen and (min-width:48em){.container{width:49rem}.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-offset-0,.col-sm-offset-1,.col-sm-offset-10,.col-sm-offset-11,.col-sm-offset-12,.col-sm-offset-2,.col-sm-offset-3,.col-sm-offset-4,.col-sm-offset-5,.col-sm-offset-6,.col-sm-offset-7,.col-sm-offset-8,.col-sm-offset-9{box-sizing:border-box;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;padding-right:.5rem;padding-left:.5rem}.col-sm{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-preferred-size:0;flex-basis:0;max-width:100%}.col-sm-1{-ms-flex-preferred-size:8.33333333%;flex-basis:8.33333333%;max-width:8.33333333%}.col-sm-2{-ms-flex-preferred-size:16.66666667%;flex-basis:16.66666667%;max-width:16.66666667%}.col-sm-3{-ms-flex-preferred-size:25%;flex-basis:25%;max-width:25%}.col-sm-4{-ms-flex-preferred-size:33.33333333%;flex-basis:33.33333333%;max-width:33.33333333%}.col-sm-5{-ms-flex-preferred-size:41.66666667%;flex-basis:41.66666667%;max-width:41.66666667%}.col-sm-6{-ms-flex-preferred-size:50%;flex-basis:50%;max-width:50%}.col-sm-7{-ms-flex-preferred-size:58.33333333%;flex-basis:58.33333333%;max-width:58.33333333%}.col-sm-8{-ms-flex-preferred-size:66.66666667%;flex-basis:66.66666667%;max-width:66.66666667%}.col-sm-9{-ms-flex-preferred-size:75%;flex-basis:75%;max-width:75%}.col-sm-10{-ms-flex-preferred-size:83.33333333%;flex-basis:83.33333333%;max-width:83.33333333%}.col-sm-11{-ms-flex-preferred-size:91.66666667%;flex-basis:91.66666667%;max-width:91.66666667%}.col-sm-12{-ms-flex-preferred-size:100%;flex-basis:100%;max-width:100%}.col-sm-offset-0{margin-left:0}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-11{margin-left:91.66666667%}.start-sm{-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;text-align:start}.center-sm{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;text-align:center}.end-sm{-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end;text-align:end}.top-sm{-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start}.middle-sm{-webkit-box-align:center;-ms-flex-align:center;align-items:center}.bottom-sm{-webkit-box-align:end;-ms-flex-align:end;align-items:flex-end}.around-sm{-ms-flex-pack:distribute;justify-content:space-around}.between-sm{-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.first-sm{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.last-sm{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}}@media only screen and (min-width:64em){.container{width:65rem}.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-offset-0,.col-md-offset-1,.col-md-offset-10,.col-md-offset-11,.col-md-offset-12,.col-md-offset-2,.col-md-offset-3,.col-md-offset-4,.col-md-offset-5,.col-md-offset-6,.col-md-offset-7,.col-md-offset-8,.col-md-offset-9{box-sizing:border-box;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;padding-right:.5rem;padding-left:.5rem}.col-md{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-preferred-size:0;flex-basis:0;max-width:100%}.col-md-1{-ms-flex-preferred-size:8.33333333%;flex-basis:8.33333333%;max-width:8.33333333%}.col-md-2{-ms-flex-preferred-size:16.66666667%;flex-basis:16.66666667%;max-width:16.66666667%}.col-md-3{-ms-flex-preferred-size:25%;flex-basis:25%;max-width:25%}.col-md-4{-ms-flex-preferred-size:33.33333333%;flex-basis:33.33333333%;max-width:33.33333333%}.col-md-5{-ms-flex-preferred-size:41.66666667%;flex-basis:41.66666667%;max-width:41.66666667%}.col-md-6{-ms-flex-preferred-size:50%;flex-basis:50%;max-width:50%}.col-md-7{-ms-flex-preferred-size:58.33333333%;flex-basis:58.33333333%;max-width:58.33333333%}.col-md-8{-ms-flex-preferred-size:66.66666667%;flex-basis:66.66666667%;max-width:66.66666667%}.col-md-9{-ms-flex-preferred-size:75%;flex-basis:75%;max-width:75%}.col-md-10{-ms-flex-preferred-size:83.33333333%;flex-basis:83.33333333%;max-width:83.33333333%}.col-md-11{-ms-flex-preferred-size:91.66666667%;flex-basis:91.66666667%;max-width:91.66666667%}.col-md-12{-ms-flex-preferred-size:100%;flex-basis:100%;max-width:100%}.col-md-offset-0{margin-left:0}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-3{margin-left:25%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-6{margin-left:50%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-9{margin-left:75%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-11{margin-left:91.66666667%}.start-md{-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;text-align:start}.center-md{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;text-align:center}.end-md{-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end;text-align:end}.top-md{-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start}.middle-md{-webkit-box-align:center;-ms-flex-align:center;align-items:center}.bottom-md{-webkit-box-align:end;-ms-flex-align:end;align-items:flex-end}.around-md{-ms-flex-pack:distribute;justify-content:space-around}.between-md{-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.first-md{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.last-md{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}}@media only screen and (min-width:75em){.container{width:76rem}.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-offset-0,.col-lg-offset-1,.col-lg-offset-10,.col-lg-offset-11,.col-lg-offset-12,.col-lg-offset-2,.col-lg-offset-3,.col-lg-offset-4,.col-lg-offset-5,.col-lg-offset-6,.col-lg-offset-7,.col-lg-offset-8,.col-lg-offset-9{box-sizing:border-box;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;padding-right:.5rem;padding-left:.5rem}.col-lg{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-preferred-size:0;flex-basis:0;max-width:100%}.col-lg-1{-ms-flex-preferred-size:8.33333333%;flex-basis:8.33333333%;max-width:8.33333333%}.col-lg-2{-ms-flex-preferred-size:16.66666667%;flex-basis:16.66666667%;max-width:16.66666667%}.col-lg-3{-ms-flex-preferred-size:25%;flex-basis:25%;max-width:25%}.col-lg-4{-ms-flex-preferred-size:33.33333333%;flex-basis:33.33333333%;max-width:33.33333333%}.col-lg-5{-ms-flex-preferred-size:41.66666667%;flex-basis:41.66666667%;max-width:41.66666667%}.col-lg-6{-ms-flex-preferred-size:50%;flex-basis:50%;max-width:50%}.col-lg-7{-ms-flex-preferred-size:58.33333333%;flex-basis:58.33333333%;max-width:58.33333333%}.col-lg-8{-ms-flex-preferred-size:66.66666667%;flex-basis:66.66666667%;max-width:66.66666667%}.col-lg-9{-ms-flex-preferred-size:75%;flex-basis:75%;max-width:75%}.col-lg-10{-ms-flex-preferred-size:83.33333333%;flex-basis:83.33333333%;max-width:83.33333333%}.col-lg-11{-ms-flex-preferred-size:91.66666667%;flex-basis:91.66666667%;max-width:91.66666667%}.col-lg-12{-ms-flex-preferred-size:100%;flex-basis:100%;max-width:100%}.col-lg-offset-0{margin-left:0}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-11{margin-left:91.66666667%}.start-lg{-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;text-align:start}.center-lg{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;text-align:center}.end-lg{-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end;text-align:end}.top-lg{-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start}.middle-lg{-webkit-box-align:center;-ms-flex-align:center;align-items:center}.bottom-lg{-webkit-box-align:end;-ms-flex-align:end;align-items:flex-end}.around-lg{-ms-flex-pack:distribute;justify-content:space-around}.between-lg{-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.first-lg{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.last-lg{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}}
--------------------------------------------------------------------------------
/resources/[managers]/spawnmanager/spawnmanager.lua:
--------------------------------------------------------------------------------
1 | -- in-memory spawnpoint array for this script execution instance
2 | local spawnPoints = {}
3 |
4 | -- auto-spawn enabled flag
5 | local autoSpawnEnabled = false
6 | local autoSpawnCallback
7 |
8 | -- support for mapmanager maps
9 | AddEventHandler('getMapDirectives', function(add)
10 | -- call the remote callback
11 | add('spawnpoint', function(state, model)
12 | -- return another callback to pass coordinates and so on (as such syntax would be [spawnpoint 'model' { options/coords }])
13 | return function(opts)
14 | local x, y, z, heading
15 |
16 | local s, e = pcall(function()
17 | -- is this a map or an array?
18 | if opts.x then
19 | x = opts.x
20 | y = opts.y
21 | z = opts.z
22 | else
23 | x = opts[1]
24 | y = opts[2]
25 | z = opts[3]
26 | end
27 |
28 | x = x + 0.0001
29 | y = y + 0.0001
30 | z = z + 0.0001
31 |
32 | -- get a heading and force it to a float, or just default to null
33 | heading = opts.heading and (opts.heading + 0.01) or 0
34 |
35 | -- add the spawnpoint
36 | addSpawnPoint({
37 | x = x, y = y, z = z,
38 | heading = heading,
39 | model = model
40 | })
41 |
42 | -- recalculate the model for storage
43 | if not tonumber(model) then
44 | model = GetHashKey(model, _r)
45 | end
46 |
47 | -- store the spawn data in the state so we can erase it later on
48 | state.add('xyz', { x, y, z })
49 | state.add('model', model)
50 | end)
51 |
52 | if not s then
53 | Citizen.Trace(e .. "\n")
54 | end
55 | end
56 | -- delete callback follows on the next line
57 | end, function(state, arg)
58 | -- loop through all spawn points to find one with our state
59 | for i, sp in ipairs(spawnPoints) do
60 | -- if it matches...
61 | if sp.x == state.xyz[1] and sp.y == state.xyz[2] and sp.z == state.xyz[3] and sp.model == state.model then
62 | -- remove it.
63 | table.remove(spawnPoints, i)
64 | return
65 | end
66 | end
67 | end)
68 | end)
69 |
70 |
71 | -- loads a set of spawn points from a JSON string
72 | function loadSpawns(spawnString)
73 | -- decode the JSON string
74 | local data = json.decode(spawnString)
75 |
76 | -- do we have a 'spawns' field?
77 | if not data.spawns then
78 | error("no 'spawns' in JSON data")
79 | end
80 |
81 | -- loop through the spawns
82 | for i, spawn in ipairs(data.spawns) do
83 | -- and add it to the list (validating as we go)
84 | addSpawnPoint(spawn)
85 | end
86 | end
87 |
88 | local spawnNum = 1
89 |
90 | function addSpawnPoint(spawn)
91 | -- validate the spawn (position)
92 | if not tonumber(spawn.x) or not tonumber(spawn.y) or not tonumber(spawn.z) then
93 | error("invalid spawn position")
94 | end
95 |
96 | -- heading
97 | if not tonumber(spawn.heading) then
98 | error("invalid spawn heading")
99 | end
100 |
101 | -- model (try integer first, if not, hash it)
102 | local model = spawn.model
103 |
104 | if not tonumber(spawn.model) then
105 | model = GetHashKey(spawn.model)
106 | end
107 |
108 | -- is the model actually a model?
109 | if not IsModelInCdimage(model) then
110 | error("invalid spawn model")
111 | end
112 |
113 | -- is is even a ped?
114 | -- not in V?
115 | --[[if not IsThisModelAPed(model) then
116 | error("this model ain't a ped!")
117 | end]]
118 |
119 | -- overwrite the model in case we hashed it
120 | spawn.model = model
121 |
122 | -- add an index
123 | spawn.idx = spawnNum
124 | spawnNum = spawnNum + 1
125 |
126 | -- all OK, add the spawn entry to the list
127 | table.insert(spawnPoints, spawn)
128 |
129 | return spawn.idx
130 | end
131 |
132 | -- removes a spawn point
133 | function removeSpawnPoint(spawn)
134 | for i = 1, #spawnPoints do
135 | if spawnPoints[i].idx == spawn then
136 | table.remove(spawnPoints, i)
137 | return
138 | end
139 | end
140 | end
141 |
142 | -- changes the auto-spawn flag
143 | function setAutoSpawn(enabled)
144 | autoSpawnEnabled = enabled
145 | end
146 |
147 | -- sets a callback to execute instead of 'native' spawning when trying to auto-spawn
148 | function setAutoSpawnCallback(cb)
149 | autoSpawnCallback = cb
150 | autoSpawnEnabled = true
151 | end
152 |
153 | -- function as existing in original R* scripts
154 | local function freezePlayer(id, freeze)
155 | local player = id
156 | SetPlayerControl(player, not freeze, false)
157 |
158 | local ped = GetPlayerPed(player)
159 |
160 | if not freeze then
161 | if not IsEntityVisible(ped) then
162 | SetEntityVisible(ped, true)
163 | end
164 |
165 | if not IsPedInAnyVehicle(ped) then
166 | SetEntityCollision(ped, true)
167 | end
168 |
169 | FreezeEntityPosition(ped, false)
170 | --SetCharNeverTargetted(ped, false)
171 | SetPlayerInvincible(player, false)
172 | else
173 | if IsEntityVisible(ped) then
174 | SetEntityVisible(ped, false)
175 | end
176 |
177 | SetEntityCollision(ped, false)
178 | FreezeEntityPosition(ped, true)
179 | --SetCharNeverTargetted(ped, true)
180 | SetPlayerInvincible(player, true)
181 | --RemovePtfxFromPed(ped)
182 |
183 | if not IsPedFatallyInjured(ped) then
184 | ClearPedTasksImmediately(ped)
185 | end
186 | end
187 | end
188 |
189 | function loadScene(x, y, z)
190 | if not NewLoadSceneStart then
191 | return
192 | end
193 |
194 | NewLoadSceneStart(x, y, z, 0.0, 0.0, 0.0, 20.0, 0)
195 |
196 | while IsNewLoadSceneActive() do
197 | networkTimer = GetNetworkTimer()
198 |
199 | NetworkUpdateLoadScene()
200 | end
201 | end
202 |
203 | -- to prevent trying to spawn multiple times
204 | local spawnLock = false
205 |
206 | -- spawns the current player at a certain spawn point index (or a random one, for that matter)
207 | function spawnPlayer(spawnIdx, cb)
208 | if spawnLock then
209 | return
210 | end
211 |
212 | spawnLock = true
213 |
214 | Citizen.CreateThread(function()
215 | -- if the spawn isn't set, select a random one
216 | if not spawnIdx then
217 | spawnIdx = GetRandomIntInRange(1, #spawnPoints + 1)
218 | end
219 |
220 | -- get the spawn from the array
221 | local spawn
222 |
223 | if type(spawnIdx) == 'table' then
224 | spawn = spawnIdx
225 |
226 | -- prevent errors when passing spawn table
227 | spawn.x = spawn.x + 0.00
228 | spawn.y = spawn.y + 0.00
229 | spawn.z = spawn.z + 0.00
230 |
231 | spawn.heading = spawn.heading and (spawn.heading + 0.00) or 0
232 | else
233 | spawn = spawnPoints[spawnIdx]
234 | end
235 |
236 | if not spawn.skipFade then
237 | DoScreenFadeOut(500)
238 |
239 | while not IsScreenFadedOut() do
240 | Citizen.Wait(0)
241 | end
242 | end
243 |
244 | -- validate the index
245 | if not spawn then
246 | Citizen.Trace("tried to spawn at an invalid spawn index\n")
247 |
248 | spawnLock = false
249 |
250 | return
251 | end
252 |
253 | -- freeze the local player
254 | freezePlayer(PlayerId(), true)
255 |
256 | -- if the spawn has a model set
257 | if spawn.model then
258 | RequestModel(spawn.model)
259 |
260 | -- load the model for this spawn
261 | while not HasModelLoaded(spawn.model) do
262 | RequestModel(spawn.model)
263 |
264 | Wait(0)
265 | end
266 |
267 | -- change the player model
268 | SetPlayerModel(PlayerId(), spawn.model)
269 |
270 | -- release the player model
271 | SetModelAsNoLongerNeeded(spawn.model)
272 |
273 | -- RDR3 player model bits
274 | if N_0x283978a15512b2fe then
275 | N_0x283978a15512b2fe(PlayerPedId(), true)
276 | end
277 | end
278 |
279 | -- preload collisions for the spawnpoint
280 | RequestCollisionAtCoord(spawn.x, spawn.y, spawn.z)
281 |
282 | -- spawn the player
283 | local ped = PlayerPedId()
284 |
285 | -- V requires setting coords as well
286 | SetEntityCoordsNoOffset(ped, spawn.x, spawn.y, spawn.z, false, false, false, true)
287 |
288 | NetworkResurrectLocalPlayer(spawn.x, spawn.y, spawn.z, spawn.heading, true, true, false)
289 |
290 | -- gamelogic-style cleanup stuff
291 | ClearPedTasksImmediately(ped)
292 | --SetEntityHealth(ped, 300) -- TODO: allow configuration of this?
293 | RemoveAllPedWeapons(ped) -- TODO: make configurable (V behavior?)
294 | ClearPlayerWantedLevel(PlayerId())
295 |
296 | -- why is this even a flag?
297 | --SetCharWillFlyThroughWindscreen(ped, false)
298 |
299 | -- set primary camera heading
300 | --SetGameCamHeading(spawn.heading)
301 | --CamRestoreJumpcut(GetGameCam())
302 |
303 | -- load the scene; streaming expects us to do it
304 | --ForceLoadingScreen(true)
305 | --loadScene(spawn.x, spawn.y, spawn.z)
306 | --ForceLoadingScreen(false)
307 |
308 | local time = GetGameTimer()
309 |
310 | while (not HasCollisionLoadedAroundEntity(ped) and (GetGameTimer() - time) < 5000) do
311 | Citizen.Wait(0)
312 | end
313 |
314 | ShutdownLoadingScreen()
315 |
316 | if IsScreenFadedOut() then
317 | DoScreenFadeIn(500)
318 |
319 | while not IsScreenFadedIn() do
320 | Citizen.Wait(0)
321 | end
322 | end
323 |
324 | -- and unfreeze the player
325 | freezePlayer(PlayerId(), false)
326 |
327 | TriggerEvent('playerSpawned', spawn)
328 |
329 | if cb then
330 | cb(spawn)
331 | end
332 |
333 | spawnLock = false
334 | end)
335 | end
336 |
337 | -- automatic spawning monitor thread, too
338 | local respawnForced
339 | local diedAt
340 |
341 | Citizen.CreateThread(function()
342 | -- main loop thing
343 | while true do
344 | Citizen.Wait(50)
345 |
346 | local playerPed = PlayerPedId()
347 |
348 | if playerPed and playerPed ~= -1 then
349 | -- check if we want to autospawn
350 | if autoSpawnEnabled then
351 | if NetworkIsPlayerActive(PlayerId()) then
352 | if (diedAt and (math.abs(GetTimeDifference(GetGameTimer(), diedAt)) > 2000)) or respawnForced then
353 | if autoSpawnCallback then
354 | autoSpawnCallback()
355 | else
356 | spawnPlayer()
357 | end
358 |
359 | respawnForced = false
360 | end
361 | end
362 | end
363 |
364 | if IsEntityDead(playerPed) then
365 | if not diedAt then
366 | diedAt = GetGameTimer()
367 | end
368 | else
369 | diedAt = nil
370 | end
371 | end
372 | end
373 | end)
374 |
375 | function forceRespawn()
376 | spawnLock = false
377 | respawnForced = true
378 | end
379 |
380 | exports('spawnPlayer', spawnPlayer)
381 | exports('addSpawnPoint', addSpawnPoint)
382 | exports('removeSpawnPoint', removeSpawnPoint)
383 | exports('loadSpawns', loadSpawns)
384 | exports('setAutoSpawn', setAutoSpawn)
385 | exports('setAutoSpawnCallback', setAutoSpawnCallback)
386 | exports('forceRespawn', forceRespawn)
387 |
--------------------------------------------------------------------------------