From 207877fde0b09b8fc85fe2e073092e29a431343d Mon Sep 17 00:00:00 2001 From: Vortrex <3858226+VortrexFTW@users.noreply.github.com> Date: Fri, 20 May 2022 17:33:52 -0500 Subject: [PATCH 01/71] Remove resource init stuff from client --- scripts/client/event.js | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/scripts/client/event.js b/scripts/client/event.js index 90875da8..ce2a2038 100644 --- a/scripts/client/event.js +++ b/scripts/client/event.js @@ -59,13 +59,7 @@ function addAllEventHandlers() { function onResourceStart(event, resource) { sendResourceStartedSignalToServer(); - //setUpInitialGame(); //garbageCollectorInterval = setInterval(collectAllGarbage, 1000*60); - - resourceStarted = true; - if(resourceReady == true) { - initClient(); - } } // =========================================================================== @@ -78,10 +72,6 @@ function onResourceStop(event, resource) { function onResourceReady(event, resource) { sendResourceReadySignalToServer(); - resourceReady = true; - if(resourceStarted == true) { - initClient(); - } } // =========================================================================== From d8eeeefc8df289537ac43da22c17bf7c9a24b20e Mon Sep 17 00:00:00 2001 From: Vortrex <3858226+VortrexFTW@users.noreply.github.com> Date: Fri, 20 May 2022 17:34:15 -0500 Subject: [PATCH 02/71] Remove echo for resources and setup game on spawn --- scripts/server/event.js | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/scripts/server/event.js b/scripts/server/event.js index 22548554..b463f892 100644 --- a/scripts/server/event.js +++ b/scripts/server/event.js @@ -94,16 +94,13 @@ function onPlayerQuit(client, quitReasonId) { resetClientStuff(client); getServerData().clients[getPlayerId(client)] = null; } - - clearTemporaryVehicles(); - clearTemporaryPeds(); } // =========================================================================== -async function onPlayerChat(client, messageText) { +function onPlayerChat(client, messageText) { processPlayerChat(client, messageText); - event.preventDefault(); + return false; } // =========================================================================== @@ -177,9 +174,9 @@ function onPedExitingVehicle(ped, vehicle) { function onResourceStart(resource) { logToConsole(LOG_WARN, `[VRR.Event] Resource ${resource.name} started!`); - if(resource != thisResource) { - messageAdmins(`{MAINCOLOUR}Resource {ALTCOLOUR}${resource.name}{MAINCOLOUR} started!`); - } + //if(resource != thisResource) { + // messageAdmins(`{MAINCOLOUR}Resource {ALTCOLOUR}${resource.name}{MAINCOLOUR} started!`); + //} } // =========================================================================== @@ -187,9 +184,9 @@ function onResourceStart(resource) { function onResourceStop(resource) { logToConsole(LOG_WARN, `[VRR.Event] Resource ${resource.name} stopped!`); - if(resource != thisResource) { - messageAdmins(`{MAINCOLOUR}Resource {ALTCOLOUR}${resource.name}{MAINCOLOUR} stopped!`); - } + //if(resource != thisResource) { + // messageAdmins(`{MAINCOLOUR}Resource {ALTCOLOUR}${resource.name}{MAINCOLOUR} stopped!`); + //} if(resource == thisResource) { kickAllClients(); @@ -497,9 +494,6 @@ function onPlayerSpawn(client) { //messagePlayerNormal(client, "This server is in early development and may restart at any time for updates.", getColourByName("orange")); //messagePlayerNormal(client, "Please report any bugs using /bug and suggestions using /idea", getColourByName("yellow")); - logToConsole(LOG_DEBUG, `[VRR.Event] Updating spawned state for ${getPlayerDisplayForConsole(client)} to true`); - updatePlayerSpawnedState(client, true); - logToConsole(LOG_DEBUG, `[VRR.Event] Setting player interior for ${getPlayerDisplayForConsole(client)} to ${getPlayerCurrentSubAccount(client).interior}`); setPlayerInterior(client, getPlayerCurrentSubAccount(client).interior); @@ -603,6 +597,9 @@ function onPlayerSpawn(client) { requestPlayerPedNetworkId(client); } + logToConsole(LOG_DEBUG, `[VRR.Event] Updating spawned state for ${getPlayerDisplayForConsole(client)} to true`); + updatePlayerSpawnedState(client, true); + getPlayerData(client).payDayTickStart = sdl.ticks; messageDiscordEventChannel(`🧍 ${getPlayerName(client)} spawned as ${getCharacterFullName(client)}`); From 52f294c5d4c497a3ab4d79b916aa13caac146ca8 Mon Sep 17 00:00:00 2001 From: Vortrex <3858226+VortrexFTW@users.noreply.github.com> Date: Fri, 20 May 2022 17:34:32 -0500 Subject: [PATCH 03/71] Remove connected native testing --- .../server/native/connected-nativeclass.js | 1047 ----------------- 1 file changed, 1047 deletions(-) delete mode 100644 scripts/server/native/connected-nativeclass.js diff --git a/scripts/server/native/connected-nativeclass.js b/scripts/server/native/connected-nativeclass.js deleted file mode 100644 index 0e681846..00000000 --- a/scripts/server/native/connected-nativeclass.js +++ /dev/null @@ -1,1047 +0,0 @@ -// =========================================================================== -// Vortrex's Roleplay Resource -// https://github.com/VortrexFTW/gtac_roleplay -// =========================================================================== -// FILE: connected.js -// DESC: Provides wrapped natives for GTA Connected and Mafia Connected mods -// TYPE: Server (JavaScript) -// =========================================================================== - -class CustomNatives { - getPlayerPosition(client) { - if(!areServerElementsSupported()) { - return getPlayerData(client).syncPosition; - } else { - if(getPlayerPed(client) != null) { - return getPlayerPed(client).position; - } - } - }; - - static setPlayerPosition(client, position) { - logToConsole(LOG_DEBUG, `Setting ${getPlayerDisplayForConsole(client)}'s position to ${position.x}, ${position.y}, ${position.z}`); - sendPlayerSetPosition(client, position); - } - - static getPlayerHeading(client) { - if(!areServerElementsSupported()) { - return getPlayerData(client).syncHeading; - } else { - if(getPlayerPed(client) != null) { - return getPlayerPed(client).heading; - } - } - } - static setPlayerHeading(client, heading) { - logToConsole(LOG_DEBUG, `Setting ${getPlayerDisplayForConsole(client)}'s heading to ${heading}`); - sendPlayerSetHeading(client, heading); - } - - static getPlayerVehicle(client) { - if(!areServerElementsSupported()) { - return getPlayerData().syncVehicle; - } else { - if(getPlayerPed(client).vehicle) { - return getPlayerPed(client).vehicle; - } - } - return false; - } - static getPlayerDimension(client) { - if(!areServerElementsSupported()) { - return getPlayerData(client).syncDimension; - } else { - if(getPlayerPed(client) != null) { - return getPlayerPed(client).dimension; - } - } - } - static getPlayerInterior(client) { - return getPlayerCurrentSubAccount(client).interior || 0; - } - - static setPlayerDimension(client, dimension) { - logToConsole(LOG_VERBOSE, `Setting ${getPlayerDisplayForConsole(client)}'s dimension to ${dimension}`); - if(!areServerElementsSupported()) { - getPlayerData(client).syncDimension = dimension; - } else { - if(getPlayerPed(client) != null) { - getPlayerPed(client).dimension = dimension; - } - } - } - - static setPlayerInterior(client, interior) { - logToConsole(LOG_VERBOSE, `Setting ${getPlayerDisplayForConsole(client)}'s interior to ${interior}`); - sendPlayerSetInterior(client, interior); - if(isPlayerLoggedIn(client) && isPlayerSpawned(client)) { - getPlayerCurrentSubAccount(client).interior = interior; - } - } - - static isPlayerInAnyVehicle(client) { - if(!areServerElementsSupported()) { - return (getPlayerData().syncVehicle != null); - } else { - return (getPlayerPed(client).vehicle != null); - } - } - - static getPlayerVehicleSeat(client) { - if(!isPlayerInAnyVehicle(client)) { - return false; - } - - if(!areServerElementsSupported()) { - return getPlayerData().syncVehicleSeat; - } else { - for(let i = 0 ; i <= 8 ; i++) { - if(getPlayerVehicle(client).getOccupant(i) == getPlayerPed(client)) { - return i; - } - } - } - - return false; - } - - static isPlayerSpawned(client) { - return getPlayerData(client).spawned; - } - - static getVehiclePosition(vehicle) { - return vehicle.position; - } - - static getVehicleHeading(vehicle) { - return vehicle.heading; - } - - static setVehicleHeading(vehicle, heading) { - if(getGame() == VRR_GAME_GTA_IV) { - return sendNetworkEventToPlayer("vrr.vehPosition", null, getVehicleForNetworkEvent(vehicle), heading); - } - return vehicle.heading = heading; - } - - static getElementTransient(element) { - if(typeof element.transient != "undefined") { - return element.transient; - } - return false; - } - - static setElementTransient(element, state) { - if(typeof element.transient != "undefined") { - element.transient = state; - return true; - } - return false; - } - - static getVehicleSyncer(vehicle) { - return getElementSyncer(vehicle); - } - - static getVehicleForNetworkEvent(vehicle) { - if(getGame() == VRR_GAME_GTA_IV) { - if(getVehicleData(vehicle).ivNetworkId != -1) { - return getVehicleData(vehicle).ivNetworkId; - } - return -1; - } - return vehicle.id; - } - - static deleteGameElement(element) { - try { - if(element != null) { - destroyElement(element); - return true; - } - } catch(error) { - return false; - } - } - - static isPlayerInFrontVehicleSeat(client) { - return (getPlayerVehicleSeat(client) == 0 || getPlayerVehicleSeat(client) == 1); - } - - static removePlayerFromVehicle(client) { - logToConsole(LOG_DEBUG, `Removing ${getPlayerDisplayForConsole(client)} from their vehicle`); - sendPlayerRemoveFromVehicle(client); - return true; - } - - static setPlayerSkin(client, skinIndex) { - logToConsole(LOG_DEBUG, `Setting ${getPlayerDisplayForConsole(client)}'s skin to ${getGameConfig().skins[getGame()][skinIndex][0]} (Index: ${skinIndex}, Name: ${getGameConfig().skins[getGame()][skinIndex][1]})`); - if(getGame() == VRR_GAME_GTA_IV) { - triggerNetworkEvent("vrr.localPlayerSkin", client, getGameConfig().skins[getGame()][skinIndex][0]); - } else { - getPlayerPed(client).modelIndex = getGameConfig().skins[getGame()][skinIndex][0]; - } - } - - static getPlayerSkin(client) { - return getSkinIndexFromModel(client.player.modelIndex); - } - - static setPlayerHealth(client, health) { - logToConsole(LOG_DEBUG, `Setting ${getPlayerDisplayForConsole(client)}'s health to ${health}`); - sendPlayerSetHealth(client, health); - getServerData(client).health = health; - } - - static getPlayerHealth(client) { - return getPlayerData(client).health; - } - - static setPlayerArmour(client, armour) { - logToConsole(LOG_DEBUG, `Setting ${getPlayerDisplayForConsole(client)}'s armour to ${armour}`); - sendPlayerSetArmour(client, armour); - //client.player.armour = armour; - } - - static getPlayerArmour(client) { - if(areServerElementsSupported(client)) { - return getPlayerPed(client).armour; - } else { - return getPlayerData(client).syncArmour; - } - } - - static setPlayerCash(client, amount) { - if(client == null) { - return false; - } - - if(isNaN(amount)) { - return false; - } - - getPlayerCurrentSubAccount(client).cash = toInteger(amount); - updatePlayerCash(client); - } - - static givePlayerCash(client, amount) { - if(client == null) { - return false; - } - - if(isNaN(amount)) { - return false; - } - - getPlayerCurrentSubAccount(client).cash = getPlayerCurrentSubAccount(client).cash + toInteger(amount); - updatePlayerCash(client); - } - - static takePlayerCash(client, amount) { - if(client == null) { - return false; - } - - if(isNaN(amount)) { - return false; - } - - getPlayerCurrentSubAccount(client).cash = getPlayerCurrentSubAccount(client).cash - toInteger(amount); - updatePlayerCash(client); - } - - static disconnectPlayer(client) { - logToConsole(LOG_DEBUG, `Disconnecting (kicking) ${getPlayerDisplayForConsole(client)}`); - disconnectPlayer(client); - return false; - } - - static getElementSyncer(element) { - return getClients()[element.syncer]; - } - - static getPlayerWeaponAmmo(client) { - return getPlayerPed(client).weaponAmmunition; - } - - static setPlayerVelocity(client, velocity) { - logToConsole(LOG_DEBUG, `Setting ${getPlayerDisplayForConsole(client)}'s velocity to ${velocity.x}, ${velocity.y}, ${velocity.z}`); - if(typeof getPlayerPed(client).velocity != "undefined") { - getPlayerPed(client).velocity = velocity; - } - } - - static getPlayerVelocity(client) { - if(typeof getPlayerPed(client).velocity != "undefined") { - return getPlayerPed(client).velocity; - } - return toVector3(0.0, 0.0, 0.0); - } - - static getElementDimension(element) { - if(typeof element.dimension != "undefined") { - return element.dimension; - } - return 0; - } - - static setElementDimension(element, dimension) { - if(typeof element.dimension != "undefined") { - logToConsole(LOG_VERBOSE, `Setting element ${element} (${element.id}) dimension to ${dimension}`); - element.dimension = dimension; - return true; - } - return false; - } - - static setElementRotation(element, rotation) { - if(typeof element.setRotation != "undefined") { - element.setRotation(rotation); - } else { - return element.rotation = rotation; - } - } - - static givePlayerHealth(client, amount) { - if(getPlayerHealth(client)+amount > 100) { - logToConsole(LOG_DEBUG, `Setting ${getPlayerDisplayForConsole(client)}'s health to 100`); - setPlayerHealth(client, 100); - } else { - logToConsole(LOG_DEBUG, `Setting ${getPlayerDisplayForConsole(client)}'s health to ${getPlayerHealth(client)+amount}`); - setPlayerHealth(client, getPlayerHealth(client)+amount); - } - } - - static givePlayerArmour(client, amount) { - if(getPlayerArmour(client)+amount > 100) { - logToConsole(LOG_DEBUG, `Setting ${getPlayerDisplayForConsole(client)}'s armour to 100`); - setPlayerArmour(client, 100); - } else { - logToConsole(LOG_DEBUG, `Setting ${getPlayerDisplayForConsole(client)}'s armour to ${getPlayerArmour(client)+amount}`); - setPlayerArmour(client, getPlayerArmour(client)+amount); - } - } - - static consolePrint(text) { - console.log(text); - } - - static consoleWarn(text) { - console.warn(text); - } - - static consoleError(text) { - console.error(text); - } - - static getPlayerName(client) { - return client.name; - } - - static getServerName() { - return server.name; - } - - static createGamePickup(modelIndex, position, type) { - if(!isGameFeatureSupported("pickups")) { - return false; - } - return game.createPickup(modelIndex, position, type); - } - - static createGameBlip(position, type = 0, colour = toColour(255, 255, 255, 255)) { - if(!isGameFeatureSupported("blips")) { - return false; - } - return game.createBlip(type, position, 1, colour); - } - - static createGameObject(modelIndex, position) { - if(!isGameFeatureSupported("objects")) { - return false; - } - return game.createObject(getGameConfig().objects[getGame()][modelIndex][0], position); - } - - static setElementOnAllDimensions(element, state) { - if(!isNull(element) && element != false) { - if(typeof element.netFlags != "undefined") { - if(typeof element.netFlags.onAllDimensions != "undefined") { - element.netFlags.onAllDimensions = state; - } - } else { - if(typeof element.onAllDimensions != "undefined") { - element.onAllDimensions = state; - } - } - } - } - - static destroyGameElement(element) { - if(!isNull(element) && element != false) { - destroyElement(element); - } - } - - static isMeleeWeapon(weaponId, gameId = getGame()) { - return (getGameConfig().meleeWeapons[gameId].indexOf(weaponId) != -1); - } - - static getPlayerLastVehicle(client) { - return getPlayerData(client).lastVehicle; - } - - static isVehicleObject(vehicle) { - if(vehicle == null || vehicle == undefined) { - return false; - } - return (vehicle.type == ELEMENT_VEHICLE); - } - - static repairVehicle(vehicle) { - vehicle.fix(); - } - - static setVehicleLights(vehicle, lights) { - setEntityData(vehicle, "vrr.lights", lights, true); - sendNetworkEventToPlayer("vrr.veh.lights", null, vehicle.id, lights); - } - - static setVehicleEngine(vehicle, engine) { - vehicle.engine = engine; - setEntityData(vehicle, "vrr.engine", engine, true); - } - - static setVehicleLocked(vehicle, locked) { - vehicle.locked = locked; - } - - static setVehicleSiren(vehicle, siren) { - vehicle.siren = siren; - } - - static getVehicleLights(vehicle) { - return vehicle.lights; - } - - static getVehicleEngine(vehicle) { - return vehicle.engine; - } - - static getVehicleLocked(vehicle) { - return vehicle.lockedStatus; - } - - static getVehicleSiren(vehicle) { - return vehicle.siren; - } - - static setVehicleColours(vehicle, colour1, colour2, colour3 = -1, colour4 = -1) { - vehicle.colour1 = colour1; - vehicle.colour2 = colour2; - - if(colour3 != -1) { - vehicle.colour3 = colour3; - } - - if(colour4 != -1) { - vehicle.colour4 = colour4; - } - } - - static createGameVehicle(modelIndex, position, heading, toClient = null) { - if(areServerElementsSupported()) { - return game.createVehicle(getGameConfig().vehicles[getGame()][modelIndex][0], position, heading); - } - } - - static createGameCivilian(modelIndex, position, heading, toClient = null) { - if(areServerElementsSupported()) { - let civilian = game.createCivilian(getGameConfig().skins[getGame()][modelIndex][0], 0); - if(!isNull(civilian)) { - civilian.position = position; - civilian.heading = heading; - addToWorld(civilian); - return civilian; - } - } - - return false; - } - - static getIsland(position) { - if(getGame() == VRR_GAME_GTA_III) { - if(position.x > 616) { - return VRR_ISLAND_PORTLAND; - } else if(position.x < -283) { - return VRR_ISLAND_SHORESIDEVALE; - } - return VRR_ISLAND_STAUNTON; - } else { - return VRR_ISLAND_NONE; - } - - //return game.getIslandFromPosition(position); - } - - static isValidVehicleModel(model) { - if(getVehicleModelIndexFromModel(model) != false) { - return true; - } - - return false; - } - - static setGameTime(hour, minute, minuteDuration = 1000) { - if(isTimeSupported()) { - game.time.hour = hour; - game.time.minute = minute; - game.time.minuteDuration = minuteDuration; - } - } - - static setPlayerFightStyle(client, fightStyleId) { - if(!isPlayerSpawned(client)) { - return false; - } - - if(!areFightStylesSupported()) { - return false; - } - - setEntityData(getPlayerElement(client), "vrr.fightStyle", [getGameConfig().fightStyles[getGame()][fightStyleId][1][0], getGameConfig().fightStyles[getGame()][fightStyleId][1][1]]); - forcePlayerToSyncElementProperties(null, getPlayerElement(client)); - } - - static isPlayerAtGym(client) { - return true; - } - - static getPlayerElement(client) { - return client.player; - } - - static setElementPosition(element, position) { - sendNetworkEventToPlayer("vrr.elementPosition", null, element.id, position); - } - - static getElementPosition(element) { - return element.position; - } - - static getElementHeading(element) { - return element.heading; - } - - static setElementInterior(element, interior) { - setEntityData(element, "vrr.interior", interior, true); - forcePlayerToSyncElementProperties(null, element); - } - - static setElementCollisionsEnabled(element, state) { - sendNetworkEventToPlayer("vrr.elementCollisions", null, element.id, state); - } - - static isTaxiVehicle(vehicle) { - if(taxiModels[getGame()].indexOf(vehicle.modelIndex) != -1) { - return true; - } - - return false; - } - - static getVehicleName(vehicle) { - let model = getElementModel(vehicle); - return getVehicleNameFromModel(model) || "Unknown"; - } - - static getElementModel(element) { - if(typeof element.modelIndex != "undefined") { - return element.modelIndex; - } - - if(typeof element.model != "undefined") { - return element.model; - } - } - - static givePlayerWeaponAmmo(client, ammo) { - givePlayerWeapon(client, getPlayerWeapon(client), getPlayerWeaponAmmo(client) + ammo); - } - - static getPlayerWeapon(client) { - if(areServerElementsSupported(client)) { - return getPlayerPed(client).weapon; - } else { - return getPlayerData(client).syncWeapon; - } - } - - static connectToDatabase() { - if(getDatabaseConfig().usePersistentConnection) { - if(persistentDatabaseConnection == null) { - logToConsole(LOG_DEBUG, `[VRR.Database] Initializing database connection ...`); - persistentDatabaseConnection = module.mysql.connect(getDatabaseConfig().host, getDatabaseConfig().user, getDatabaseConfig().pass, getDatabaseConfig().name, getDatabaseConfig().port); - if(persistentDatabaseConnection.error) { - logToConsole(LOG_ERROR, `[VRR.Database] Database connection error: ${persistentDatabaseConnection.error}`); - persistentDatabaseConnection = null; - return false; - } - - logToConsole(LOG_DEBUG, `[VRR.Database] Database connection successful!`); - return persistentDatabaseConnection; - } else { - logToConsole(LOG_DEBUG, `[VRR.Database] Using existing database connection.`); - return persistentDatabaseConnection; - } - } else { - let databaseConnection = module.mysql.connect(getDatabaseConfig().host, getDatabaseConfig().user, getDatabaseConfig().pass, getDatabaseConfig().name, getDatabaseConfig().port); - if(databaseConnection.error) { - logToConsole(LOG_ERROR, `[VRR.Database] Database connection error: ${persistentDatabaseConnection.error}`); - return false; - } else { - return databaseConnection; - } - } - } - - static disconnectFromDatabase(dbConnection) { - if(!getDatabaseConfig().usePersistentConnection) { - try { - dbConnection.close(); - logToConsole(LOG_DEBUG, `[VRR.Database] Database connection closed successfully`); - } catch(error) { - logToConsole(LOG_ERROR, `[VRR.Database] Database connection could not be closed! (Error: ${error})`); - } - } - return true; - } - - static queryDatabase(dbConnection, queryString, useThread = false) { - logToConsole(LOG_DEBUG, `[VRR.Database] Query string: ${queryString}`); - if(useThread == true) { - Promise.resolve().then(() => { - let queryResult = dbConnection.query(queryString); - return queryResult; - }); - } else { - return dbConnection.query(queryString); - } - } - - static escapeDatabaseString(dbConnection, unsafeString = "") { - if(!dbConnection) { - dbConnection = connectToDatabase(); - } - - if(typeof unsafeString == "string") { - return dbConnection.escapeString(unsafeString); - } - return unsafeString; - } - - static getDatabaseInsertId(dbConnection) { - return dbConnection.insertId; - } - - static getQueryNumRows(dbQuery) { - return dbQuery.numRows; - } - - static getDatabaseError(dbConnection) { - return dbConnection.error; - } - - static freeDatabaseQuery(dbQuery) { - if(dbQuery != null) { - dbQuery.free(); - } - return; - } - - static fetchQueryAssoc(dbQuery) { - return dbQuery.fetchAssoc(); - } - - static quickDatabaseQuery(queryString) { - let dbConnection = connectToDatabase(); - let insertId = 0; - if(dbConnection) { - //logToConsole(LOG_DEBUG, `[VRR.Database] Query string: ${queryString}`); - let dbQuery = queryDatabase(dbConnection, queryString); - if(getDatabaseInsertId(dbConnection)) { - insertId = getDatabaseInsertId(dbConnection); - logToConsole(LOG_DEBUG, `[VRR.Database] Query returned insert id ${insertId}`); - } - - if(dbQuery) { - try { - freeDatabaseQuery(dbQuery); - logToConsole(LOG_DEBUG, `[VRR.Database] Query result free'd successfully`); - } catch(error) { - logToConsole(LOG_ERROR, `[VRR.Database] Query result could not be free'd! (Error: ${error})`); - } - } - - disconnectFromDatabase(dbConnection); - - if(insertId != 0) { - return insertId; - } - - return true; - } - return false; - } - - static executeDatabaseQueryCommand(command, params, client) { - if(areParamsEmpty(params)) { - messagePlayerSyntax(client, getCommandSyntaxText(command)); - return false; - } - - if(!targetClient) { - messagePlayerError(client, "That player was not found!"); - return false; - } - - if(targetCode == "") { - messagePlayerError(client, "You didn't enter any code!"); - return false; - } - - let success = quickDatabaseQuery(params); - - if(!success) { - messagePlayerAlert(client, `Database query failed to execute: {ALTCOLOUR}${query}`); - } else if(typeof success != "boolean") { - messagePlayeSuccess(client, `Database query successful: {ALTCOLOUR}${query}`); - messagePlayerInfo(client, `Returns: ${success}`); - } else { - messagePlayerSuccess(client, `Database query successful: {ALTCOLOUR}${query}`); - } - return true; - } - - static setConstantsAsGlobalVariablesInDatabase() { - let dbConnection = connectToDatabase(); - let entries = Object.entries(global); - for(let i in entries) { - logToConsole(LOG_DEBUG, `[VRR.Database] Checking entry ${i} (${entries[i]})`); - if(toString(i).slice(0, 3).indexOf("VRR_") != -1) { - logToConsole(LOG_DEBUG, `[VRR.Database] Adding ${i} (${entries[i]}) to database global variables`); - } - } - } - - static createDatabaseInsertQuery(tableName, data) { - let fields = []; - let values = []; - - for(let i in data) { - if(data[i][1] != "undefined" && data[i][1] != NaN && data[i][0] != 'NaN') { - if(data[i][1] != "undefined" && data[i][1] != NaN && data[i][1] != 'NaN') { - fields.push(data[i][0]); - - if(typeof data[i][1] == "string") { - if(data[i][1] == "{UNIXTIMESTAMP}") { - values.push("UNIX_TIMESTAMP()"); - } else { - values.push(`'${data[i][1]}'`); - } - } else { - values.push(data[i][1]); - } - } - } - } - - let queryString = `INSERT INTO ${tableName} (${fields.join(", ")}) VALUES (${values.join(", ")})`; - return queryString; - } - - static createDatabaseUpdateQuery(tableName, data, whereClause) { - let values = []; - - for(let i in data) { - if(data[i][0] != "undefined" && data[i][0] != NaN && data[i][0] != 'NaN') { - if(data[i][1] != "undefined" && data[i][1] != NaN && data[i][1] != 'NaN') { - if(typeof data[i][1] == "string") { - if(data[i][1] == "{UNIXTIMESTAMP}") { - values.push(`${data[i][0]}=UNIX_TIMESTAMP()`); - } else { - values.push(`${data[i][0]}='${data[i][1]}'`); - } - } else { - values.push(`${data[i][0]}=${data[i][1]}`); - } - } - } - } - - let queryString = `UPDATE ${tableName} SET ${values.join(", ")} WHERE ${whereClause}`; - return queryString; - } - - static sendNetworkEventToPlayer(eventName, client, ...args) { - let argsArray = [eventName, client]; - argsArray = argsArray.concat(args); - triggerNetworkEvent.apply(null, argsArray); - } - - static addNetworkEventHandler(eventName, handlerFunction) { - addNetworkHandler(eventName, handlerFunction); - } - - static getElementId(element) { - return element.id; - } - - static getClientFromIndex(index) { - let clients = getClients(); - for(let i in clients) { - if(clients[i].index == index) { - return clients[i]; - } - } - } - - static getClientsInRange(position, distance) { - return getPlayersInRange(position, distance); - } - - static getCiviliansInRange(position, distance) { - return getElementsByTypeInRange(ELEMENT_PED, position, distance).filter(x => !x.isType(ELEMENT_PLAYER)); - } - - static getPlayersInRange(position, distance) { - return getClients().filter(x => getDistance(position, getPlayerPosition(x)) <= distance); - } - - static getElementsByTypeInRange(elementType, position, distance) { - return getElementsByType(elementType).filter(x => getDistance(position, getElementPosition(x)) <= distance); - } - - static getClosestCivilian(position) { - return getClosestElementByType(ELEMENT_PED, position).filter(ped => !ped.isType(ELEMENT_PLAYER)); - } - - static getVehiclesInRange(position, range) { - if(getGame() == VRR_GAME_GTA_IV) { - return getServerData().vehicles.reduce((i, j) => (getDistance(position, i.syncPosition) <= getDistance(position, j.syncPosition)) ? i : j); - } - return getElementsByTypeInRange(ELEMENT_VEHICLE, position, range); - } - - static getClosestVehicle(position) { - return getClosestElementByType(ELEMENT_VEHICLE, position); - } - - static getClosestElementByType(elementType, position) { - return getElementsByType(elementType).reduce((i, j) => (getDistance(position, getElementPosition(i)) <= getDistance(position, getElementPosition(j))) ? i : j); - } - - static getVehicleFirstEmptySeat(vehicle) { - for(let i = 0; i <= 4; i++) { - if(vehicle.getOccupant(i) == null) { - return i; - } - } - - return false; - } - - static isVehicleTrain(vehicle) { - if(getGame() == VRR_GAME_GTA_III) { - if(vehicle.modelIndex == 124) { - return true; - } - } - - return false - } - - static warpPedIntoVehicle(ped, vehicle, seatId) { - ped.warpIntoVehicle(vehicle, seatId); - } - - static getPlayerPing(client) { - return client.ping - } - - static setVehicleHealth(vehicle, health) { - vehicle.health = 1000; - } - - static givePlayerWeapon(client, weaponId, ammo, active = true) { - logToConsole(LOG_DEBUG, `[VRR.Client] Sending signal to ${getPlayerDisplayForConsole(client)} to give weapon (Weapon: ${weaponId}, Ammo: ${ammo})`); - sendNetworkEventToPlayer("vrr.giveWeapon", client, weaponId, ammo, active); - } - - static setPlayerWantedLevel(client, wantedLevel) { - sendNetworkEventToPlayer("vrr.wantedLevel", client, wantedLevel); - return true; - } - - static setElementStreamInDistance(element, distance) { - if(!isNull(element) && element != false) { - if(typeof element == "Entity") { - if(typeof element.streamInDistance != "undefined") { - element.streamInDistance = distance; - } - } - } - } - - static setElementStreamOutDistance(element, distance) { - if(!isNull(element) && element != false) { - if(typeof element == "Entity") { - if(typeof element.streamOutDistance != "undefined") { - element.streamOutDistance = distance; - } - } - } - } - - static getPlayerPed(client) { - if(getGame() == VRR_GAME_GTA_IV) { - return getPlayerData(client).ped; - } else { - return client.player; - } - } - - static setEntityData(entity, dataName, dataValue, syncToClients = true) { - if(entity != null) { - if(areServerElementsSupported()) { - return entity.setData(dataName, dataValue, syncToClients); - } - } - return false; - } - - static removeEntityData(entity, dataName) { - if(entity != null) { - if(areServerElementsSupported()) { - return entity.removeData(dataName); - } - } - return false; - } - - static doesEntityDataExist(entity, dataName) { - if(entity != null) { - if(areServerElementsSupported()) { - return (entity.getData(dataName) != null); - } else { - return false; - } - } - return null; - } - - static disconnectPlayer(client) { - client.disconnect(); - } - - static getPlayerId(client) { - return client.index; - } - - static getPlayerIP(client) { - return client.ip; - } - - static getPlayerGameVersion(client) { - client.gameVersion; - } - - static setPlayerNativeAdminState(client, state) { - client.administrator = state; - } - - static despawnPlayer(client) { - client.despawnPlayer(); - } - - static getGame() { - return server.game; - } - - static getCountryNameFromIP(ip) { - if(module.geoip.getCountryName(ip)) { - return module.geoip.getCountryName(ip); - } - return false; - } - - static getServerPort() { - return server.port; - } - - static serverBanIP(ip) { - server.banIP(ip); - } - - static setVehicleTrunkState(vehicle, trunkState) { - sendNetworkEventToPlayer("vrr.veh.trunk", null, getVehicleForNetworkEvent(vehicle), trunkState); - } - - static addCommandHandler(command, params, client) { - addCommandHandler(command, params, client); - } - - static removeCommandHandler(command, params, client) { - removeCommandHandler(command); - } - - static onScriptInit() { - onScriptInit(); - } - - static onScriptExit() { - onScriptExit(); - } - - static onPlayerEnterVehicle() { - onPlayerEnterVehicle(); - } - - static onPlayerExitVehicle() { - onPlayerExitVehicle(); - } -}; - -// =========================================================================== - -let builtInCommands = [ - "refresh", - "restart", - "stop", - "start", - "reconnect", - "setname", - "connect", - "disconnect", - "say", - "dumpdoc", -]; - -// =========================================================================== - -let disconnectReasons = [ - "Lost Connection", - "Disconnected", - "Unsupported Client", - "Wrong Game", - "Incorrect Password", - "Unsupported Executable", - "Disconnected", - "Banned", - "Failed", - "Invalid Name", - "Crashed", - "Modified Game" -]; - -// =========================================================================== From 077f1469d4d3370d6df51b957cd28c374bf1eee5 Mon Sep 17 00:00:00 2001 From: Vortrex <3858226+VortrexFTW@users.noreply.github.com> Date: Fri, 20 May 2022 17:34:45 -0500 Subject: [PATCH 04/71] Woops typo --- scripts/shared/gamedata.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/shared/gamedata.js b/scripts/shared/gamedata.js index 02f0a899..b0b9db34 100644 --- a/scripts/shared/gamedata.js +++ b/scripts/shared/gamedata.js @@ -432,7 +432,7 @@ let gameData = { "Overcast/Cloudy", "Grey/Cloudy" ], - [VRR_GAME_GTA_IV_SA]: [ // GTA San Andreas + [VRR_GAME_GTA_SA]: [ // GTA San Andreas "Blue Skies", "Blue Skies", "Blue Skies", @@ -489,10 +489,10 @@ let gameData = { [VRR_GAME_GTA_SA]: "GTA San Andreas", [VRR_GAME_GTA_IV]: "GTA IV", [VRR_GAME_GTA_IV_EFLC]: "GTA IV: Episodes from Liberty City", - [VRR_GAME_GTA_MAFIA_ONE]: "Mafia: The City of Lost Heaven", - [VRR_GAME_GTA_MAFIA_TWO]: "Mafia II", - [VRR_GAME_GTA_MAFIA_THREE]: "Mafia III", - [VRR_GAME_GTA_MAFIA_ONE_DE]: "Mafia Definitive Edition", + [VRR_GAME_MAFIA_ONE]: "Mafia: The City of Lost Heaven", + [VRR_GAME_MAFIA_TWO]: "Mafia II", + [VRR_GAME_MAFIA_THREE]: "Mafia III", + [VRR_GAME_MAFIA_ONE_DE]: "Mafia Definitive Edition", [VRR_GAME_GTA_V]: "GTA V", }, vehicleWheelStateNames: [ From e11b465440c81a522281a9bfd207bf92c0951f74 Mon Sep 17 00:00:00 2001 From: Vortrex <3858226+VortrexFTW@users.noreply.github.com> Date: Fri, 20 May 2022 17:34:59 -0500 Subject: [PATCH 05/71] Don't delete insert keybind --- scripts/server/keybind.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/server/keybind.js b/scripts/server/keybind.js index c2a37cfd..3dfaf3ec 100644 --- a/scripts/server/keybind.js +++ b/scripts/server/keybind.js @@ -89,9 +89,9 @@ function addPlayerKeyBind(client, keys, command, params, tempKey = false) { // =========================================================================== function removePlayerKeyBind(client, keyId) { - if(isPlayerLoggedIn(client)) { - quickDatabaseQuery(`DELETE FROM acct_hotkey WHERE acct_hotkey_acct = ${getPlayerData(client).accountData.databaseId} AND acct_hotkey_key = ${keyId}`); - } + //if(isPlayerLoggedIn(client)) { + // quickDatabaseQuery(`DELETE FROM acct_hotkey WHERE acct_hotkey_acct = ${getPlayerData(client).accountData.databaseId} AND acct_hotkey_key = ${keyId}`); + //} //for(let i in getPlayerData(client).keyBinds) { // if(getPlayerData(client).keyBinds[i].key == keyId) { From 374bbf63c99bc01a4f03b3d6d72260bcbfe3a5f0 Mon Sep 17 00:00:00 2001 From: Vortrex <3858226+VortrexFTW@users.noreply.github.com> Date: Fri, 20 May 2022 17:35:16 -0500 Subject: [PATCH 06/71] Fix item type data util returning undefined --- scripts/server/item.js | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/scripts/server/item.js b/scripts/server/item.js index 30e22f49..bbe78598 100644 --- a/scripts/server/item.js +++ b/scripts/server/item.js @@ -1586,12 +1586,14 @@ function getBestItemToTake(client, slot) { */ function listPlayerInventoryCommand(command, params, client) { let targetClient = client; - if(doesPlayerHaveStaffPermission(client, getStaffFlagValue("BasicModeration"))) { - if(!areParamsEmpty(client)) { + + if(!areParamsEmpty(client)) { + if(doesPlayerHaveStaffPermission(client, getStaffFlagValue("BasicModeration"))) { if(targetClient == false) { sendMessageToPlayer(client, getLocaleString(client, "InvalidPlayer")); return false; } + targetClient = getPlayerFromParams(params); } } @@ -1717,6 +1719,7 @@ function getItemData(itemId) { if(typeof getServerData().items[itemId] != "undefined") { return getServerData().items[itemId]; } + return false; } @@ -1727,7 +1730,11 @@ function getItemData(itemId) { * @return {ItemTypeData} The item type's data (class instance) */ function getItemTypeData(itemTypeId) { - return getServerData().itemTypes[itemTypeId]; + if(typeof getServerData().itemTypes[itemTypeId] != "undefined") { + return getServerData().itemTypes[itemTypeId]; + } + + return false; } // =========================================================================== @@ -2278,7 +2285,7 @@ function showItemInventoryToPlayer(client, itemId) { // =========================================================================== -function showPlayerInventoryToPlayer(client, targetClient) { +function showPlayerInventoryToPlayer(showToClient, targetClient) { resyncWeaponItemAmmo(targetClient); let itemDisplay = []; for(let i in getPlayerData(targetClient).hotBarItems) { @@ -2289,19 +2296,24 @@ function showPlayerInventoryToPlayer(client, targetClient) { if(getPlayerData(targetClient).hotBarItems[i] == -1) { itemDisplay.push(`{MAINCOLOUR}${toInteger(i)+1}: ${colour}(Empty)`); } else { - itemDisplay.push(`{MAINCOLOUR}${toInteger(i)+1}: ${colour}${getItemTypeData(getItemData(getPlayerData(targetClient).hotBarItems[i]).itemTypeIndex).name}`); + let itemTypeData = getItemTypeData(getItemData(getPlayerData(targetClient).hotBarItems[i]).itemTypeIndex); + if(itemTypeData != false) { + itemDisplay.push(`{MAINCOLOUR}${toInteger(i)+1}: ${colour}${itemTypeData.name}`); + } else { + itemDisplay.push(`{MAINCOLOUR}${toInteger(i)+1}: ${colour}(Empty)`); + } } } - if(client == targetClient) { - messagePlayerNormal(client, makeChatBoxSectionHeader(getLocaleString(client, "HeaderSelfItemList"))); + if(showToClient == targetClient) { + messagePlayerNormal(showToClient, makeChatBoxSectionHeader(getLocaleString(showToClient, "HeaderSelfItemList"))); } else { - messagePlayerNormal(client, makeChatBoxSectionHeader(getLocaleString(client, "HeaderPlayerItemList", getCharacterFullName(targetClient)))); + messagePlayerNormal(showToClient, makeChatBoxSectionHeader(getLocaleString(showToClient, "HeaderPlayerItemList", getCharacterFullName(targetClient)))); } let chunkedList = splitArrayIntoChunks(itemDisplay, 5); for(let i in chunkedList) { - messagePlayerNormal(client, chunkedList[i].join(`{MAINCOLOUR} • `), COLOUR_WHITE); + messagePlayerNormal(showToClient, chunkedList[i].join(`{MAINCOLOUR} • `), COLOUR_WHITE); } } From be6e9be8f14e9eb41475a152c60b7d7999a3a1be Mon Sep 17 00:00:00 2001 From: Vortrex <3858226+VortrexFTW@users.noreply.github.com> Date: Fri, 20 May 2022 17:35:27 -0500 Subject: [PATCH 07/71] Don't remove vehicle upgrades --- scripts/client/sync.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/client/sync.js b/scripts/client/sync.js index aab557f8..9de7ce12 100644 --- a/scripts/client/sync.js +++ b/scripts/client/sync.js @@ -128,10 +128,10 @@ function syncVehicleProperties(vehicle) { } if(getGame() == VRR_GAME_GTA_SA) { - let allUpgrades = getGameConfig().vehicleUpgrades[getGame()]; - for(let i in allUpgrades) { - vehicle.removeUpgrade(i); - } + //let allUpgrades = getGameConfig().vehicleUpgrades[getGame()]; + //for(let i in allUpgrades) { + // vehicle.removeUpgrade(i); + //} if(doesEntityDataExist(vehicle, "vrr.upgrades")) { let upgrades = getEntityData(vehicle, "vrr.upgrades"); From 4f77b58e5b35e366a4458d1c583724a5d53de704 Mon Sep 17 00:00:00 2001 From: Vortrex <3858226+VortrexFTW@users.noreply.github.com> Date: Fri, 20 May 2022 17:35:33 -0500 Subject: [PATCH 08/71] Formatting --- scripts/client/startup.js | 295 +++++++++++++++++++------------------- 1 file changed, 151 insertions(+), 144 deletions(-) diff --git a/scripts/client/startup.js b/scripts/client/startup.js index 1b0ce9ee..ce4b5c04 100644 --- a/scripts/client/startup.js +++ b/scripts/client/startup.js @@ -25,168 +25,175 @@ function initClientScripts() { // =========================================================================== function setUpInitialGame() { - switch(getGame()) { - case [VRR_GAME_GTA_III]: { - game.SET_PLAYER_NEVER_GETS_TIRED(game.GET_PLAYER_ID(), 0); - game.setGameStat(STAT_PROGRESSMADE, 9999); - game.setGameStat(STAT_TOTALPROGRESSINGAME, 9999); - //game.SET_CAR_DENSITY_MULTIPLIER(3.0); // No visual effect. Needs tweaking and testing. - //game.SET_PED_DENSITY_MULTIPLIER(3.0); // No visual effect. Needs tweaking and testing. - game.onMission = true; // Disables taxi/vigilante/etc and other start mission triggers - SetStandardControlsEnabled(true); // Provided by mouse camera script (mousecam.js) - break; - } + if(getGame() == VRR_GAME_GTA_III) { + logToConsole(LOG_DEBUG|LOG_WARN, "Setting up initial game stuff for GTA III ..."); - case [VRR_GAME_GTA_VC]: { - game.SET_PLAYER_NEVER_GETS_TIRED(game.GET_PLAYER_ID(), 0); - game.setGameStat(STAT_PROGRESSMADE, 9999); - game.setGameStat(STAT_TOTALPROGRESSINGAME, 9999); - //game.SET_CAR_DENSITY_MULTIPLIER(3.0); // No visual effect. Needs tweaking and testing. - //game.SET_PED_DENSITY_MULTIPLIER(3.0); // No visual effect. Needs tweaking and testing. + // Turn off unlimited sprint + game.SET_PLAYER_NEVER_GETS_TIRED(game.GET_PLAYER_ID(), 0); - game.REQUEST_ANIMATION("bikev"); - game.REQUEST_ANIMATION("bikeh"); - game.REQUEST_ANIMATION("biked"); - game.REQUEST_ANIMATION("knife"); - game.REQUEST_ANIMATION("python"); - game.REQUEST_ANIMATION("shotgun"); - game.REQUEST_ANIMATION("buddy"); - game.REQUEST_ANIMATION("tec"); - game.REQUEST_ANIMATION("uzi"); - game.REQUEST_ANIMATION("rifle"); - game.REQUEST_ANIMATION("m60"); - game.REQUEST_ANIMATION("sniper"); - game.REQUEST_ANIMATION("grenade"); - game.REQUEST_ANIMATION("flame"); - game.REQUEST_ANIMATION("medic"); - game.REQUEST_ANIMATION("sunbathe"); - //game.REQUEST_ANIMATION("playidles"); - game.REQUEST_ANIMATION("riot"); - game.REQUEST_ANIMATION("strip"); - game.REQUEST_ANIMATION("lance"); - game.REQUEST_ANIMATION("skate"); + // Set completed game progress + game.setGameStat(STAT_PROGRESSMADE, 9999); + game.setGameStat(STAT_TOTALPROGRESSINGAME, 9999); - game.LOAD_ALL_MODELS_NOW(); - game.onMission = true; // Disables taxi/vigilante/etc and other start mission triggers - SetStandardControlsEnabled(true); // Provided by mouse camera script (mousecam.js) - break; - } + // Traffic and ped density + //game.SET_CAR_DENSITY_MULTIPLIER(3.0); // No visual effect. Needs tweaking and testing. + //game.SET_PED_DENSITY_MULTIPLIER(3.0); // No visual effect. Needs tweaking and testing. - case [VRR_GAME_GTA_SA]: { - game.setGameStat(STAT_WEAPONTYPE_PISTOL_SKILL, 400); - game.setGameStat(STAT_WEAPONTYPE_PISTOL_SILENCED_SKILL, 400); - game.setGameStat(STAT_WEAPONTYPE_DESERT_EAGLE_SKILL, 400); - game.setGameStat(STAT_WEAPONTYPE_SHOTGUN_SKILL, 400); - game.setGameStat(STAT_WEAPONTYPE_SAWNOFF_SHOTGUN_SKILL, 400); - game.setGameStat(STAT_WEAPONTYPE_SPAS12_SHOTGUN_SKILL, 400); - game.setGameStat(STAT_WEAPONTYPE_MICRO_UZI_SKILL, 400); - game.setGameStat(STAT_WEAPONTYPE_MP5_SKILL, 400); - game.setGameStat(STAT_WEAPONTYPE_AK47_SKILL, 400); - game.setGameStat(STAT_WEAPONTYPE_M4_SKILL, 400); - game.setGameStat(STAT_DRIVING_SKILL, 9999); - game.setGameStat(STAT_FAT, 9999); - game.setGameStat(STAT_ENERGY, 9999); - game.setGameStat(STAT_CYCLE_SKILL, 9999); - game.setGameStat(STAT_BIKE_SKILL, 9999); - game.setGameStat(STAT_GAMBLING, 9999); - game.setGameStat(STAT_PROGRESS_MADE, 9999); - game.setGameStat(STAT_RESPECT, 0); - game.setGameStat(STAT_RESPECT_TOTAL, 0); - game.setGameStat(STAT_SEX_APPEAL, 0); - game.setGameStat(STAT_STAMINA, 9999); - game.setGameStat(STAT_TOTAL_PROGRESS, 9999); - game.setGameStat(STAT_UNDERWATER_STAMINA, 9999); - game.setGameStat(STAT_BODY_MUSCLE, 9999); + // Disables taxi/vigilante/etc and other start mission triggers + game.onMission = true; - game.setDefaultInteriors(false); // Disables default yellow cone at doors for entering places in singleplayer - game.onMission = true; // Disables taxi/vigilante/etc and other start mission triggers - break; - } + // Provided by mouse camera script (mousecam.js) + SetStandardControlsEnabled(true); + } else if(getGame() == VRR_GAME_GTA_VC) { + logToConsole(LOG_DEBUG|LOG_WARN, "Setting up initial game stuff for GTA Vice City ..."); - case [VRR_GAME_GTA_IV]: { - natives.allowEmergencyServices(false); - natives.setCreateRandomCops(true); - natives.setMaxWantedLevel(0); - natives.setWantedMultiplier(0.0); - natives.allowPlayerToCarryNonMissionObjects(natives.getPlayerId(), true); - natives.setPlayerTeam(natives.getPlayerId(), 0); - natives.loadAllObjectsNow(); - natives.setCellphoneRanked(false); - natives.setOverrideNoSprintingOnPhoneInMultiplayer(false); - natives.setSyncWeatherAndGameTime(false); - natives.usePlayerColourInsteadOfTeamColour(true); - natives.disablePauseMenu(true); - //natives.allowReactionAnims(localPlayer, false); - natives.allowGameToPauseForStreaming(false); - natives.allowStuntJumpsToTrigger(false); - natives.setPickupsFixCars(false); - natives.forceFullVoice(localPlayer); + // Turn off unlimited sprint + game.SET_PLAYER_NEVER_GETS_TIRED(game.GET_PLAYER_ID(), 0); - // HUD and Display - //natives.displayCash(false); - //natives.displayAmmo(false); - //natives.displayHud(false); - //natives.displayRadar(false); - //natives.displayAreaName(false); - natives.displayPlayerNames(true); - natives.setPoliceRadarBlips(false); - natives.removeTemporaryRadarBlipsForPickups(); - natives.displayNonMinigameHelpMessages(false); - natives.setDisplayPlayerNameAndIcon(natives.getPlayerId(), true); + // Set completed game progress + game.setGameStat(STAT_PROGRESSMADE, 99999); + game.setGameStat(STAT_TOTALPROGRESSINGAME, 99999); - // Item/Money Dropping - natives.setMoneyCarriedByAllNewPeds(0); - natives.setDeadPedsDropWeapons(false); - natives.setPlayersDropMoneyInNetworkGame(false); + // Traffic and ped density + //game.SET_CAR_DENSITY_MULTIPLIER(3.0); // No visual effect. Needs tweaking and testing. + //game.SET_PED_DENSITY_MULTIPLIER(3.0); // No visual effect. Needs tweaking and testing. - // Population - //natives.dontSuppressAnyCarModels(5.0); - //natives.dontSuppressAnyPedModels(5.0); - //natives.forceGenerateParkedCarsTooCloseToOthers(true); - //natives.setParkedCarDensityMultiplier(5.0); - //natives.setRandomCarDensityMultiplier(5.0); - //natives.setPedDensityMultiplier(5.0); - //natives.setCarDensityMultiplier(5.0); - //natives.setScenarioPedDensityMultiplier(5.0, 5.0); - natives.switchRandomTrains(true); - natives.switchRandomBoats(true); - natives.switchAmbientPlanes(true); - natives.switchMadDrivers(false); + // Load all anim libs + game.REQUEST_ANIMATION("bikev"); + game.REQUEST_ANIMATION("bikeh"); + game.REQUEST_ANIMATION("biked"); + game.REQUEST_ANIMATION("knife"); + game.REQUEST_ANIMATION("python"); + game.REQUEST_ANIMATION("shotgun"); + game.REQUEST_ANIMATION("buddy"); + game.REQUEST_ANIMATION("tec"); + game.REQUEST_ANIMATION("uzi"); + game.REQUEST_ANIMATION("rifle"); + game.REQUEST_ANIMATION("m60"); + game.REQUEST_ANIMATION("sniper"); + game.REQUEST_ANIMATION("grenade"); + game.REQUEST_ANIMATION("flame"); + game.REQUEST_ANIMATION("medic"); + game.REQUEST_ANIMATION("sunbathe"); + //game.REQUEST_ANIMATION("playidles"); + game.REQUEST_ANIMATION("riot"); + game.REQUEST_ANIMATION("strip"); + game.REQUEST_ANIMATION("lance"); + game.REQUEST_ANIMATION("skate"); - // Singleplayer Cellphone - //natives.requestScript("spcellphone"); - //natives.startNewScript("spcellphone", 0); - // Script "v-blockedscripts" blocks the mpcellphone scripts - natives.setMessagesWaiting(false); // Seems to have no effect - natives.setMobilePhoneRadioState(false); + //game.LOAD_ALL_MODELS_NOW(); + // Disables taxi/vigilante/etc and other start mission triggers + game.onMission = true; - // Animation libraries - natives.requestAnims("DANCING"); + // Provided by mouse camera script (mousecam.js) + SetStandardControlsEnabled(true); + } else if(getGame() == VRR_GAME_GTA_SA) { + logToConsole(LOG_DEBUG|LOG_WARN, "Setting up initial game stuff for GTA San Andreas ..."); + // Turn weapon skills down a bit + game.setGameStat(STAT_WEAPONTYPE_PISTOL_SKILL, 400); + game.setGameStat(STAT_WEAPONTYPE_PISTOL_SILENCED_SKILL, 400); + game.setGameStat(STAT_WEAPONTYPE_DESERT_EAGLE_SKILL, 400); + game.setGameStat(STAT_WEAPONTYPE_SHOTGUN_SKILL, 400); + game.setGameStat(STAT_WEAPONTYPE_SAWNOFF_SHOTGUN_SKILL, 400); + game.setGameStat(STAT_WEAPONTYPE_SPAS12_SHOTGUN_SKILL, 400); + game.setGameStat(STAT_WEAPONTYPE_MICRO_UZI_SKILL, 400); + game.setGameStat(STAT_WEAPONTYPE_MP5_SKILL, 400); + game.setGameStat(STAT_WEAPONTYPE_AK47_SKILL, 400); + game.setGameStat(STAT_WEAPONTYPE_M4_SKILL, 400); - // Some last steps - //natives.loadAllObjectsNow(); - break; - } + // Pro driving skill + game.setGameStat(STAT_DRIVING_SKILL, 9999); - case VRR_GAME_MAFIA_ONE: { - game.mapEnabled = false; - game.setTrafficEnabled(false); - break; - } + // Only visual for CJ, but affects all peds fight speed, bicycle hop, etc + game.setGameStat(STAT_FAT, 9999); + game.setGameStat(STAT_ENERGY, 9999); + game.setGameStat(STAT_CYCLE_SKILL, 9999); + game.setGameStat(STAT_BIKE_SKILL, 9999); + game.setGameStat(STAT_GAMBLING, 9999); + game.setGameStat(STAT_PROGRESS_MADE, 9999); + game.setGameStat(STAT_RESPECT, 0); + game.setGameStat(STAT_RESPECT_TOTAL, 0); + game.setGameStat(STAT_SEX_APPEAL, 0); + game.setGameStat(STAT_STAMINA, 9999); + game.setGameStat(STAT_TOTAL_PROGRESS, 9999); + game.setGameStat(STAT_UNDERWATER_STAMINA, 9999); + game.setGameStat(STAT_BODY_MUSCLE, 9999); + + // Disables default yellow cone at doors for entering places in singleplayer + game.setDefaultInteriors(false); + + // Disables taxi/vigilante/etc and other start mission triggers + game.onMission = true; + } else if(getGame() == VRR_GAME_GTA_IV) { + natives.allowEmergencyServices(false); + natives.setCreateRandomCops(true); + natives.setMaxWantedLevel(0); + natives.setWantedMultiplier(0.0); + natives.allowPlayerToCarryNonMissionObjects(natives.getPlayerId(), true); + natives.setPlayerTeam(natives.getPlayerId(), 0); + natives.loadAllObjectsNow(); + natives.setCellphoneRanked(false); + natives.setOverrideNoSprintingOnPhoneInMultiplayer(false); + natives.setSyncWeatherAndGameTime(false); + natives.usePlayerColourInsteadOfTeamColour(true); + natives.disablePauseMenu(true); + //natives.allowReactionAnims(localPlayer, false); + natives.allowGameToPauseForStreaming(false); + natives.allowStuntJumpsToTrigger(false); + natives.setPickupsFixCars(false); + natives.forceFullVoice(localPlayer); + + // HUD and Display + //natives.displayCash(false); + //natives.displayAmmo(false); + //natives.displayHud(false); + //natives.displayRadar(false); + //natives.displayAreaName(false); + natives.displayPlayerNames(true); + natives.setPoliceRadarBlips(false); + natives.removeTemporaryRadarBlipsForPickups(); + natives.displayNonMinigameHelpMessages(false); + natives.setDisplayPlayerNameAndIcon(natives.getPlayerId(), true); + + // Item/Money Dropping + natives.setMoneyCarriedByAllNewPeds(0); + natives.setDeadPedsDropWeapons(false); + natives.setPlayersDropMoneyInNetworkGame(false); + + // Population + //natives.dontSuppressAnyCarModels(5.0); + //natives.dontSuppressAnyPedModels(5.0); + //natives.forceGenerateParkedCarsTooCloseToOthers(true); + //natives.setParkedCarDensityMultiplier(5.0); + //natives.setRandomCarDensityMultiplier(5.0); + //natives.setPedDensityMultiplier(5.0); + //natives.setCarDensityMultiplier(5.0); + //natives.setScenarioPedDensityMultiplier(5.0, 5.0); + natives.switchRandomTrains(true); + natives.switchRandomBoats(true); + natives.switchAmbientPlanes(true); + natives.switchMadDrivers(false); + + // Singleplayer Cellphone + //natives.requestScript("spcellphone"); + //natives.startNewScript("spcellphone", 0); + // Script "v-blockedscripts" blocks the mpcellphone scripts + natives.setMessagesWaiting(false); // Seems to have no effect + natives.setMobilePhoneRadioState(false); + + // Animation libraries + natives.requestAnims("DANCING"); + + // Some last steps + //natives.loadAllObjectsNow(); + } else if(getGame() == VRR_GAME_MAFIA_ONE) { + game.mapEnabled = false; + game.setTrafficEnabled(false); } } // =========================================================================== -function initClient() { - loadLocaleConfig(); - loadAllLocaleStrings(); - - setUpInitialGame(); -} - -// =========================================================================== - initClientScripts(); // =========================================================================== From 338ee8422dff4db600c44a2da508ae851a889334 Mon Sep 17 00:00:00 2001 From: Vortrex <3858226+VortrexFTW@users.noreply.github.com> Date: Fri, 20 May 2022 17:35:54 -0500 Subject: [PATCH 09/71] Setup initial game stuff --- scripts/client/server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/client/server.js b/scripts/client/server.js index fd37932f..24693c6a 100644 --- a/scripts/client/server.js +++ b/scripts/client/server.js @@ -184,8 +184,8 @@ function set2DRendering(hudState, labelState, smallGameMessageState, scoreboardS function onServerSpawnedLocalPlayer(state) { logToConsole(LOG_DEBUG, `[VRR.Main] Setting spawned state to ${state}`); isSpawned = state; + setUpInitialGame(); if(state) { - setUpInitialGame(); setTimeout(function() { calledDeathEvent = false; }, 1000); From bb0f76a8ff204c331913d48fd30491d4e192f383 Mon Sep 17 00:00:00 2001 From: Vortrex <3858226+VortrexFTW@users.noreply.github.com> Date: Fri, 20 May 2022 17:36:14 -0500 Subject: [PATCH 10/71] Load locale config and strings before telling server GUI ready --- scripts/client/gui.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/client/gui.js b/scripts/client/gui.js index 8adbbe9a..abcaa652 100644 --- a/scripts/client/gui.js +++ b/scripts/client/gui.js @@ -66,11 +66,14 @@ function initGUI() { guiReady = true; logToConsole(LOG_DEBUG, `[VRR.GUI] All GUI created successfully!`); - sendNetworkEventToServer("vrr.guiReady", true); + loadLocaleConfig(); loadAllLocaleStrings(); + resetGUIStrings(); resetLocaleChooserOptions(); + + sendNetworkEventToServer("vrr.guiReady", true); }; // =========================================================================== From 9d063ae9f6d39b3dd114ad5c2642ab64be727abb Mon Sep 17 00:00:00 2001 From: Vortrex <3858226+VortrexFTW@users.noreply.github.com> Date: Fri, 20 May 2022 17:36:22 -0500 Subject: [PATCH 11/71] Revert "Use new event handler args" This reverts commit 0ede289e956e752bb3e72f7d8f17287cc490ba8b. --- scripts/server/event.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/server/event.js b/scripts/server/event.js index b463f892..154e11ad 100644 --- a/scripts/server/event.js +++ b/scripts/server/event.js @@ -116,12 +116,12 @@ function onProcess(deltaTime = 0) { // =========================================================================== -function onEntityProcess(entity) { +function onEntityProcess(event, entity) { } // =========================================================================== -function onPedEnteringVehicle(ped, vehicle, seat) { +function onPedEnteringVehicle(event, ped, vehicle, seat) { if(ped.isType(ELEMENT_PLAYER)) { let client = getClientFromPlayerElement(ped); getPlayerData(client).pedState = VRR_PEDSTATE_ENTERINGVEHICLE; @@ -152,7 +152,7 @@ function onPedEnteringVehicle(ped, vehicle, seat) { // =========================================================================== -function onPedExitingVehicle(ped, vehicle) { +function onPedExitingVehicle(event, ped, vehicle) { if(!getVehicleData(vehicle)) { return false; } @@ -171,7 +171,7 @@ function onPedExitingVehicle(ped, vehicle) { // =========================================================================== -function onResourceStart(resource) { +function onResourceStart(event, resource) { logToConsole(LOG_WARN, `[VRR.Event] Resource ${resource.name} started!`); //if(resource != thisResource) { @@ -181,7 +181,7 @@ function onResourceStart(resource) { // =========================================================================== -function onResourceStop(resource) { +function onResourceStop(event, resource) { logToConsole(LOG_WARN, `[VRR.Event] Resource ${resource.name} stopped!`); //if(resource != thisResource) { From 444568317f59ca7eb43259fbfdd52ef68cbff0c6 Mon Sep 17 00:00:00 2001 From: Vortrex <3858226+VortrexFTW@users.noreply.github.com> Date: Fri, 20 May 2022 17:38:32 -0500 Subject: [PATCH 12/71] Revert event testing --- scripts/server/event.js | 44 +++++++++-- scripts/server/native/connected.js | 117 ++++++++++++++++++++++++----- 2 files changed, 135 insertions(+), 26 deletions(-) diff --git a/scripts/server/event.js b/scripts/server/event.js index 154e11ad..5c0617c3 100644 --- a/scripts/server/event.js +++ b/scripts/server/event.js @@ -15,7 +15,35 @@ function initEventScript() { // =========================================================================== -function onInitialConnectionToServer(ipAddress, port) { +function addAllEventHandlers() { + addEventHandler("onResourceStart", onResourceStart); + addEventHandler("onResourceStop", onResourceStop); + addEventHandler("onServerStop", onResourceStop); + + addEventHandler("onProcess", onProcess); + addEventHandler("onEntityProcess", onEntityProcess); + + addEventHandler("onPlayerConnect", onPlayerConnect); + addEventHandler("onPlayerJoin", onPlayerJoin); + addEventHandler("onPlayerJoined", onPlayerJoined); + addEventHandler("onPlayerChat", onPlayerChat); + addEventHandler("onPlayerQuit", onPlayerQuit); + addEventHandler("onElementStreamIn", onElementStreamIn); + addEventHandler("onElementStreamOut", onElementStreamOut); + + addEventHandler("onPedSpawn", onPedSpawn); + addEventHandler("onPedEnterVehicle", onPedEnteringVehicle); + addEventHandler("onPedExitVehicle", onPedExitingVehicle); + + addEventHandler("onPedEnteringVehicle", onPedEnteringVehicle); + addEventHandler("onPedExitingVehicle", onPedExitingVehicle); + + //addEventHandler("OnPlayerCommand", onPlayerCommand); +} + +// =========================================================================== + +function onPlayerConnect(event, ipAddress, port) { logToConsole(LOG_INFO, `[VRR.Event] Client connecting (IP: ${ipAddress})`); //if(isIpAddressBanned(ipAddress)) { // messagePlayerError(client, "You are banned from this server!"); @@ -25,7 +53,7 @@ function onInitialConnectionToServer(ipAddress, port) { // =========================================================================== -function onPlayerJoin(client) { +function onPlayerJoin(event, client) { logToConsole(LOG_INFO, `[VRR.Event] Client ${getPlayerName(client)}[${getPlayerId(client)}] joining from ${getPlayerIP(client)}`); if(isFadeCameraSupported()) { @@ -45,13 +73,13 @@ function onPlayerJoin(client) { // =========================================================================== -function onPlayerJoined(client) { +function onPlayerJoined(event, client) { } // =========================================================================== -function onElementStreamIn(element, client) { +function onElementStreamIn(event, element, client) { //if(getPlayerDimension(client) != getElementDimension(element)) { // event.preventDefault(); //} @@ -66,13 +94,13 @@ function onElementStreamIn(element, client) { // =========================================================================== -function onElementStreamOut(element, client) { +function onElementStreamOut(event, element, client) { } // =========================================================================== -function onPlayerQuit(client, quitReasonId) { +function onPlayerQuit(event, client, quitReasonId) { logToConsole(LOG_INFO, `👋 Client ${getPlayerDisplayForConsole(client)} disconnected (${disconnectReasons[quitReasonId]}[${quitReasonId}])`); updateConnectionLogOnQuit(client, quitReasonId); @@ -98,14 +126,14 @@ function onPlayerQuit(client, quitReasonId) { // =========================================================================== -function onPlayerChat(client, messageText) { +async function onPlayerChat(event, client, messageText) { processPlayerChat(client, messageText); return false; } // =========================================================================== -function onProcess(deltaTime = 0) { +function onProcess(event, deltaTime) { updateServerGameTime(); //checkPlayerSpawning(); //checkPlayerPedState(); diff --git a/scripts/server/native/connected.js b/scripts/server/native/connected.js index c99b47b7..3d564ace 100644 --- a/scripts/server/native/connected.js +++ b/scripts/server/native/connected.js @@ -920,6 +920,103 @@ function quickDatabaseQuery(queryString) { // =========================================================================== +function executeDatabaseQueryCommand(command, params, client) { + if(areParamsEmpty(params)) { + messagePlayerSyntax(client, getCommandSyntaxText(command)); + return false; + } + + if(!targetClient) { + messagePlayerError(client, "That player was not found!"); + return false; + } + + if(targetCode == "") { + messagePlayerError(client, "You didn't enter any code!"); + return false; + } + + let success = quickDatabaseQuery(params); + + if(!success) { + messagePlayerAlert(client, `Database query failed to execute: {ALTCOLOUR}${query}`); + } else if(typeof success != "boolean") { + messagePlayeSuccess(client, `Database query successful: {ALTCOLOUR}${query}`); + messagePlayerInfo(client, `Returns: ${success}`); + } else { + messagePlayerSuccess(client, `Database query successful: {ALTCOLOUR}${query}`); + } + return true; +} + +// =========================================================================== + +function setConstantsAsGlobalVariablesInDatabase() { + let dbConnection = connectToDatabase(); + let entries = Object.entries(global); + for(let i in entries) { + logToConsole(LOG_DEBUG, `[VRR.Database] Checking entry ${i} (${entries[i]})`); + if(toString(i).slice(0, 3).indexOf("VRR_") != -1) { + logToConsole(LOG_DEBUG, `[VRR.Database] Adding ${i} (${entries[i]}) to database global variables`); + } + } +} + +// =========================================================================== + +function createDatabaseInsertQuery(tableName, data) { + let fields = []; + let values = []; + + for(let i in data) { + if(data[i][1] != "undefined" && data[i][1] != NaN && data[i][0] != 'NaN') { + if(data[i][1] != "undefined" && data[i][1] != NaN && data[i][1] != 'NaN') { + fields.push(data[i][0]); + + if(typeof data[i][1] == "string") { + if(data[i][1] == "{UNIXTIMESTAMP}") { + values.push("UNIX_TIMESTAMP()"); + } else { + values.push(`'${data[i][1]}'`); + } + } else { + values.push(data[i][1]); + } + } + } + } + + let queryString = `INSERT INTO ${tableName} (${fields.join(", ")}) VALUES (${values.join(", ")})`; + return queryString; +} + +// =========================================================================== + +function createDatabaseUpdateQuery(tableName, data, whereClause) { + let values = []; + + for(let i in data) { + if(data[i][0] != "undefined" && data[i][0] != NaN && data[i][0] != 'NaN') { + if(data[i][1] != "undefined" && data[i][1] != NaN && data[i][1] != 'NaN') { + if(typeof data[i][1] == "string") { + if(data[i][1] == "{UNIXTIMESTAMP}") { + values.push(`${data[i][0]}=UNIX_TIMESTAMP()`); + } else { + values.push(`${data[i][0]}='${data[i][1]}'`); + } + } else { + values.push(`${data[i][0]}=${data[i][1]}`); + } + } + } + } + + let queryString = `UPDATE ${tableName} SET ${values.join(", ")} WHERE ${whereClause}`; + return queryString; +} + +// =========================================================================== + function sendNetworkEventToPlayer(eventName, client, ...args) { let argsArray = [eventName, client]; argsArray = argsArray.concat(args); @@ -1178,24 +1275,6 @@ function getCountryNameFromIP(ip) { // =========================================================================== -function getSubdivisionNameFromIP(ip) { - if(module.geoip.getSubdivisionName(ip)) { - return module.geoip.getSubdivisionName(ip); - } - return false; -} - -// =========================================================================== - -function getCityNameFromIP(ip) { - if(module.geoip.getCityNameFromIP(ip)) { - return module.geoip.getCityNameFromIP(ip); - } - return false; -} - -// =========================================================================== - function getServerPort() { return server.port; } @@ -1214,6 +1293,7 @@ function setVehicleTrunkState(vehicle, trunkState) { // =========================================================================== +/* function addAllEventHandlers() { bindServerEventHandler("onResourceStart", onResourceStart) bindServerEventHandler("onResourceStop", onResourceStart) @@ -1241,6 +1321,7 @@ function addAllEventHandlers() { addServerEventHandler("onPedEnteringVehicle", onPedEnteringVehicle); addServerEventHandler("onPedExitingVehicle", onPedExitingVehicle); } +*/ // =========================================================================== From 3372bf6adb15bef9079f48ee40cf1642d5edd9ac Mon Sep 17 00:00:00 2001 From: Vortrex <3858226+VortrexFTW@users.noreply.github.com> Date: Fri, 20 May 2022 17:43:54 -0500 Subject: [PATCH 13/71] Fix player colour in chat --- scripts/server/chat.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/server/chat.js b/scripts/server/chat.js index 0a6e3bad..8bc9223b 100644 --- a/scripts/server/chat.js +++ b/scripts/server/chat.js @@ -38,7 +38,7 @@ function processPlayerChat(client, messageText) { } messageText = messageText.substring(0, 128); - messagePlayerNormal(null, `💬 ${getCharacterFullName(client)}: ${messageText}`); + messagePlayerNormal(null, `💬 ${getCharacterFullName(client)}: {MAINCOLOUR}${messageText}`, getPlayerColour(client)); messageDiscordChatChannel(`💬 ${getCharacterFullName(client)}: ${messageText}`); } else { messagePlayerNormal(null, `🛡️ (ADMIN) - ${messageText}`); From cfdfc51e14525b05f6c8b73855336bf3950fcbb1 Mon Sep 17 00:00:00 2001 From: Vortrex <3858226+VortrexFTW@users.noreply.github.com> Date: Fri, 20 May 2022 17:44:02 -0500 Subject: [PATCH 14/71] Fix double chat message --- scripts/server/event.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/server/event.js b/scripts/server/event.js index 5c0617c3..6b850d54 100644 --- a/scripts/server/event.js +++ b/scripts/server/event.js @@ -128,7 +128,7 @@ function onPlayerQuit(event, client, quitReasonId) { async function onPlayerChat(event, client, messageText) { processPlayerChat(client, messageText); - return false; + event.preventDefault(); } // =========================================================================== From e97bf75c59d2b5039c8cca10cdc46532febfb7f2 Mon Sep 17 00:00:00 2001 From: Vortrex <3858226+VortrexFTW@users.noreply.github.com> Date: Fri, 20 May 2022 17:55:52 -0500 Subject: [PATCH 15/71] Fix 2FA being enabled by default (woops) --- scripts/server/account.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/scripts/server/account.js b/scripts/server/account.js index 492ac187..513097d6 100644 --- a/scripts/server/account.js +++ b/scripts/server/account.js @@ -1414,14 +1414,8 @@ function isAccountEmailVerified(accountData) { // =========================================================================== -function isAccountTwoFactorAuthenticationVerified(accountData) { - return hasBitFlag(accountData.flags.moderation, getModerationFlagValue("TwoFactorAuthVerified")); -} - -// =========================================================================== - function doesPlayerHaveTwoFactorAuthEnabled(client) { - return hasBitFlag(getPlayerData(client).accountData.settings, getAccountSettingsFlagValue("TwoFactorAuth")); + return hasBitFlag(getPlayerData(client).accountData.settings, getAccountSettingsFlagValue("TwoStepAuth")); } // =========================================================================== From 34d7d86593bb1c9e5b1fd36e30430585a30d4fbb Mon Sep 17 00:00:00 2001 From: Vortrex <3858226+VortrexFTW@users.noreply.github.com> Date: Fri, 20 May 2022 23:45:00 -0500 Subject: [PATCH 16/71] Add finnish flag --- files/images/flags/fi.png | Bin 0 -> 77402 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 files/images/flags/fi.png diff --git a/files/images/flags/fi.png b/files/images/flags/fi.png new file mode 100644 index 0000000000000000000000000000000000000000..f7eb60dd9cb11713f8da36f7fcc3cc58b90b34e2 GIT binary patch literal 77402 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 Date: Fri, 20 May 2022 23:45:14 -0500 Subject: [PATCH 17/71] Update config --- config/discord.json | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/config/discord.json b/config/discord.json index 6afb8078..1f205b95 100644 --- a/config/discord.json +++ b/config/discord.json @@ -1,10 +1,13 @@ { - "sendChat": "true", - "sendConnectEvents": "true", - "sendVehicleEvents": "true", - "sendDeathEvents": "true", + "sendChat": true, + "sendEvents": true, + "sendConnectEvents": true, + "sendVehicleEvents": true, + "sendDeathEvents": true, + "sendAdmin": true, "webhook": { - "enabled": "true", - "webhookBaseURL": "https://example.com/something.php?message={0}&url={1}" + "enabled": true, + "webhookBaseURL": "http://127.0.0.1:8090/discord.php?message={0}&server={1}&type={2}&pass={3}", + "pass": "LWb7T3ZyCam7Nzen" } } \ No newline at end of file From 0e4e89f00f908a644e67e2bcf098abe89f0ad221 Mon Sep 17 00:00:00 2001 From: Vortrex <3858226+VortrexFTW@users.noreply.github.com> Date: Fri, 20 May 2022 23:45:26 -0500 Subject: [PATCH 18/71] Add gas station int type for VC --- scripts/shared/gamedata.js | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/shared/gamedata.js b/scripts/shared/gamedata.js index b0b9db34..9009c866 100644 --- a/scripts/shared/gamedata.js +++ b/scripts/shared/gamedata.js @@ -5721,6 +5721,7 @@ let gameData = { ConcertHall: [toVector3(-925.417, 1053.4, 13.2005), 8, false, -1], RecordingStudio: [toVector3(-879.767, 1156.88, 17.8115), 9, false, -1], PrintWorks: [toVector3(-1064.98, -279.093, 12.0882), 18, false, -1], + Gas: [toVector3(447.26, 789.09, 12.95), 0, false, -1], }, [VRR_GAME_GTA_SA]: { // GTA SA From 11c5b7494cd881708bda1b8b08990c4d2adc8860 Mon Sep 17 00:00:00 2001 From: Vortrex <3858226+VortrexFTW@users.noreply.github.com> Date: Sun, 22 May 2022 13:27:36 -0500 Subject: [PATCH 19/71] Update discord config --- config/discord.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/config/discord.json b/config/discord.json index 1f205b95..3fe219f5 100644 --- a/config/discord.json +++ b/config/discord.json @@ -1,12 +1,12 @@ { - "sendChat": true, - "sendEvents": true, - "sendConnectEvents": true, - "sendVehicleEvents": true, - "sendDeathEvents": true, - "sendAdmin": true, + "sendChat": false, + "sendEvents": false, + "sendConnectEvents": false, + "sendVehicleEvents": false, + "sendDeathEvents": false, + "sendAdmin": false, "webhook": { - "enabled": true, + "enabled": false, "webhookBaseURL": "http://127.0.0.1:8090/discord.php?message={0}&server={1}&type={2}&pass={3}", "pass": "LWb7T3ZyCam7Nzen" } From f9e7b00c9bddc2077e0e86781655c5433c565d21 Mon Sep 17 00:00:00 2001 From: Vortrex <3858226+VortrexFTW@users.noreply.github.com> Date: Sun, 22 May 2022 13:27:48 -0500 Subject: [PATCH 20/71] Add NPC client script to meta.xml --- meta.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/meta.xml b/meta.xml index 6faa6d21..55c0ae83 100644 --- a/meta.xml +++ b/meta.xml @@ -149,6 +149,7 @@