mirror of
https://github.com/citizenfx/cfx-server-data.git
synced 2026-09-21 02:39:29 +02:00
tweak: cleanup server-data
This commit is contained in:
@@ -1,2 +0,0 @@
|
||||
.yarn.installed
|
||||
node_modules/
|
||||
@@ -1,13 +0,0 @@
|
||||
-- This resource is part of the default Cfx.re asset pack (cfx-server-data)
|
||||
-- Altering or recreating for local use only is strongly discouraged.
|
||||
|
||||
version '1.0.0'
|
||||
author 'Cfx.re <[email protected]>'
|
||||
description 'Builds resources with webpack. To learn more: https://webpack.js.org'
|
||||
repository 'https://github.com/citizenfx/cfx-server-data'
|
||||
|
||||
dependency 'yarn'
|
||||
server_script 'webpack_builder.js'
|
||||
|
||||
fx_version 'adamant'
|
||||
game 'common'
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"name": "webpack-builder",
|
||||
"version": "1.0.1",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"async": "^3.1.0",
|
||||
"webpack": "^4.41.2",
|
||||
"worker-farm": "^1.7.0"
|
||||
}
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const workerFarm = require('worker-farm');
|
||||
const async = require('async');
|
||||
let buildingInProgress = false;
|
||||
let currentBuildingModule = '';
|
||||
|
||||
// some modules will not like the custom stack trace logic
|
||||
const ops = Error.prepareStackTrace;
|
||||
Error.prepareStackTrace = undefined;
|
||||
|
||||
const webpackBuildTask = {
|
||||
shouldBuild(resourceName) {
|
||||
const numMetaData = GetNumResourceMetadata(resourceName, 'webpack_config');
|
||||
|
||||
if (numMetaData > 0) {
|
||||
for (let i = 0; i < numMetaData; i++) {
|
||||
const configName = GetResourceMetadata(resourceName, 'webpack_config');
|
||||
|
||||
if (shouldBuild(configName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
function loadCache(config) {
|
||||
const cachePath = `cache/${resourceName}/${config.replace(/\//g, '_')}.json`;
|
||||
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(cachePath, {encoding: 'utf8'}));
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function shouldBuild(config) {
|
||||
const cache = loadCache(config);
|
||||
|
||||
if (!cache) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const file of cache) {
|
||||
const stats = getStat(file.name);
|
||||
|
||||
if (!stats ||
|
||||
stats.mtime !== file.stats.mtime ||
|
||||
stats.size !== file.stats.size ||
|
||||
stats.inode !== file.stats.inode) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function getStat(path) {
|
||||
try {
|
||||
const stat = fs.statSync(path);
|
||||
|
||||
return stat ? {
|
||||
mtime: stat.mtimeMs,
|
||||
size: stat.size,
|
||||
inode: stat.ino,
|
||||
} : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
build(resourceName, cb) {
|
||||
let buildWebpack = async () => {
|
||||
let error = null;
|
||||
const configs = [];
|
||||
const promises = [];
|
||||
const numMetaData = GetNumResourceMetadata(resourceName, 'webpack_config');
|
||||
|
||||
for (let i = 0; i < numMetaData; i++) {
|
||||
configs.push(GetResourceMetadata(resourceName, 'webpack_config', i));
|
||||
}
|
||||
|
||||
for (const configName of configs) {
|
||||
const configPath = GetResourcePath(resourceName) + '/' + configName;
|
||||
|
||||
const cachePath = `cache/${resourceName}/${configName.replace(/\//g, '_')}.json`;
|
||||
|
||||
try {
|
||||
fs.mkdirSync(path.dirname(cachePath));
|
||||
} catch {
|
||||
}
|
||||
|
||||
const config = require(configPath);
|
||||
|
||||
const workers = workerFarm(require.resolve('./webpack_runner'));
|
||||
|
||||
if (config) {
|
||||
const resourcePath = path.resolve(GetResourcePath(resourceName));
|
||||
|
||||
while (buildingInProgress) {
|
||||
console.log(`webpack is busy: we are waiting to compile ${resourceName} (${configName})`);
|
||||
await sleep(3000);
|
||||
}
|
||||
|
||||
console.log(`${resourceName}: started building ${configName}`);
|
||||
|
||||
buildingInProgress = true;
|
||||
currentBuildingModule = resourceName;
|
||||
|
||||
promises.push(new Promise((resolve, reject) => {
|
||||
workers({
|
||||
configPath,
|
||||
resourcePath,
|
||||
cachePath
|
||||
}, (err, outp) => {
|
||||
workerFarm.end(workers);
|
||||
|
||||
if (err) {
|
||||
console.error(err.stack || err);
|
||||
if (err.details) {
|
||||
console.error(err.details);
|
||||
}
|
||||
|
||||
buildingInProgress = false;
|
||||
currentBuildingModule = '';
|
||||
currentBuildingScript = '';
|
||||
reject("worker farm webpack errored out");
|
||||
return;
|
||||
}
|
||||
|
||||
if (outp.errors) {
|
||||
for (const error of outp.errors) {
|
||||
console.log(error);
|
||||
}
|
||||
buildingInProgress = false;
|
||||
currentBuildingModule = '';
|
||||
currentBuildingScript = '';
|
||||
reject("webpack got an error");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`${resourceName}: built ${configName}`);
|
||||
buildingInProgress = false;
|
||||
resolve();
|
||||
});
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await Promise.all(promises);
|
||||
} catch (e) {
|
||||
error = e.toString();
|
||||
}
|
||||
|
||||
buildingInProgress = false;
|
||||
currentBuildingModule = '';
|
||||
|
||||
if (error) {
|
||||
cb(false, error);
|
||||
} else cb(true);
|
||||
};
|
||||
buildWebpack().then();
|
||||
}
|
||||
};
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
RegisterResourceBuildTaskFactory('z_webpack', () => webpackBuildTask);
|
||||
@@ -1,75 +0,0 @@
|
||||
const webpack = require('webpack');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
function getStat(path) {
|
||||
try {
|
||||
const stat = fs.statSync(path);
|
||||
|
||||
return stat ? {
|
||||
mtime: stat.mtimeMs,
|
||||
size: stat.size,
|
||||
inode: stat.ino,
|
||||
} : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class SaveStatePlugin {
|
||||
constructor(inp) {
|
||||
this.cache = [];
|
||||
this.cachePath = inp.cachePath;
|
||||
}
|
||||
|
||||
apply(compiler) {
|
||||
compiler.hooks.afterCompile.tap('SaveStatePlugin', (compilation) => {
|
||||
for (const file of compilation.fileDependencies) {
|
||||
this.cache.push({
|
||||
name: file,
|
||||
stats: getStat(file)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
compiler.hooks.done.tap('SaveStatePlugin', (stats) => {
|
||||
if (stats.hasErrors()) {
|
||||
return;
|
||||
}
|
||||
|
||||
fs.writeFile(this.cachePath, JSON.stringify(this.cache), () => {
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = (inp, callback) => {
|
||||
const config = require(inp.configPath);
|
||||
|
||||
config.context = inp.resourcePath;
|
||||
|
||||
if (config.output && config.output.path) {
|
||||
config.output.path = path.resolve(inp.resourcePath, config.output.path);
|
||||
}
|
||||
|
||||
if (!config.plugins) {
|
||||
config.plugins = [];
|
||||
}
|
||||
|
||||
config.plugins.push(new SaveStatePlugin(inp));
|
||||
|
||||
webpack(config, (err, stats) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (stats.hasErrors()) {
|
||||
callback(null, stats.toJson());
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, {});
|
||||
});
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,12 +0,0 @@
|
||||
-- This resource is part of the default Cfx.re asset pack (cfx-server-data)
|
||||
-- Altering or recreating for local use only is strongly discouraged.
|
||||
|
||||
version '1.0.0'
|
||||
author 'Cfx.re <[email protected]>'
|
||||
description 'Builds resources with yarn. To learn more: https://classic.yarnpkg.com'
|
||||
repository 'https://github.com/citizenfx/cfx-server-data'
|
||||
|
||||
fx_version 'adamant'
|
||||
game 'common'
|
||||
|
||||
server_script 'yarn_builder.js'
|
||||
@@ -1,81 +0,0 @@
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const child_process = require('child_process');
|
||||
let buildingInProgress = false;
|
||||
let currentBuildingModule = '';
|
||||
|
||||
const initCwd = process.cwd();
|
||||
const trimOutput = (data) => {
|
||||
return `[yarn]\t` + data.toString().replace(/\s+$/, '');
|
||||
}
|
||||
|
||||
const yarnBuildTask = {
|
||||
shouldBuild(resourceName) {
|
||||
try {
|
||||
const resourcePath = GetResourcePath(resourceName);
|
||||
|
||||
const packageJson = path.resolve(resourcePath, 'package.json');
|
||||
const yarnLock = path.resolve(resourcePath, '.yarn.installed');
|
||||
|
||||
const packageStat = fs.statSync(packageJson);
|
||||
|
||||
try {
|
||||
const yarnStat = fs.statSync(yarnLock);
|
||||
|
||||
if (packageStat.mtimeMs > yarnStat.mtimeMs) {
|
||||
return true;
|
||||
}
|
||||
} catch (e) {
|
||||
// no yarn.installed, but package.json - install time!
|
||||
return true;
|
||||
}
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
build(resourceName, cb) {
|
||||
(async () => {
|
||||
while (buildingInProgress && currentBuildingModule !== resourceName) {
|
||||
console.log(`yarn is currently busy: we are waiting to compile ${resourceName}`);
|
||||
await sleep(3000);
|
||||
}
|
||||
buildingInProgress = true;
|
||||
currentBuildingModule = resourceName;
|
||||
const proc = child_process.fork(
|
||||
require.resolve('./yarn_cli.js'),
|
||||
['install', '--ignore-scripts', '--cache-folder', path.join(initCwd, 'cache', 'yarn-cache'), '--mutex', 'file:' + path.join(initCwd, 'cache', 'yarn-mutex')],
|
||||
{
|
||||
cwd: path.resolve(GetResourcePath(resourceName)),
|
||||
stdio: 'pipe',
|
||||
});
|
||||
proc.stdout.on('data', (data) => console.log(trimOutput(data)));
|
||||
proc.stderr.on('data', (data) => console.error(trimOutput(data)));
|
||||
proc.on('exit', (code, signal) => {
|
||||
setImmediate(() => {
|
||||
if (code != 0 || signal) {
|
||||
buildingInProgress = false;
|
||||
currentBuildingModule = '';
|
||||
cb(false, 'yarn failed!');
|
||||
return;
|
||||
}
|
||||
|
||||
const resourcePath = GetResourcePath(resourceName);
|
||||
const yarnLock = path.resolve(resourcePath, '.yarn.installed');
|
||||
fs.writeFileSync(yarnLock, '');
|
||||
|
||||
buildingInProgress = false;
|
||||
currentBuildingModule = '';
|
||||
cb(true);
|
||||
});
|
||||
});
|
||||
})();
|
||||
}
|
||||
};
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
RegisterResourceBuildTaskFactory('yarn', () => yarnBuildTask);
|
||||
File diff suppressed because one or more lines are too long
@@ -1,11 +0,0 @@
|
||||
Citizen.CreateThread(function()
|
||||
while true do
|
||||
Wait(0)
|
||||
|
||||
if NetworkIsSessionStarted() then
|
||||
TriggerServerEvent('hardcap:playerActivated')
|
||||
|
||||
return
|
||||
end
|
||||
end
|
||||
end)
|
||||
@@ -1,14 +0,0 @@
|
||||
-- This resource is part of the default Cfx.re asset pack (cfx-server-data)
|
||||
-- Altering or recreating for local use only is strongly discouraged.
|
||||
|
||||
version '1.0.0'
|
||||
author 'Cfx.re <[email protected]>'
|
||||
description 'Limits the number of players to the amount set by sv_maxclients in your server.cfg.'
|
||||
repository 'https://github.com/citizenfx/cfx-server-data'
|
||||
|
||||
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.'
|
||||
@@ -1,31 +0,0 @@
|
||||
local playerCount = 0
|
||||
local list = {}
|
||||
|
||||
RegisterServerEvent('hardcap:playerActivated')
|
||||
|
||||
AddEventHandler('hardcap:playerActivated', function()
|
||||
if not list[source] then
|
||||
playerCount = playerCount + 1
|
||||
list[source] = true
|
||||
end
|
||||
end)
|
||||
|
||||
AddEventHandler('playerDropped', function()
|
||||
if list[source] then
|
||||
playerCount = playerCount - 1
|
||||
list[source] = nil
|
||||
end
|
||||
end)
|
||||
|
||||
AddEventHandler('playerConnecting', function(name, setReason)
|
||||
local cv = GetConvarInt('sv_maxclients', 32)
|
||||
|
||||
print('Connecting: ' .. name .. '^7')
|
||||
|
||||
if playerCount >= cv then
|
||||
print('Full. :(')
|
||||
|
||||
setReason('This server is full (past ' .. tostring(cv) .. ' players).')
|
||||
CancelEvent()
|
||||
end
|
||||
end)
|
||||
@@ -1,15 +0,0 @@
|
||||
-- This resource is part of the default Cfx.re asset pack (cfx-server-data)
|
||||
-- Altering or recreating for local use only is strongly discouraged.
|
||||
|
||||
version '1.0.0'
|
||||
author 'Cfx.re <[email protected]>'
|
||||
description 'Handles old-style server player management commands.'
|
||||
repository 'https://github.com/citizenfx/cfx-server-data'
|
||||
|
||||
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.'
|
||||
@@ -1,25 +0,0 @@
|
||||
RegisterNetEvent('rlUpdateNames')
|
||||
|
||||
AddEventHandler('rlUpdateNames', function()
|
||||
local names = {}
|
||||
|
||||
for i = 0, 31 do
|
||||
if NetworkIsPlayerActive(i) then
|
||||
names[GetPlayerServerId(i)] = { id = i, name = GetPlayerName(i) }
|
||||
end
|
||||
end
|
||||
|
||||
TriggerServerEvent('rlUpdateNamesResult', names)
|
||||
end)
|
||||
|
||||
Citizen.CreateThread(function()
|
||||
while true do
|
||||
Wait(0)
|
||||
|
||||
if NetworkIsSessionStarted() then
|
||||
TriggerServerEvent('rlPlayerActivated')
|
||||
|
||||
return
|
||||
end
|
||||
end
|
||||
end)
|
||||
@@ -1,84 +0,0 @@
|
||||
RconLog({ msgType = 'serverStart', hostname = 'lovely', maxplayers = 32 })
|
||||
|
||||
RegisterServerEvent('rlPlayerActivated')
|
||||
|
||||
local names = {}
|
||||
|
||||
AddEventHandler('rlPlayerActivated', function()
|
||||
RconLog({ msgType = 'playerActivated', netID = source, name = GetPlayerName(source), guid = GetPlayerIdentifiers(source)[1], ip = GetPlayerEP(source) })
|
||||
|
||||
names[source] = { name = GetPlayerName(source), id = source }
|
||||
|
||||
if GetHostId() then
|
||||
TriggerClientEvent('rlUpdateNames', GetHostId())
|
||||
end
|
||||
end)
|
||||
|
||||
RegisterServerEvent('rlUpdateNamesResult')
|
||||
|
||||
AddEventHandler('rlUpdateNamesResult', function(res)
|
||||
if source ~= tonumber(GetHostId()) then
|
||||
print('bad guy')
|
||||
return
|
||||
end
|
||||
|
||||
for id, data in pairs(res) do
|
||||
if data then
|
||||
if data.name then
|
||||
if not names[id] then
|
||||
names[id] = data
|
||||
end
|
||||
|
||||
if names[id].name ~= data.name or names[id].id ~= data.id then
|
||||
names[id] = data
|
||||
|
||||
RconLog({ msgType = 'playerRenamed', netID = id, name = data.name })
|
||||
end
|
||||
end
|
||||
else
|
||||
names[id] = nil
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
AddEventHandler('playerDropped', function()
|
||||
RconLog({ msgType = 'playerDropped', netID = source, name = GetPlayerName(source) })
|
||||
|
||||
names[source] = nil
|
||||
end)
|
||||
|
||||
AddEventHandler('chatMessage', function(netID, name, message)
|
||||
RconLog({ msgType = 'chatMessage', netID = netID, name = name, message = message, guid = GetPlayerIdentifiers(netID)[1] })
|
||||
end)
|
||||
|
||||
-- NOTE: DO NOT USE THIS METHOD FOR HANDLING COMMANDS
|
||||
-- This resource has not been updated to use newer methods such as RegisterCommand.
|
||||
AddEventHandler('rconCommand', function(commandName, args)
|
||||
if commandName == 'status' then
|
||||
for netid, data in pairs(names) do
|
||||
local guid = GetPlayerIdentifiers(netid)
|
||||
|
||||
if guid and guid[1] and data then
|
||||
local ping = GetPlayerPing(netid)
|
||||
|
||||
RconPrint(netid .. ' ' .. guid[1] .. ' ' .. data.name .. ' ' .. GetPlayerEP(netid) .. ' ' .. ping .. "\n")
|
||||
end
|
||||
end
|
||||
|
||||
CancelEvent()
|
||||
elseif commandName:lower() == 'clientkick' then
|
||||
local playerId = table.remove(args, 1)
|
||||
local msg = table.concat(args, ' ')
|
||||
|
||||
DropPlayer(playerId, msg)
|
||||
|
||||
CancelEvent()
|
||||
elseif commandName:lower() == 'tempbanclient' then
|
||||
local playerId = table.remove(args, 1)
|
||||
local msg = table.concat(args, ' ')
|
||||
|
||||
TempBanPlayer(playerId, msg)
|
||||
|
||||
CancelEvent()
|
||||
end
|
||||
end)
|
||||
@@ -19,6 +19,7 @@ shared_script 'runcode.js'
|
||||
client_script 'runcode_ui.lua'
|
||||
|
||||
ui_page 'web/nui.html'
|
||||
nui_callback_strict_mode 'false'
|
||||
files {
|
||||
'web/nui.html'
|
||||
}
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
node_modules/
|
||||
.yarn.installed
|
||||
@@ -1,17 +0,0 @@
|
||||
-- This resource is part of the default Cfx.re asset pack (cfx-server-data)
|
||||
-- Altering or recreating for local use only is strongly discouraged.
|
||||
|
||||
version '1.0.0'
|
||||
author 'Cfx.re <[email protected]>'
|
||||
description 'Handles Social Club conductor session API for RedM. Do not disable.'
|
||||
repository 'https://github.com/citizenfx/cfx-server-data'
|
||||
|
||||
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.'
|
||||
|
||||
dependencies {
|
||||
'yarn'
|
||||
}
|
||||
|
||||
server_script 'sm_server.js'
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@citizenfx/protobufjs": "6.8.8"
|
||||
}
|
||||
}
|
||||
@@ -1,192 +0,0 @@
|
||||
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;
|
||||
};
|
||||
|
||||
message UriType {
|
||||
string url = 1;
|
||||
};
|
||||
|
||||
message TransitionReady_PlayerQueue_Parameters {
|
||||
UriType serverUri = 1;
|
||||
uint32 serverSandbox = 2;
|
||||
MpTransitionIdDto id = 3;
|
||||
uint32 sessionType = 4;
|
||||
MpSessionRequestIdDto requestId = 5;
|
||||
MpSessionIdDto transferId = 6;
|
||||
};
|
||||
|
||||
message TransitionToSession_Parameters {
|
||||
MpTransitionIdDto id = 1;
|
||||
float x = 2;
|
||||
float y = 3;
|
||||
};
|
||||
|
||||
message TransitionToSessionResult {
|
||||
uint32 code = 1;
|
||||
};
|
||||
@@ -1,328 +0,0 @@
|
||||
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;
|
||||
const isOneSync = GetConvar("onesync", "off") !== "off";
|
||||
|
||||
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 TransitionReady_PlayerQueue_Parameters = root.lookupType("rline.TransitionReady_PlayerQueue_Parameters");
|
||||
const TransitionToSession_Parameters = root.lookupType("rline.TransitionToSession_Parameters");
|
||||
const TransitionToSessionResult = root.lookupType("rline.TransitionToSessionResult");
|
||||
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', () => {
|
||||
if (isOneSync) {
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (!isOneSync) {
|
||||
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 TransitionToSession(source, data) {
|
||||
const req = TransitionToSession_Parameters.decode(data);
|
||||
|
||||
return makeResponse(TransitionToSessionResult, {
|
||||
code: 1 // in this message, 1 is success
|
||||
});
|
||||
},
|
||||
|
||||
async QueueForSession_Seamless(source, data) {
|
||||
const req = QueueForSession_Seamless_Parameters.decode(data);
|
||||
|
||||
if (!isOneSync) {
|
||||
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 (isOneSync) {
|
||||
hostIndex = 16
|
||||
} else if (hostIndex === -1) {
|
||||
hostIndex = playerDatas[source].slot | 0;
|
||||
}
|
||||
|
||||
emitMsg(source, RpcMessage.encode({
|
||||
Header: {
|
||||
MethodName: 'TransitionReady_PlayerQueue'
|
||||
},
|
||||
Content: TransitionReady_PlayerQueue_Parameters.encode({
|
||||
serverUri: {
|
||||
url: ''
|
||||
},
|
||||
requestId: req.requestId,
|
||||
id: {
|
||||
value: {
|
||||
a: 2,
|
||||
b: 0
|
||||
}
|
||||
},
|
||||
serverSandbox: 0xD656C677,
|
||||
sessionType: 3,
|
||||
transferId: {
|
||||
value: {
|
||||
a: 2,
|
||||
b: 2
|
||||
}
|
||||
},
|
||||
}).finish()
|
||||
}).finish());
|
||||
|
||||
setTimeout(() => {
|
||||
emitSessionCmds(source, 0, 'EnterSession', {
|
||||
index: (isOneSync) ? 16 : playerDatas[source].slot | 0,
|
||||
hindex: hostIndex,
|
||||
sessionFlags: 0,
|
||||
mode: 0,
|
||||
size: (isOneSync) ? 0 : Object.entries(playerDatas).filter(a => a[1].id).length,
|
||||
//size: 2,
|
||||
//size: Object.entries(playerDatas).length,
|
||||
teamIndex: 0,
|
||||
transitionId: {
|
||||
value: {
|
||||
a: 2,
|
||||
b: 0
|
||||
}
|
||||
},
|
||||
sessionManagerType: 0,
|
||||
slotCount: 32
|
||||
});
|
||||
}, 50);
|
||||
|
||||
if (!isOneSync) {
|
||||
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);
|
||||
}
|
||||
}, 250);
|
||||
|
||||
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);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,90 +0,0 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@citizenfx/[email protected]":
|
||||
version "6.8.8"
|
||||
resolved "https://registry.yarnpkg.com/@citizenfx/protobufjs/-/protobufjs-6.8.8.tgz#d12edcada06182f3785dea1d35eebf0ebe4fd2c3"
|
||||
integrity sha512-RBJvHPWNwguEPxV+ALbCZBXAEQf2byP5KtYrYl36Mbb0+Ch7MxpOm+764S+YqYWj/A/iNSW3jXglfJ9hlxjBLA==
|
||||
dependencies:
|
||||
"@protobufjs/aspromise" "^1.1.2"
|
||||
"@protobufjs/base64" "^1.1.2"
|
||||
"@protobufjs/codegen" "^2.0.4"
|
||||
"@protobufjs/eventemitter" "^1.1.0"
|
||||
"@protobufjs/fetch" "^1.1.0"
|
||||
"@protobufjs/float" "^1.0.2"
|
||||
"@protobufjs/inquire" "^1.1.0"
|
||||
"@protobufjs/path" "^1.1.2"
|
||||
"@protobufjs/pool" "^1.1.0"
|
||||
"@protobufjs/utf8" "^1.1.0"
|
||||
"@types/long" "^4.0.0"
|
||||
"@types/node" "^10.1.0"
|
||||
long "^4.0.0"
|
||||
|
||||
"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf"
|
||||
integrity sha1-m4sMxmPWaafY9vXQiToU00jzD78=
|
||||
|
||||
"@protobufjs/base64@^1.1.2":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735"
|
||||
integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==
|
||||
|
||||
"@protobufjs/codegen@^2.0.4":
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb"
|
||||
integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==
|
||||
|
||||
"@protobufjs/eventemitter@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70"
|
||||
integrity sha1-NVy8mLr61ZePntCV85diHx0Ga3A=
|
||||
|
||||
"@protobufjs/fetch@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45"
|
||||
integrity sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=
|
||||
dependencies:
|
||||
"@protobufjs/aspromise" "^1.1.1"
|
||||
"@protobufjs/inquire" "^1.1.0"
|
||||
|
||||
"@protobufjs/float@^1.0.2":
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1"
|
||||
integrity sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=
|
||||
|
||||
"@protobufjs/inquire@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089"
|
||||
integrity sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=
|
||||
|
||||
"@protobufjs/path@^1.1.2":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d"
|
||||
integrity sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=
|
||||
|
||||
"@protobufjs/pool@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54"
|
||||
integrity sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=
|
||||
|
||||
"@protobufjs/utf8@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570"
|
||||
integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=
|
||||
|
||||
"@types/long@^4.0.0":
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9"
|
||||
integrity sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==
|
||||
|
||||
"@types/node@^10.1.0":
|
||||
version "10.17.58"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.58.tgz#10682f6016fd866725c36d22ce6bbbd029bf4545"
|
||||
integrity sha512-Dn5RBxLohjdHFj17dVVw3rtrZAeXeWg+LQfvxDIW/fdPkSiuQk7h3frKMYtsQhtIW42wkErDcy9UMVxhGW4O7w==
|
||||
|
||||
long@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28"
|
||||
integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==
|
||||
@@ -1,3 +0,0 @@
|
||||
--This empty file causes the scheduler.lua to load clientside
|
||||
--scheduler.lua when loaded inside the sessionmanager resource currently manages remote callbacks.
|
||||
--Without this, callbacks will only work server->client and not client->server.
|
||||
@@ -1,13 +0,0 @@
|
||||
-- This resource is part of the default Cfx.re asset pack (cfx-server-data)
|
||||
-- Altering or recreating for local use only is strongly discouraged.
|
||||
|
||||
version '1.0.0'
|
||||
author 'Cfx.re <[email protected]>'
|
||||
description 'Handles the "host lock" for non-OneSync servers. Do not disable.'
|
||||
repository 'https://github.com/citizenfx/cfx-server-data'
|
||||
|
||||
fx_version 'cerulean'
|
||||
games { 'gta4', 'gta5' }
|
||||
|
||||
server_script 'server/host_lock.lua'
|
||||
client_script 'client/empty.lua'
|
||||
@@ -1,69 +0,0 @@
|
||||
-- whitelist c2s events
|
||||
RegisterServerEvent('hostingSession')
|
||||
RegisterServerEvent('hostedSession')
|
||||
|
||||
-- event handler for pre-session 'acquire'
|
||||
local currentHosting
|
||||
local hostReleaseCallbacks = {}
|
||||
|
||||
-- TODO: add a timeout for the hosting lock to be held
|
||||
-- TODO: add checks for 'fraudulent' conflict cases of hosting attempts (typically whenever the host can not be reached)
|
||||
AddEventHandler('hostingSession', function()
|
||||
-- if the lock is currently held, tell the client to await further instruction
|
||||
if currentHosting then
|
||||
TriggerClientEvent('sessionHostResult', source, 'wait')
|
||||
|
||||
-- register a callback for when the lock is freed
|
||||
table.insert(hostReleaseCallbacks, function()
|
||||
TriggerClientEvent('sessionHostResult', source, 'free')
|
||||
end)
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
-- if the current host was last contacted less than a second ago
|
||||
if GetHostId() then
|
||||
if GetPlayerLastMsg(GetHostId()) < 1000 then
|
||||
TriggerClientEvent('sessionHostResult', source, 'conflict')
|
||||
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
hostReleaseCallbacks = {}
|
||||
|
||||
currentHosting = source
|
||||
|
||||
TriggerClientEvent('sessionHostResult', source, 'go')
|
||||
|
||||
-- set a timeout of 5 seconds
|
||||
SetTimeout(5000, function()
|
||||
if not currentHosting then
|
||||
return
|
||||
end
|
||||
|
||||
currentHosting = nil
|
||||
|
||||
for _, cb in ipairs(hostReleaseCallbacks) do
|
||||
cb()
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
AddEventHandler('hostedSession', function()
|
||||
-- check if the client is the original locker
|
||||
if currentHosting ~= source then
|
||||
-- TODO: drop client as they're clearly lying
|
||||
print(currentHosting, '~=', source)
|
||||
return
|
||||
end
|
||||
|
||||
-- free the host lock (call callbacks and remove the lock value)
|
||||
for _, cb in ipairs(hostReleaseCallbacks) do
|
||||
cb()
|
||||
end
|
||||
|
||||
currentHosting = nil
|
||||
end)
|
||||
|
||||
EnableEnhancedHostSupport(true)
|
||||
Reference in New Issue
Block a user