diff --git a/resources/[gamemodes]/[maps]/fivem-map-hipster/__resource.lua b/resources/[gamemodes]/[maps]/fivem-map-hipster/fxmanifest.lua similarity index 71% rename from resources/[gamemodes]/[maps]/fivem-map-hipster/__resource.lua rename to resources/[gamemodes]/[maps]/fivem-map-hipster/fxmanifest.lua index 1c6e74e..a656eb5 100644 --- a/resources/[gamemodes]/[maps]/fivem-map-hipster/__resource.lua +++ b/resources/[gamemodes]/[maps]/fivem-map-hipster/fxmanifest.lua @@ -1,3 +1,6 @@ resource_type 'map' { gameTypes = { ['basic-gamemode'] = true } } map 'map.lua' + +fx_version 'adamant' +game 'gta5' \ No newline at end of file diff --git a/resources/[gamemodes]/[maps]/fivem-map-skater/__resource.lua b/resources/[gamemodes]/[maps]/fivem-map-skater/fxmanifest.lua similarity index 71% rename from resources/[gamemodes]/[maps]/fivem-map-skater/__resource.lua rename to resources/[gamemodes]/[maps]/fivem-map-skater/fxmanifest.lua index 1c6e74e..a656eb5 100644 --- a/resources/[gamemodes]/[maps]/fivem-map-skater/__resource.lua +++ b/resources/[gamemodes]/[maps]/fivem-map-skater/fxmanifest.lua @@ -1,3 +1,6 @@ resource_type 'map' { gameTypes = { ['basic-gamemode'] = true } } map 'map.lua' + +fx_version 'adamant' +game 'gta5' \ No newline at end of file diff --git a/resources/[gamemodes]/[maps]/redm-map-one/fxmanifest.lua b/resources/[gamemodes]/[maps]/redm-map-one/fxmanifest.lua new file mode 100644 index 0000000..0ec5657 --- /dev/null +++ b/resources/[gamemodes]/[maps]/redm-map-one/fxmanifest.lua @@ -0,0 +1,8 @@ +resource_type 'map' { gameTypes = { ['basic-gamemode'] = true } } + +map 'map.lua' + +fx_version 'adamant' +game 'rdr3' + +rdr3_warning 'I acknowledge that this is a prerelease build of RedM, and I am aware my resources *will* become incompatible once RedM ships.' \ No newline at end of file diff --git a/resources/[gamemodes]/[maps]/redm-map-one/map.lua b/resources/[gamemodes]/[maps]/redm-map-one/map.lua new file mode 100644 index 0000000..d010aed --- /dev/null +++ b/resources/[gamemodes]/[maps]/redm-map-one/map.lua @@ -0,0 +1,2 @@ +spawnpoint 'player_three' { x = -262.849, y = 793.404, z = 118.087 } +spawnpoint 'player_zero' { x = -262.849, y = 793.404, z = 118.087 } diff --git a/resources/[gamemodes]/basic-gamemode/__resource.lua b/resources/[gamemodes]/basic-gamemode/__resource.lua deleted file mode 100644 index 31c5354..0000000 --- a/resources/[gamemodes]/basic-gamemode/__resource.lua +++ /dev/null @@ -1,3 +0,0 @@ -resource_type 'gametype' { name = 'Freeroam' } - -client_script 'basic_client.lua' \ No newline at end of file diff --git a/resources/[gamemodes]/basic-gamemode/fxmanifest.lua b/resources/[gamemodes]/basic-gamemode/fxmanifest.lua new file mode 100644 index 0000000..e7446a0 --- /dev/null +++ b/resources/[gamemodes]/basic-gamemode/fxmanifest.lua @@ -0,0 +1,6 @@ +resource_type 'gametype' { name = 'Freeroam' } + +client_script 'basic_client.lua' + +game 'common' +fx_version 'adamant' \ No newline at end of file diff --git a/resources/[gameplay]/chat-theme-gtao/__resource.lua b/resources/[gameplay]/chat-theme-gtao/fxmanifest.lua similarity index 83% rename from resources/[gameplay]/chat-theme-gtao/__resource.lua rename to resources/[gameplay]/chat-theme-gtao/fxmanifest.lua index a9a6938..0b42a97 100644 --- a/resources/[gameplay]/chat-theme-gtao/__resource.lua +++ b/resources/[gameplay]/chat-theme-gtao/fxmanifest.lua @@ -7,4 +7,7 @@ chat_theme 'gtao' { msgTemplates = { default = '{0}{1}' } -} \ No newline at end of file +} + +game 'common' +fx_version 'adamant' \ No newline at end of file diff --git a/resources/[gameplay]/chat-theme-gtao/style.css b/resources/[gameplay]/chat-theme-gtao/style.css index 661d2ee..c2c0fa3 100644 --- a/resources/[gameplay]/chat-theme-gtao/style.css +++ b/resources/[gameplay]/chat-theme-gtao/style.css @@ -3,10 +3,10 @@ } .chat-window { - --size: calc((((2.7vw / 1.77777) * 1.2)) * 6); + --size: calc(((2.7vh * 1.2)) * 6); position: absolute; - right: calc(1.56vw); + right: calc(2.77vh); top: calc(50% - (var(--size) / 2)); height: var(--size) !important; @@ -33,10 +33,10 @@ font-family: Font2, sans-serif; color: #fff; - font-size: calc(1.8vw / 1.77777); /* 13px in 720p, calc'd by width */ + font-size: calc(1.8vh); /* 13px in 720p, calc'd by width */ filter: url(#svgDropShadowFilter); - line-height: calc((2.7vw / 1.77777) * 1.2); + line-height: calc(2.7vh * 1.2); margin-bottom: 0; } @@ -55,7 +55,7 @@ line-height: 1; - font-size: calc(2.7vw / 1.77777); /* 13px in 720p, calc'd by width */ + font-size: calc(2.7vh); } .msg > span > span > span { @@ -69,8 +69,8 @@ .chat-input { position: absolute; - right: calc(1.56vw); - bottom: calc(1.56vw); + right: calc(2.77vh); + bottom: calc(2.77vh); background: inherit !important; @@ -86,7 +86,7 @@ .chat-input > div { background-color: rgba(0, 0, 0, .6); - padding: calc(0.15625vw / 2); + padding: calc(0.28vh / 2); } .chat-input .prefix { @@ -97,20 +97,32 @@ .chat-input > div + div { position: absolute; - bottom: calc(1.65vh + 0.15625vw + 0.15625vw + 0.15625vw + (0.15625vw / 2)); + bottom: calc(1.65vh + 0.28vh + 0.28vh + 0.28vh + (0.28vh / 2)); width: 99.6%; text-align: left; } .suggestions { - border: calc(0.15625vw / 2) solid rgba(180, 180, 180, .6); + border: calc(0.28vh / 2) solid rgba(180, 180, 180, .6); background: transparent; } textarea { background: transparent; - border: calc(0.15625vw / 2) solid rgba(180, 180, 180, .6); - padding: calc(0.15625vw / 2); - padding-left: calc(3.5% + (0.15625vw / 2)); -} \ No newline at end of file + border: calc(0.28vh / 2) solid rgba(180, 180, 180, .6); + padding: calc(0.28vh / 2); + padding-left: calc(3.5% + (0.28vh / 2)); +} + +@media screen and (min-aspect-ratio: 21/9) { + .chat-window, .chat-input { + right: calc(12.8vw); + } +} + +@media screen and (min-aspect-ratio: 32/9) { + .chat-window, .chat-input { + right: calc(25vw); + } +} diff --git a/resources/[gameplay]/chat/cl_chat.lua b/resources/[gameplay]/chat/cl_chat.lua index 1a1eece..f42d441 100644 --- a/resources/[gameplay]/chat/cl_chat.lua +++ b/resources/[gameplay]/chat/cl_chat.lua @@ -1,3 +1,5 @@ +local isRDR = not TerraingridActivate and true or false + local chatInputActive = false local chatInputActivating = false local chatHidden = true @@ -194,7 +196,7 @@ Citizen.CreateThread(function() Wait(0) if not chatInputActive then - if IsControlPressed(0, 245) --[[ INPUT_MP_TEXT_CHAT_ALL ]] then + if IsControlPressed(0, isRDR and `INPUT_MP_TEXT_CHAT_ALL` or 245) --[[ INPUT_MP_TEXT_CHAT_ALL ]] then chatInputActive = true chatInputActivating = true @@ -205,7 +207,7 @@ Citizen.CreateThread(function() end if chatInputActivating then - if not IsControlPressed(0, 245) then + if not IsControlPressed(0, isRDR and `INPUT_MP_TEXT_CHAT_ALL` or 245) then SetNuiFocus(true) chatInputActivating = false diff --git a/resources/[gameplay]/chat/__resource.lua b/resources/[gameplay]/chat/fxmanifest.lua similarity index 78% rename from resources/[gameplay]/chat/__resource.lua rename to resources/[gameplay]/chat/fxmanifest.lua index b617599..14c61bd 100644 --- a/resources/[gameplay]/chat/__resource.lua +++ b/resources/[gameplay]/chat/fxmanifest.lua @@ -1,26 +1,30 @@ -description 'chat management stuff' - -ui_page 'html/index.html' - -client_script 'cl_chat.lua' -server_script 'sv_chat.lua' - -files { - 'html/index.html', - 'html/index.css', - 'html/config.default.js', - 'html/config.js', - 'html/App.js', - 'html/Message.js', - 'html/Suggestions.js', - 'html/vendor/vue.2.3.3.min.js', - 'html/vendor/flexboxgrid.6.3.1.min.css', - 'html/vendor/animate.3.5.2.min.css', - 'html/vendor/latofonts.css', - 'html/vendor/fonts/LatoRegular.woff2', - 'html/vendor/fonts/LatoRegular2.woff2', - 'html/vendor/fonts/LatoLight2.woff2', - 'html/vendor/fonts/LatoLight.woff2', - 'html/vendor/fonts/LatoBold.woff2', - 'html/vendor/fonts/LatoBold2.woff2', - } +description 'chat management stuff' + +ui_page 'html/index.html' + +client_script 'cl_chat.lua' +server_script 'sv_chat.lua' + +files { + 'html/index.html', + 'html/index.css', + 'html/config.default.js', + 'html/config.js', + 'html/App.js', + 'html/Message.js', + 'html/Suggestions.js', + 'html/vendor/vue.2.3.3.min.js', + 'html/vendor/flexboxgrid.6.3.1.min.css', + 'html/vendor/animate.3.5.2.min.css', + 'html/vendor/latofonts.css', + 'html/vendor/fonts/LatoRegular.woff2', + 'html/vendor/fonts/LatoRegular2.woff2', + 'html/vendor/fonts/LatoLight2.woff2', + 'html/vendor/fonts/LatoLight.woff2', + 'html/vendor/fonts/LatoBold.woff2', + 'html/vendor/fonts/LatoBold2.woff2', + } + +fx_version 'adamant' +games { 'rdr3', 'gta5' } +rdr3_warning 'I acknowledge that this is a prerelease build of RedM, and I am aware my resources *will* become incompatible once RedM ships.' \ No newline at end of file diff --git a/resources/[gameplay]/playernames/__resource.lua b/resources/[gameplay]/playernames/fxmanifest.lua similarity index 88% rename from resources/[gameplay]/playernames/__resource.lua rename to resources/[gameplay]/playernames/fxmanifest.lua index 2714468..5efabe2 100644 --- a/resources/[gameplay]/playernames/__resource.lua +++ b/resources/[gameplay]/playernames/fxmanifest.lua @@ -24,4 +24,5 @@ files { } -- support the latest resource manifest -resource_manifest_version '05cfa83c-a124-4cfa-a768-c24a5811d8f9' \ No newline at end of file +fx_version 'adamant' +game 'gta5' \ No newline at end of file diff --git a/resources/[managers]/mapmanager/__resource.lua b/resources/[managers]/mapmanager/fxmanifest.lua similarity index 61% rename from resources/[managers]/mapmanager/__resource.lua rename to resources/[managers]/mapmanager/fxmanifest.lua index 922c93c..8b45592 100644 --- a/resources/[managers]/mapmanager/__resource.lua +++ b/resources/[managers]/mapmanager/fxmanifest.lua @@ -8,7 +8,8 @@ server_scripts { "mapmanager_server.lua" } -resource_manifest_version "77731fab-63ca-442c-a67b-abc70f28dfa5" +fx_version 'adamant' +games { 'gta5', 'rdr3' } server_export "getCurrentGameType" server_export "getCurrentMap" @@ -16,4 +17,6 @@ server_export "changeGameType" server_export "changeMap" server_export "doesMapSupportGameType" server_export "getMaps" -server_export "roundEnded" \ No newline at end of file +server_export "roundEnded" + +rdr3_warning 'I acknowledge that this is a prerelease build of RedM, and I am aware my resources *will* become incompatible once RedM ships.' \ No newline at end of file diff --git a/resources/[managers]/mapmanager/mapmanager_client.lua b/resources/[managers]/mapmanager/mapmanager_client.lua index 321a114..510c24e 100644 --- a/resources/[managers]/mapmanager/mapmanager_client.lua +++ b/resources/[managers]/mapmanager/mapmanager_client.lua @@ -62,6 +62,10 @@ AddEventHandler('onResourceStop', function(res) end) AddEventHandler('getMapDirectives', function(add) + if not CreateScriptVehicleGenerator then + return + end + add('vehicle_generator', function(state, name) return function(opts) local x, y, z, heading diff --git a/resources/[managers]/spawnmanager/__resource.lua b/resources/[managers]/spawnmanager/fxmanifest.lua similarity index 54% rename from resources/[managers]/spawnmanager/__resource.lua rename to resources/[managers]/spawnmanager/fxmanifest.lua index 71eccd4..f1c0be9 100644 --- a/resources/[managers]/spawnmanager/__resource.lua +++ b/resources/[managers]/spawnmanager/fxmanifest.lua @@ -8,3 +8,8 @@ export 'loadSpawns' export 'setAutoSpawn' export 'setAutoSpawnCallback' export 'forceRespawn' + +fx_version 'adamant' +games { 'rdr3', 'gta5' } + +rdr3_warning 'I acknowledge that this is a prerelease build of RedM, and I am aware my resources *will* become incompatible once RedM ships.' \ No newline at end of file diff --git a/resources/[managers]/spawnmanager/spawnmanager.lua b/resources/[managers]/spawnmanager/spawnmanager.lua index 1aa46f1..dc6195c 100644 --- a/resources/[managers]/spawnmanager/spawnmanager.lua +++ b/resources/[managers]/spawnmanager/spawnmanager.lua @@ -187,6 +187,10 @@ local function freezePlayer(id, freeze) end function loadScene(x, y, z) + if not NewLoadSceneStart then + return + end + NewLoadSceneStart(x, y, z, 0.0, 0.0, 0.0, 20.0, 0) while IsNewLoadSceneActive() do @@ -258,14 +262,18 @@ function spawnPlayer(spawnIdx, cb) -- release the player model SetModelAsNoLongerNeeded(spawn.model) + + -- RDR3 player model bits + if N_0x283978a15512b2fe then + N_0x283978a15512b2fe(PlayerPedId(), true) + end end -- preload collisions for the spawnpoint RequestCollisionAtCoord(spawn.x, spawn.y, spawn.z) -- spawn the player - --ResurrectNetworkPlayer(GetPlayerId(), spawn.x, spawn.y, spawn.z, spawn.heading) - local ped = GetPlayerPed(-1) + local ped = PlayerPedId() -- V requires setting coords as well SetEntityCoordsNoOffset(ped, spawn.x, spawn.y, spawn.z, false, false, false, true) @@ -328,7 +336,7 @@ Citizen.CreateThread(function() while true do Citizen.Wait(50) - local playerPed = GetPlayerPed(-1) + local playerPed = PlayerPedId() if playerPed and playerPed ~= -1 then -- check if we want to autospawn diff --git a/resources/[system]/[builders]/webpack/__resource.lua b/resources/[system]/[builders]/webpack/__resource.lua deleted file mode 100644 index 6c9e3d9..0000000 --- a/resources/[system]/[builders]/webpack/__resource.lua +++ /dev/null @@ -1,3 +0,0 @@ -dependency 'yarn' ---server_only 'yes' -server_script 'webpack_builder.js' \ No newline at end of file diff --git a/resources/[system]/[builders]/webpack/fxmanifest.lua b/resources/[system]/[builders]/webpack/fxmanifest.lua new file mode 100644 index 0000000..35a210c --- /dev/null +++ b/resources/[system]/[builders]/webpack/fxmanifest.lua @@ -0,0 +1,5 @@ +dependency 'yarn' +server_script 'webpack_builder.js' + +fx_version 'adamant' +game 'common' \ No newline at end of file diff --git a/resources/[system]/[builders]/yarn/__resource.lua b/resources/[system]/[builders]/yarn/__resource.lua deleted file mode 100644 index a0082e6..0000000 --- a/resources/[system]/[builders]/yarn/__resource.lua +++ /dev/null @@ -1,2 +0,0 @@ ---server_only 'yes' -server_script 'yarn_builder.js' \ No newline at end of file diff --git a/resources/[system]/[builders]/yarn/fxmanifest.lua b/resources/[system]/[builders]/yarn/fxmanifest.lua new file mode 100644 index 0000000..ec61b19 --- /dev/null +++ b/resources/[system]/[builders]/yarn/fxmanifest.lua @@ -0,0 +1,4 @@ +fx_version 'adamant' +game 'common' + +server_script 'yarn_builder.js' \ No newline at end of file diff --git a/resources/[system]/[builders]/yarn/yarn_builder.js b/resources/[system]/[builders]/yarn/yarn_builder.js index 84c9fc0..1013783 100644 --- a/resources/[system]/[builders]/yarn/yarn_builder.js +++ b/resources/[system]/[builders]/yarn/yarn_builder.js @@ -41,7 +41,7 @@ const yarnBuildTask = { currentBuildingModule = resourceName; const process = child_process.fork( require.resolve('./yarn_cli.js'), - ['install'], + ['install', '--ignore-scripts'], { cwd: path.resolve(GetResourcePath(resourceName)) }); diff --git a/resources/[system]/hardcap/__resource.lua b/resources/[system]/hardcap/__resource.lua deleted file mode 100644 index 7ad9de5..0000000 --- a/resources/[system]/hardcap/__resource.lua +++ /dev/null @@ -1,2 +0,0 @@ -client_script 'client.lua' -server_script 'server.lua' diff --git a/resources/[system]/hardcap/fxmanifest.lua b/resources/[system]/hardcap/fxmanifest.lua new file mode 100644 index 0000000..912b34a --- /dev/null +++ b/resources/[system]/hardcap/fxmanifest.lua @@ -0,0 +1,6 @@ +client_script 'client.lua' +server_script 'server.lua' + +fx_version 'adamant' +games { 'gta5', 'rdr3' } +rdr3_warning 'I acknowledge that this is a prerelease build of RedM, and I am aware my resources *will* become incompatible once RedM ships.' \ No newline at end of file diff --git a/resources/[system]/rconlog/__resource.lua b/resources/[system]/rconlog/__resource.lua deleted file mode 100644 index b849ec2..0000000 --- a/resources/[system]/rconlog/__resource.lua +++ /dev/null @@ -1,2 +0,0 @@ -client_script 'rconlog_client.lua' -server_script 'rconlog_server.lua' diff --git a/resources/[system]/rconlog/fxmanifest.lua b/resources/[system]/rconlog/fxmanifest.lua new file mode 100644 index 0000000..79df6db --- /dev/null +++ b/resources/[system]/rconlog/fxmanifest.lua @@ -0,0 +1,7 @@ +client_script 'rconlog_client.lua' +server_script 'rconlog_server.lua' + +fx_version 'adamant' +games { 'gta5', 'rdr3' } + +rdr3_warning 'I acknowledge that this is a prerelease build of RedM, and I am aware my resources *will* become incompatible once RedM ships.' diff --git a/resources/[system]/scoreboard/__resource.lua b/resources/[system]/scoreboard/fxmanifest.lua similarity index 91% rename from resources/[system]/scoreboard/__resource.lua rename to resources/[system]/scoreboard/fxmanifest.lua index e614e8c..aca4ad9 100644 --- a/resources/[system]/scoreboard/__resource.lua +++ b/resources/[system]/scoreboard/fxmanifest.lua @@ -15,4 +15,7 @@ files { 'html/res/futurastd-medium.woff', 'html/res/futurastd-medium.ttf', 'html/res/futurastd-medium.svg', -} \ No newline at end of file +} + +fx_version 'adamant' +game 'gta5' \ No newline at end of file diff --git a/resources/[system]/scoreboard/scoreboard.lua b/resources/[system]/scoreboard/scoreboard.lua index bbed5e7..4d0efdb 100644 --- a/resources/[system]/scoreboard/scoreboard.lua +++ b/resources/[system]/scoreboard/scoreboard.lua @@ -8,7 +8,7 @@ Citizen.CreateThread(function() if IsControlPressed(0, 27)--[[ INPUT_PHONE ]] then if not listOn then local players = {} - ptable = GetPlayers() + local ptable = GetActivePlayers() for _, i in ipairs(ptable) do local wantedLevel = GetPlayerWantedLevel(i) r, g, b = GetPlayerRgbColour(i) @@ -35,18 +35,6 @@ Citizen.CreateThread(function() end end) -function GetPlayers() - local players = {} - - for i = 0, 31 do - if NetworkIsPlayerActive(i) then - table.insert(players, i) - end - end - - return players -end - function sanitize(txt) local replacements = { ['&' ] = '&', diff --git a/resources/[system]/sessionmanager-rdr3/.gitignore b/resources/[system]/sessionmanager-rdr3/.gitignore new file mode 100644 index 0000000..23d67fc --- /dev/null +++ b/resources/[system]/sessionmanager-rdr3/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +yarn.lock diff --git a/resources/[system]/sessionmanager-rdr3/fxmanifest.lua b/resources/[system]/sessionmanager-rdr3/fxmanifest.lua new file mode 100644 index 0000000..d6e7fe1 --- /dev/null +++ b/resources/[system]/sessionmanager-rdr3/fxmanifest.lua @@ -0,0 +1,8 @@ +fx_version 'adamant' +game 'common' + +dependencies { + 'yarn' +} + +server_script 'sm_server.js' \ No newline at end of file diff --git a/resources/[system]/sessionmanager-rdr3/package.json b/resources/[system]/sessionmanager-rdr3/package.json new file mode 100644 index 0000000..e00b6dc --- /dev/null +++ b/resources/[system]/sessionmanager-rdr3/package.json @@ -0,0 +1,6 @@ +{ + "private": true, + "dependencies": { + "@citizenfx/protobufjs": "6.8.8" + } +} \ No newline at end of file diff --git a/resources/[system]/sessionmanager-rdr3/rline.proto b/resources/[system]/sessionmanager-rdr3/rline.proto new file mode 100644 index 0000000..f5b9844 --- /dev/null +++ b/resources/[system]/sessionmanager-rdr3/rline.proto @@ -0,0 +1,169 @@ +syntax = "proto3"; +package rline; + +message RpcErrorData { + string ErrorCodeString = 1; + int32 ErrorCode = 2; + string DomainString = 3; + int32 DomainCode = 4; + bytes DataEx = 5; +}; + +message RpcError { + int32 ErrorCode = 1; + string ErrorMessage = 2; + RpcErrorData Data = 3; +}; + +message RpcHeader { + string RequestId = 1; + string MethodName = 2; + RpcError Error = 3; + string srcTid = 4; +}; + +message RpcMessage { + RpcHeader Header = 1; + bytes Content = 2; +}; + +message RpcResponseContainer { + bytes Content = 1; +}; + +message RpcResponseMessage { + RpcHeader Header = 1; + RpcResponseContainer Container = 2; +}; + +message TokenStuff { + string tkn = 1; +}; + +message InitSessionResponse { + bytes sesid = 1; + TokenStuff token = 2; +}; + +message MpGamerHandleDto { + string gh = 1; +}; + +message MpPeerAddressDto { + string addr = 1; +}; + +message InitPlayer2_Parameters { + MpGamerHandleDto gh = 1; + MpPeerAddressDto peerAddress = 2; + int32 discriminator = 3; + int32 seamlessType = 4; + uint32 connectionReason = 5; +}; + +message InitPlayerResult { + uint32 code = 1; +}; + +message Restriction { + int32 u1 = 1; + int32 u2 = 2; + int32 u3 = 3; +} + +message GetRestrictionsData { + repeated Restriction restriction = 1; + repeated string unk2 = 2; +}; + +message GetRestrictionsResult { + GetRestrictionsData data = 1; +}; + +message PlayerIdSto { + int32 acctId = 1; + int32 platId = 2; +}; + +message MpSessionRequestIdDto { + PlayerIdSto requestor = 1; + int32 index = 2; + int32 hash = 3; +}; + +message QueueForSession_Seamless_Parameters { + MpSessionRequestIdDto requestId = 1; + uint32 optionFlags = 2; + int32 x = 3; + int32 y = 4; +}; + +message QueueForSessionResult { + uint32 code = 1; +}; + +message QueueEntered_Parameters { + uint32 queueGroup = 1; + MpSessionRequestIdDto requestId = 2; + uint32 optionFlags = 3; +}; + +message GuidDto { + fixed64 a = 1; + fixed64 b = 2; +}; + +message MpTransitionIdDto { + GuidDto value = 1; +}; + +message MpSessionIdDto { + GuidDto value = 1; +}; + +message SessionSubcommandEnterSession { + int32 index = 1; + int32 hindex = 2; + uint32 sessionFlags = 3; + uint32 mode = 4; + int32 size = 5; + int32 teamIndex = 6; + MpTransitionIdDto transitionId = 7; + uint32 sessionManagerType = 8; + int32 slotCount = 9; +}; + +message SessionSubcommandLeaveSession { + uint32 reason = 1; +}; + +message SessionSubcommandAddPlayer { + PlayerIdSto id = 1; + MpGamerHandleDto gh = 2; + MpPeerAddressDto addr = 3; + int32 index = 4; +}; + +message SessionSubcommandRemovePlayer { + PlayerIdSto id = 1; +}; + +message SessionSubcommandHostChanged { + int32 index = 1; +}; + +message SessionCommand { + uint32 cmd = 1; + string cmdname = 2; + SessionSubcommandEnterSession EnterSession = 3; + SessionSubcommandLeaveSession LeaveSession = 4; + SessionSubcommandAddPlayer AddPlayer = 5; + SessionSubcommandRemovePlayer RemovePlayer = 6; + SessionSubcommandHostChanged HostChanged = 7; +}; + +message scmds_Parameters { + MpSessionIdDto sid = 1; + int32 ncmds = 2; + repeated SessionCommand cmds = 3; +}; \ No newline at end of file diff --git a/resources/[system]/sessionmanager-rdr3/sm_server.js b/resources/[system]/sessionmanager-rdr3/sm_server.js new file mode 100644 index 0000000..ded45fd --- /dev/null +++ b/resources/[system]/sessionmanager-rdr3/sm_server.js @@ -0,0 +1,276 @@ +const protobuf = require("@citizenfx/protobufjs"); + +const playerDatas = {}; +let slotsUsed = 0; + +function assignSlotId() { + for (let i = 0; i < 32; i++) { + if (!(slotsUsed & (1 << i))) { + slotsUsed |= (1 << i); + return i; + } + } + + return -1; +} + +let hostIndex = -1; + +protobuf.load(GetResourcePath(GetCurrentResourceName()) + "/rline.proto", function(err, root) { + if (err) { + console.log(err); + return; + } + + const RpcMessage = root.lookupType("rline.RpcMessage"); + const RpcResponseMessage = root.lookupType("rline.RpcResponseMessage"); + const InitSessionResponse = root.lookupType("rline.InitSessionResponse"); + const InitPlayer2_Parameters = root.lookupType("rline.InitPlayer2_Parameters"); + const InitPlayerResult = root.lookupType("rline.InitPlayerResult"); + const GetRestrictionsResult = root.lookupType("rline.GetRestrictionsResult"); + const QueueForSession_Seamless_Parameters = root.lookupType("rline.QueueForSession_Seamless_Parameters"); + const QueueForSessionResult = root.lookupType("rline.QueueForSessionResult"); + const QueueEntered_Parameters = root.lookupType("rline.QueueEntered_Parameters"); + const scmds_Parameters = root.lookupType("rline.scmds_Parameters"); + + function toArrayBuffer(buf) { + var ab = new ArrayBuffer(buf.length); + var view = new Uint8Array(ab); + for (var i = 0; i < buf.length; ++i) { + view[i] = buf[i]; + } + return ab; + } + + function emitMsg(target, data) { + emitNet('__cfx_internal:pbRlScSession', target, toArrayBuffer(data)); + } + + function emitSessionCmds(target, cmd, cmdname, msg) { + const stuff = {}; + stuff[cmdname] = msg; + + emitMsg(target, RpcMessage.encode({ + Header: { + MethodName: 'scmds' + }, + Content: scmds_Parameters.encode({ + sid: { + value: { + a: 2, + b: 2 + } + }, + ncmds: 1, + cmds: [ + { + cmd, + cmdname, + ...stuff + } + ] + }).finish() + }).finish()); + } + + function emitAddPlayer(target, msg) { + emitSessionCmds(target, 2, 'AddPlayer', msg); + } + + function emitRemovePlayer(target, msg) { + emitSessionCmds(target, 3, 'RemovePlayer', msg); + } + + function emitHostChanged(target, msg) { + emitSessionCmds(target, 5, 'HostChanged', msg); + } + + onNet('playerDropped', () => { + try { + const oData = playerDatas[source]; + delete playerDatas[source]; + + if (oData && hostIndex === oData.slot) { + const pda = Object.entries(playerDatas); + + if (pda.length > 0) { + hostIndex = pda[0][1].slot | 0; // TODO: actually use <=31 slot index *and* check for id + + for (const [ id, data ] of Object.entries(playerDatas)) { + emitHostChanged(id, { + index: hostIndex + }); + } + } else { + hostIndex = -1; + } + } + + if (!oData) { + return; + } + + if (oData.slot > -1) { + slotsUsed &= ~(1 << oData.slot); + } + + for (const [ id, data ] of Object.entries(playerDatas)) { + emitRemovePlayer(id, { + id: oData.id + }); + } + } catch (e) { + console.log(e); + console.log(e.stack); + } + }); + + function makeResponse(type, data) { + return { + Header: { + }, + Container: { + Content: type.encode(data).finish() + } + }; + } + + const handlers = { + async InitSession(source, data) { + return makeResponse(InitSessionResponse, { + sesid: Buffer.alloc(16), + /*token: { + tkn: 'ACSTOKEN token="meow",signature="meow"' + }*/ + }); + }, + + async InitPlayer2(source, data) { + const req = InitPlayer2_Parameters.decode(data); + + playerDatas[source] = { + gh: req.gh, + peerAddress: req.peerAddress, + discriminator: req.discriminator, + slot: -1 + }; + + return makeResponse(InitPlayerResult, { + code: 0 + }); + }, + + async GetRestrictions(source, data) { + return makeResponse(GetRestrictionsResult, { + data: { + + } + }); + }, + + async ConfirmSessionEntered(source, data) { + return {}; + }, + + async QueueForSession_Seamless(source, data) { + const req = QueueForSession_Seamless_Parameters.decode(data); + + playerDatas[source].req = req.requestId; + playerDatas[source].id = req.requestId.requestor; + playerDatas[source].slot = assignSlotId(); + + setTimeout(() => { + emitMsg(source, RpcMessage.encode({ + Header: { + MethodName: 'QueueEntered' + }, + Content: QueueEntered_Parameters.encode({ + queueGroup: 69, + requestId: req.requestId, + optionFlags: req.optionFlags + }).finish() + }).finish()); + + if (hostIndex === -1) { + hostIndex = playerDatas[source].slot | 0; + } + + emitSessionCmds(source, 0, 'EnterSession', { + index: playerDatas[source].slot | 0, + hindex: hostIndex, + sessionFlags: 0, + mode: 0, + size: Object.entries(playerDatas).filter(a => a[1].id).length, + //size: 2, + //size: Object.entries(playerDatas).length, + teamIndex: 0, + transitionId: { + value: { + a: 0,//2, + b: 0 + } + }, + sessionManagerType: 0, + slotCount: 32 + }); + + setTimeout(() => { + // tell player about everyone, and everyone about player + const meData = playerDatas[source]; + + const aboutMe = { + id: meData.id, + gh: meData.gh, + addr: meData.peerAddress, + index: playerDatas[source].slot | 0 + }; + + for (const [ id, data ] of Object.entries(playerDatas)) { + if (id == source || !data.id) continue; + + emitAddPlayer(source, { + id: data.id, + gh: data.gh, + addr: data.peerAddress, + index: data.slot | 0 + }); + + emitAddPlayer(id, aboutMe); + } + }, 150); + }, 50); + + return makeResponse(QueueForSessionResult, { + code: 1 + }); + }, + }; + + async function handleMessage(source, method, data) { + if (handlers[method]) { + return await handlers[method](source, data); + } + + return {}; + } + + onNet('__cfx_internal:pbRlScSession', async (data) => { + const s = source; + + try { + const message = RpcMessage.decode(new Uint8Array(data)); + const response = await handleMessage(s, message.Header.MethodName, message.Content); + + if (!response || !response.Header) { + return; + } + + response.Header.RequestId = message.Header.RequestId; + + emitMsg(s, RpcResponseMessage.encode(response).finish()); + } catch (e) { + console.log(e); + console.log(e.stack); + } + }); +}); \ No newline at end of file