├── 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 | 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 | 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 | 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 |
19 |
20 |
21 |
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 | --------------------------------------------------------------------------------