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