Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 23/04/20 in all areas

  1. مبارك عليكم الشهر الكريم , وكل عام وانتم بخير
    2 points
  2. Advanced Gaming Roleplay - Innovation With Every Interaction About We hear you, “another role-play server? Guess it’s an edit again” – but that’s not the case. Advanced Gaming is, back again, getting things ready to provide our loyal players with what we do best: next level scripts with a nice balance between realism and fun in each and every script. With at least 10 years of experience on the MTA platform and countless sleepless nights, we present to you: A role-play script written from scratch! If you remember us from back in 2015, hi there! If you don’t, no worries. We’ve been here before and life got in the way, we took over a project (SAPD:FR) which required less of our attention and made it possible for us to get a few things straight as a community but also in our personal lives. Now that we're back we're more than ready to finally put full dedication into this project and bring you guys a brand new twist into the MTA:SA Roleplay scene with a brand new script with some never before seen features, so without further ado let's go on with what we have to offer! Features Since a role-play gamemode requires constant changes, new features and removal of features... we’re not going to list all of our features (we know, it’s a lot, we all hate reading). To give you a glance of what we got to offer, we would love to invite you to our server and just give it a shot (when it’s released), if you want to know what our deal is, here’s a glance of what we got to offer: Realistic Vehicle System: We've put countless hours into the details of our vehicle system which allowed us to implement numerous hidden features which we will not yet release to the public, they'll sure blow your mind when you find out about all of it. After hours and hours of study and at least 4 years of experience we've made a perfect balance between the game's engine and physics system to carefully recreate real-life vehicle models in-game regarding handlings which includes top speeds, accelerations, realistic fuel tank capacities and consumptions. FPS-friendly UI: We’ve written our very own custom GUI (graphical user interface) system to create windows, labels, images and what not with ease without frying your CPU and GPU. Balanced Economy: Our economy has been built in a way to promote player to player interaction. How so? Simple, you can forget about getting rich by doing the default jobs, if you want to get yourself some more cash then you're simply going to have to join one of many player ran companies or perhaps even go down the dark path and join a gang? That's up to you, we won't tell anyone and especially not the cops! Jobs and companies, how do they work?: Simple, the entire server will be running on companies instead of default jobs. The default jobs have been made useful for new players (deliveries from our very own storage units, a government funded bus or a taxi driver) but the real fun starts with real people, in real companies. Hurr Durr I’m a big guy on another server, can I..: let me stop you right there, we don’t care about your previous progress on other servers, your entire track list of bans and offenses. Everyone is fair and square as a new player on our server, no advantages and no labels. Items and products: From snacks to meals, from coca-cola cans to glasses of wine, property keys to theory certificates. We've got it all built in with effect, if any. Users are able to purchase some items, other items have to be earned. You'll get a theory certificate after completing your theory for a driving license, a receipt after purchasing something or an empty can after drinking your coca cola. Illegal activities / gameplay: It’s a tough scene, you want to start your own gang or clap your neighbors, we get it. Since the illegal role-play defines or breaks a server, we’re taking close care of the scene with constant monitoring with a team of people. Guns will be distributed passively, drugs will be taken care of (and tested before distribution…). In the trash(bin)! Attention to detail in every script, want to get rid of the annoying receipt you recieved from the store and don't even need it? Simply throw it into one of the many trash bins present around town and let the garbage collectors take care of your junk for you! Police & Criminals: Tired of officers having super cars, heavy duty weapons like AR-15 or server owner-run departments? So are we, which we why we've given the department normal vehicles with fitting handlings which decreases their advantage, increases the experience of both roleplaying as well as the thrill of being a criminal or officer. They're forced to think tactically and plan their actions, their equipment is distributed on a monitored basis to only the higher ranks in the department where we see fit. Giving the illegal roleplay scene space to grow and feel alive And much more!: You want more? We got more! Two number 9s, a number 9 large, a number 6... oh, you meant features? We have a tight schedule of daily bugfixes, weekly updates and weekly announcements regarding progress. Want to see what we're made of? Check us out.. when we've launched! (sorry, I know..) Screenshots Note: This is still Work in Progress, final product may look different! DMV System Trash Bin System Shopping Vehicle Plate System Other Where to find us? Since we’re still getting things ready, you’re more than welcome to join us on our discord server. We’re working on the forums, lots of script changes and making sure that you’re getting something new and fun, rather than just another edit. Server IP: Coming soon! Website: Coming soon! Forums: https://forum.ag-rp.org/ Discord: http://discord.ag-rp.org/ Launch Date: Upcoming Winter (Q4 2024 or Q1 2025) Note: This thread was written using the Dark Theme, if you spot any issues on the default theme please let us know!
    1 point
  3. O problema está na table. Você fez uma table para funcionar com somente 1 ped, dessa forma não precisaria de loop nenhum. PedLoja = {0, 1398.5822753906, -1630.8250732422, 14.102245330811, 88} -- id, x, y, z, rotZ (não aceita outros Peds, pois precisaria de uma sub-table. PedLoja = { {0, 1398.5822753906, -1630.8250732422, 14.102245330811, 88}, -- Agora sim, cada ped separado em uma sub-table. {0, 1398.5822753906, -1630.8250732422, 14.102245330811, 88} } -- Se usar a primeira tabela, o loop vai rodar 5 vezes. Na primeira vez, v = 0, na segunda vez, v = 1398.5822... e assim por diante. for i, v in ipairs(PedLoja) do ped = createPed(v[1], v[2], v[3], v[4], v[5]) -- erro, não é possível indexar o número 0[1]. end -- Se usar a segunda tabela, o loop vai rodar duas vezes, pois no exemplo a tabela tem 2 itens. Sendo cada item uma sub-table. for i, v in ipairs(PedLoja) do -- Na primeira vez, v = {0, 1398.5822753906, -1630.82507324422, 14.102245330811, 88} ped = createPed(v[1], v[2], v[3], v[4], v[5]) -- Agora sim, v[1] = 0. Pois é o primeiro item da sub-table v, v[2] = 1398.5822753906, e assim por diante. end
    1 point
  4. رمضان كريم عالجميع , !
    1 point
  5. فكره جميله استمر في تطوير السكريبت
    1 point
  6. Estranho... era pra ter funcionado desta forma.
    1 point
  7. Trancar veículo por fora: setVehicleLocked Trancar veículo por dentro: use cancelEvent() no evento onVehicleStartExit.
    1 point
  8. Yeah... about that: https://github.com/multitheftauto/mtasa-blue/issues/1377
    1 point
  9. I'm just leaving this here https://youtu.be/QXkUWqdpWo0
    1 point
  10. @PedroFaria seu pedido foi recusado. Infelizmente você não atende aos critérios necessários para ser da Staff do MTA. Entre os requisitos, destacamos: Conhecer todas as regras do fórum. (falhou) O mínimo que todo usuário deveria fazer. Consideramos que você não conhece as regras do fórum, pois criou o tópico no local incorreto e na linguagem incorreta daquela seção. A propósito, você pode ver as regras aqui. Ser ativo no fórum/discord do MTA (falhou/falhou) Você acabou de criar sua conta aqui no fórum, é necessário ser ativo no fórum, ajudar os usuários, ter um número razoável de mensagens que ajudaram os usuários. Sua conta não é membro no Discord do MTA. Ter habilidade de MTA Scripting (falhou) É necessário saber programar resources para o MTA no nível intermediário ou avançado, assim você tem capacidade para ajudar nas dúvidas de scripting dos outros usuários, responder dúvidas sobre os resources, etc. Ter habilidade para corrigir bugs do MTA (OK) É necessário saber resolver bugs de crashes e fechamentos inesperados do programa para poder ajudar os usuários a resolvê-los. Saber falar em inglês (falhou) Inglês intermediário ou avançado é necessário para manter a comunicação com o resto da Staff. Como vc solicitou em português numa seção inglesa, consideramos que você não sabe inglês. Ter alta habilidade de comunicação. (falhou) Analisamos a forma com que você se comunica de maneira escrita (seu tópico) e verbal (seu canal) e ela pode não ser compreendida por todos os usuários, principalmente por aqueles que utilizam linguagem mais culta. Além de diversos erros gramaticais na escrita. Para ser moderador é necessário linguagem formal enquanto estiver atuando como moderador. Critérios opcionais, não são necessários para se tornar moderador, mas fazem de você um moderador mais capaz. Ter habilidade de Modelagem 3D para GTA SA Saber modelar objetos, convertê-los para o formato que o MTA entende e saber como instalá-los num servidor de MTA. Para poder ajudar os usuários com dúvidas sobre modelagem. Ter habilidade de animação para GTA SA O mesmo que o critério acima. Ter habilidade com Host e Servidores. Saber criar um servidor de MTA tanto público quanto local e instalar resources nele. Ter habilidade com Banco de dados. Saber criar e gerenciar bancos de dados no MTA, tanto internal.db quanto registry.db e custom.db. Em SQLite ou MySQL. Ter habilidade em programação de Shaders. Utilizado em scripting no MTA. Os seguintes critérios não puderam ser analisados: É necessário ter inteligência emocional. Isto é, não se deixar levar por emoções e agir de maneira lógica. Se estiver ocorrendo algum tipo de conflito, o moderador precisa saber resolver sem brigar com os usuários. Quando algum usuário tenta provocar um moderador, ele deve saber agir de maneira séria sem entrar na provocação e sem abusar ao dar punições severas demais. Também entra neste critério, a capacidade de liderança. Outra coisa a ser considerada é que um moderador precisa ser querido em sua comunidade, isto é, os usuários gostam do trabalho de moderação dele. Ser uma pessoa que ajuda a todos sem ficar julgando ou se achando melhor que os outros. As pessoas não gostam de pedir ajuda para pessoas arrogantes ou desumildes. Vale lembrar que os moderadores não mandam nos usuários, eles apenas citam as regras e pedem para os usuários segui-las. Espero que entenda os motivos acima listados. Um abraço.
    1 point
  11. Introduction This guide intends to teach 3ds Max users the basics, and more advanced ways, of working with (environment) animations and rigs to enhance atmosphere of the game. Animations, moving images or however you name it, they are there, everywhere in games. GTA San Andreas and its predecessors do utilize animations, however to a limited extent; most environment models were kept frozen in the dark days of PC games to save computer resources. Few objects like mechanical oil drills and food store mascots are animated. Today we're past those days and have advanced in technology to exceed limits, and integrate known resource demanding features into our games without much of a performance impact. It's important to keep in mind that the Renderware engine has various limitations associated with models/3D assets. These limitations do apply very harsh on what this guide covers. Table of contents Demo reel - Custom rigs and animations Prerequisites Recommended Character skeleton Basics to skinning Simulation conversion to bones workflow Demo reel - Custom rigs and animations Prerequisites Must have Autodesk 3ds Max of any desired version. 2017+ needed to speed up cloth rigging. Must have Kam's Maxscripts. Must have Animation Manager tool. Recommended You can use virtually any modeling or animation software e.g Blender or Cinema 4D to do 90% of the work. 3ds Max is required to export to GTA formats IFP and DFF. It's recommended to have experience in game modding and 3D modeling software of choice. It's highly recommended to use Shagg_E's anim/char helper for easy setup of bones and animation. For those who calculate bone hierarchy, add SPunknown = 4 to Root bone's object property. It's possible to use ARKTOS Tools VertexToBones for converting vertex-baked animations to bone animations. It should be noted however that this does not create bone hierarchy the desired way. It creates bones that aren't linked to each other, only the root, so it generates the animation for each bone to move, not rotate, and thus the majority of the animation relies on position keys, where SA animations only support rotation keys. Character skeleton San Andreas uses bones for its character animations. These bones are linked together, forming a skeleton with joints that can be rotated. Motion capture data can be transferred onto bones to ease the creation of character and more detailed animations. This was done for GTA cutscene models primarily. On 3ds Max, bones can be virtually anything, ranging from camera's to dummies. When exporting to GTA formats, the bone objects are automatically converted into dummies (helpers). This is how bones worked in early 2000's and/or 90's. If you desire to convert a helper or model into a dummy, follow this CGTalk post's instructions. For creating a skeleton, place all bones around the inner mesh of the model. Everything must be in a hierarchy and bones can have as many children as desired. Naming conventions are only recommended for easier overview, however they can be named according to one's desire e.g Bone_0 - Bone_12. To have the bones show the way the imported skins ones do, head to Display Panel under Links, and check both boxes. Make sure this is done for every bone. This will display joint links in correct hierarchy in viewport. Some games may use vertex baked animations, thus negating the need for bones. This is often seen on models such as all types of clothing. For game engines that do not support vertex animation or dynamic cloth, bones will be used instead. Below is a spoiler of valid bone ID's for regular ped models. There are 31 functional bone ID's, 32 with Root included. Once again, bones does not have to use same naming convenience as presented below. They also don't have to use same order of ID's or bone hierarchy, so for instance, Pelvis and Jaw ID's can be exchanged, as long as Root ID = 0. Or, the Head bone can be child of R Hand, not necessarily the Neck. Basics to skinning Although not hugely covered in this guide; Skinned models use a different system from vehicles and map objects. Besides having their 3D mesh, they have a skeleton. A skeleton is made of bone joints linked to each other, so that when the shoulder moves, the lower arm does as well. A bone will only affect part of the mesh that it's weighed to. If you weigh all vertices of the player's hat to the Head bone, then the hat will move according to the head bone. If you weigh all of the hat's vertices to the Pelvis bone, then the hat will stay static in air while the head moves. All vertices must be affiliated with at least one bone. Although a skin can have 32 bones, it'll work just fine if you weigh all vertices to only 3 bones. Not all bones require to be affiliated with a vertex. If you try to export a skinned model where 1 vertex is not weighted, the exporter will reject your request to export or your game will crash. A skin can have as few as 2 bones (root and children), so if your model has only 8 vertices, there's really no point having 32 bones when only 8 are functional. Below image (credits: IamObama) shows the basics to weighing joints to a character mesh. While this doesn't show all of the bones, it can prove very helpful as a lookup while weighing vertices to your skin. You can also load in a GTA ped to reference its miscellaneous bones e.g facial and fingers. Simulation conversion to bones workflow Note: The following process or techniques may apply for Blender/Maya users as well. 3ds Max has inbuilt cloth physics. These can simulate with help of gravity, wind, etc. and is a great way to generate animations for cloth-like elements. With help of tracking markers, you can track a bone to follow the orientation of a marker, which can then be baked into keyframes. The process is incredibly simple, you just have to understand the basics of cloth physics and object constraints. Video tutorial: Text tutorial: Create your model simulation model Be mindful about geometry and bone count when creating your model. The fewer polygons, the more rough the animation will be. The more polygons, the more cloth-like it'll look. The more bones with keyframes, the larger the file size (lots) of your IFP. For flags, one bone should never have full influence of a vertex. A vertex should always be affected by 2 or more different bones for a nice blend. It may be an idea to load in a GTA model so that you can somewhat reference the scale, if you don't have access to a dimension ruler. For the purpose of the guide (assuming you follow along the video), create a plane (primitive) with 8 segments in both directions. Make it length: 1,280 meter, width: 848 millimeters. Prepare your simulation model Convert the primitive to either editable mesh or editable poly. Polygon mode is more sustainable for modeling, but it needs to be in Mesh mode once you export it. Enable angle snap (default: A) and rotate it so it stands vertically. On the Hierarchy tab, reset transform to reset rotation values to 0. Add a Cloth modifier to your model. Go to sub-object level group of cloth modifier. Select the vertices that you want to act as solid points (e.g ones attached to flagpole). Click Drag as group mode. Simulate the model Head to Create tab > Space warps. Create a wind. Its position doesn't matter, only the rotation which represents the direction, matters. The default settings of the wind are usually too high. Try setting strength: 0,12, Turbulence: 0,8, Frequency: 0,05. Select the simulation model. On the cloth modifier, select Cloth forces and choose the wind. On the same interface, select Object properties. Select the model's name, then select Cloth, then pick a preset of your preference. Try silk or cashmere. You can optionally use Edge springs. I'm not fully sure what it represents, but I believe it's got to do with keeping the edges intact and not stretching them (which you don't want for bone animations). Try simulating the model. You may want to increase the timeline to a more reasonable duration, but be cautious as the higher it goes, the more your potato will suffer. If you aren't happy with the simulation, erase it and it'll let you try resimulate it. If you're unlucky enough to get the infamous error message dT increased/decreased you may want to start a new 3ds max project file... Create your real model In most cases you'll simply clone the simulated model, name the clone skin_mesh (name can be anything, but for easier scene management, name it something related). Remove the cloth modifier from your "real" model. For the simulated model, select the create keys button in cloth modifier. This will collapse your modifier and bake animation into keyframes, can NOT be reverted! Move the simulated model a bit away from the skin mesh, so that it'll make it significantly easier for a later procedure. Make sure to center the skin mesh to 0,0,0 or you'll get real trouble later. Convert the model to editable mesh. When you need to export later, it doesn't want it as editable poly. Create bones for your model Apply a Skin modifier to your model. This is used to carry the skeleton bones for later on. To make the bone creation and setup a whole lot easier, run Shagg_E's animation tools (link in top of thread). If you simply choose to create a set of dummies, his script won't recognise them as bones and you'll have to add all the object properties manually. So do yourself a favor and use his tool. Navigate to the Char helper section on the interface. Click create root, this'll create a dummy in center of the scene, assuming it corresponds to the model's center. Click create children, this'll create a dummy linked to the root. Drag it out from the model and title it "Pelvis" (again naming doesn't matter, can be "sampsucks" and samp modders will know once they load in your skin!). This bone is usually needed to keep part of the model's verts (abbreviation for vertices) in place, e.g flag pole attachments. Keep creating children bones until you reach your desired amount. It's a good idea to enable snaps (to vertex) so that you can magnet position bones directly onto the vertices. Select any bone in hierarchy and click Calculate for all hierarchy. This calculates bone ID's, bone index and bone type. If you were to set this yourself, you would need to understand how they work, on top of this it would be very time consuming. So we can all thank Shagg_E for making this wonderful script. Select the root bone, head to object properties, in user defined add to the string SPunknown = 4 or else your skin won't work as intended. Prepare motion tracking Create motion tracking markers. Head to create tab -> helpers -> point helper. Keep their scale at .2 or so. Spawn the amount equivalent to the amount of functional bones (excluding root and pelvis). Select all of the point helpers. Under the Animation rollout, select constraint -> attachment constraint -> select the simulated model. Go to Motion tab. The positions are based on triangle points, so you have to insert either 0 or 1 on A and B, then pick a face. It'll make more sense if you watch the video. You need to position the point helpers to the vertices where the bones are positioned at. I don't know of any more efficient way of doing this. On the model's skin modifier, add all functional bones (except root and pelvis). If you're running Max 2017+, you can use Voxel Solver to automate rigging (rough solution) and works great for simulated models. Use setting: falloff 0,2, max influence 3, max resolution 128. Try rotating some bones and hopefully they should affect the skin mesh. Now add the rest of the bones which you didn't rig to the model. If the model is a flag, then select a few vertices that you want to be solid in place, and weight those to pelvis at 100 weight. Track the motion of the simulated model Select all point helpers, click E for rotate, click F12 for rotate transforms and reset all 3 axis to 0. If you don't, some of your bones will be rotated massively (undesired). Apply an orientation constraint to all of your bones, 1 by 1, to their point helper counterpart. This tracks the rotations of the point helpers onto the dummy bones. You can add a shortcut to speed this up a tad... Select all the functional bones. Go to motion tab, click trajectories. This is where you bake the orientation constraints into keyframes for your bone animation. Settings below. Start time = 0 by default, or the first frame of your timeline End time = last frame of timeline OR whenever you desire Samples = amount of keys for the animation. If Start = 0, End = 1500, you can add 1500 samples to have 1500 keys generated, or 150 to have 150 keys generated. Don't use much more than 200 samples or you'll get in serious trouble. You'll have to optimize your keyframes at some point. You can change the keyframe tangent mode (i prefer auto or spline) to change the velocity of the keyframes. Once everything is set, click collapse. This generates keyframes. Animation is now made. Making the cloth simulation seamless/loop is another story. Finishing up Custom model and custom IFP is essentially done now. In order to export animation from 3ds Max, simply select all bones, find an IFP file on your computer and clone it, on 3ds Max run the IFP script, click "load IFP" and then save your custom animation to the cloned IFP file. You can remove excess IFP animations from the IFP archive by using Animation Manager Tool. Have fun with it! There was a recent update where MTA made a couple dozens of unused ped ID's available for servers to use. This is what inspired me to make this guide, since you could use those ID's to "add" new models, of which could be environment models to bring life to the world. Custom IFP's can simply be added into the server, they don't need to replace existing ID's. It's important to note that by changing bone ID's, you change the way that the custom model reacts to default animations. As such, models like banners will appear incredibly distorted. In those cases it's suggested to create some idle, walkstyle and run animations. It's also important to note that you can create LOD animations, since there's really no limit for how many IFP's a server can have. LOD animations may be very handy for animations that have highly dense simulations applied, so the further away the player goes, the less keyframes will be processed.
    1 point
  12. Lua tutorial for absolute beginners This tutorial can also be viewed on GitHub This tutorial aims to teach the Lua scripting language to those with 0 previous experience with programming / scripting. This guide will start with explaining some Lua concepts, and will later on move to explaining MTA:SA specific concepts. Table of contents: Lua Scripts Variables Data types Operators If statements Functions Return values scopes & locals For loops Tables Iterators (pairs/ipairs) Callbacks Anonymous functions MTA Server & Resources Server vs client MTA functions & wiki Elements (userdata) Command handlers Event handlers predefined globals server <-> client communication Now what? Lua This part of the tutorial discusses Lua itself. No MTA-specific concepts will be discussed here. Scripts The Lua scripting language is a language which is interpreted by a "Lua interpreter". MTA:SA's resources use such an interpreter to run Lua code. For the beginning of this tutorial you can use the online Lua interpreter available on https://www.Lua.org/demo.html. Any code in the Lua part of the tutorial can be run on this website. Lua is written in "plain text". This means it exists of regular characters like any other text you are writing. To edit Lua files you will require a text editor. You could use Notepad, but there are many far better alternatives. My personal favourite is Visual Studio Code, but there are many other good options, to name a few: Atom, sublime text, notepad++ Lua files are usually saved with the .Lua file extension Variables The first concept we're going to discuss is variables. Variables allow you to store a value in your code. For example: x = 10 print(x) print(x) will output the value of the x variable. We will get into what exactly print is later in the tutorial. Variables can have any name you want, as long as you follow some specific rules. variable names must start with a letter (lower or upper case), or an underscore (_) variable names may contain letters (lower and upper case), numbers and underscores. x = 10 y = 20 z = 30 print(x) print(y) print(z) The convention in Lua is to name your variables in "camelCase". This means if a variable exists of multiple words you start every word with a capital letter, except for the first one. camelCaseVariable = 5 Data types So far we've seen variables used to store numeric values, but there are many different types of data Lua can handle. These are: number Any numeric value string A piece of text, a string is surrounded by " or '. For example "Hello world" or 'Hello world' boolean A boolean is a data type that has only 2 options, true and false. nil nil is a value indicating nothing. It's the absence of a value. (Not the be confused with 0) table Tables will be discussed later in the tutorial userdata Userdata will be discussed later in the tutorial function Functions will be discussed later in the tutorial thread Threads are out of scope for this tutorial and won't be discussed So we can use these different data types, and store them in variables: numberVariable = 10 stringVariable = "Hello world" booleanVariable = true nilVariable = nil Operators Operators are symbols in Lua which can be used to do "things" with variables. Here's a list of operators and an example for each: + operator Adds two values together x = 10 + 10 print(x) y = 20 print(y) z = x + y print(z) - operator Subtracts a value from another value x = 10 - 10 print(x) * operator Multiplies two values x = 10 * 10 print(x) / operator Divides a value by another value x = 10 / 10 print(x) % operator This is the "modulo" operator. This will divide a value by another, and return the leftover. x = 10 % 4 print(x) The result of this is 2 and operator The and operator will return true if both variables are "truthy". Otherwise it returns false (A "truthy" value is anything except for false and nil) x = true and false print(x) y = true and true print(y) or operator The and operator will return true if one of the variables are "truthy". Otherwise it returns false x = true or false print(x) y = false or false print(y) == operator The == (equals) operator will return true if both of the variables are the same. Otherwise it returns false x = "hey there" == "hello there" print(x) y = 150 == 150 print(y) ~= operator The ~= (does not equal) operator will return true if both variables are not the same. Otherwise it returns false x = "hey there" ~= "hello there" print(x) y = 150 ~= 150 print(y) > operator The > (greater than) operator will return true if the first value is greater than the second value. Otherwise it returns false x = 10 > 5 print(x) y = 10 > 15 print(y) y = 10 > 10 print(y) >= operator The >= (greater than or equals) operator will return true if the first value is greater than, or equal to, the second value. Otherwise it returns false x = 10 > 5 print(x) y = 10 > 15 print(y) y = 10 > 10 print(y) < operator The < (less than) operator will return true if the first value is less than the second value. Otherwise it returns false x = 10 < 5 print(x) y = 10 < 15 print(y) y = 10 < 10 print(y) <= operator The <= (less than or equals) operator will return true if the first value is less than, or equal to, the second value. Otherwise it returns false x = 10 < 5 print(x) y = 10 < 15 print(y) y = 10 < 10 print(y) .. operator The .. (string concatanation) operator allows you to add two strings together. x = "Hello" z = "World!" combined = x .. " " .. z print(combined) If statements An if statement allows your code to decide to do something, or not. Depending on a value. Often times if statements are used in combination with some of the above operators. An if statement is written as : if <expression> then <code> end x = 10 if x > 5 then print("X is higher than 5") end Any code between then and end will only be executed when the expression is true. You might also have noticed that the code between the then and end is moved over a bit to the right. This is called "indentation". Whenever we open a new scope (scopes will be discussed later in the tutorial) we move our code to the right. This is usually done by either a tab or several spaces . Many code editors will convert a tab to spaces. Else Within an if statement, you can also add an else block. The code in such a block will be executed when the code in the if block is not executed. x = 10 if x > 5 then print("X is higher than 5") else print("X is not higher than 5") end Elseif If you want to do multiple if statements, you can use an elseif: x = 15 if x > 10 then print("X is higher than 10") end if x > 5 then print("X is higher than 5") end x = 15 if x > 10 then print("X is higher than 10") elseif x > 5 then print("X is higher than 5") end The difference between the first example and the second is that if x is higher than 10 in the first example both lines "X is higher than 10" and "X is higher than 5" will be output. Whilst in the second example only "X is higher than 10" will be output. And if statement must always start with an if, can contain multiple elseifs, and may only have one else. name = "NanoBob" if name == "NanoBob" then print("Hello world!") elseif name == "Brophy" then print("Black 123") elseif name == "Tombaa" then print("Stupid") else print("I have no idea") end Functions Functions allow you to write less code, by reusing pieces of code. The syntax to create a function is function <name>(<parameters>) <code> end function foo() print("Hello world #1") print("Hello world #2") end In order to execute code in the function, you "call" the function. You do this by writing the function name followed by (). function foo() print("Hello world #1") print("Hello world #2") end foo() foo() foo() Functions also allow you to send a variable to the function, for it to do something with. This is what's called a function parameter. Function parameters are defined in the brackets () after the function name. function foo(x) print(x) end foo(10) foo("50") You may notice that this looks a lot like the print() we have been using. This is because print is a built-in Lua function. Return values A function not only can execute code, it can also give something back to where it was called. This is called a return value. In order to return something from a function you use the return keyword. function foo() return 10 end x = foo() print(x) print(foo()) Just like in an if statement, all code within a function is indented. Now let's combine everything we have learnt so far: function foo(x) if x > 10 then return "X is higher than 10" elseif x > 5 then return "X is higher than 5" else return "X is not higher than 5" end end y = foo(15) print(y) print(foo(10)) print(foo(0)) Scopes & locals We quickly encountered scopes before, and said we indent our code whenever we enter a new scope. But scopes allow you to do more than that. Most importantly, "local" variables. A local variable is only available in the scope it was defined in (or scopes that were created from within that scope) You can create a new scope using a do block (functions and if statements also have their own scope). do local x = 5 print(x) end print(x) do local x = 5 do local y = 10 print(x) print(y) end print(y) end For loops (For) loops are ways in scripting / programming to have code executed multiple times, without having to write the same thing multiple times. An example of such a loop: for i = 1, 10 do print(i) end The first part : i = 1, 10 defines a variable called i. Which start at 1. This will be incremented by 1, until it reaches 10. The code within the loop then can use this variable. You can also increment with a different number than 1 (including negative numbers) using this construct. for i = 20, 0, -2 do print(i) end This code sample will start with i at 20, and keep adding -2 (thus subtracting 2), until it reaches 0 Tables Tables are a datatype in Lua which allows for lists of things. Here's an example: x = { [1] = 100, [2] = 200, [3] = 300 } print(x[1]) print(x[2]) print(x[3]) Tables consist of key/value pairs. In the example above the key 1, has the value 100, 2 has the value 200, and 3 has the value 300. You can get the value in a table, by putting the key in between square brackets []. Like in print(x[1]). x = { 100, 200, 300 } print(x[1]) print(x[2]) print(x[3]) You can choose to not include the keys in a table. Doing so will automatically add numbers as keys, starting at 1. So the above example would be the exact same as the first example. Table keys and values can be of any data type, including other tables. This allows you to create (very) complex table structures. t = { [1] = { 100, 200, 300 }, ["x"] = 100, [true] = "something" } print(t["x"]) print(t[true]) print(t[1][1]) print(t[1][2]) print(t[1][3]) When using a string as the key in a table, you can leave out the square brackets and the quotes. This goes for both when defining the table, and for indexing it (getting a value from it). For example t = { x = 100, y = 200, z = 300 } print(t.x) print(t.y) print(t.z) A table's values can be modified / set after creating the table as well. t = {} t[1] = 10 t[2] = 20 t[3] = 30 t["x"] = "banana" t[true] = false t.x = "banana" When using tables you will often want to add something to the "end" of a table. This is most common when you are using tables with numeric keys. In order to do this you can use a # to get the amount of items currently in the table. t = { 10, 20, 30 } t[#t + 1] = 40 This will store the value 40 on the key 4 , because #t is 3. Iterators (pairs/ipairs) Iterators are a mechanism that allow you to make a loop, which goes over a set of values. Writing your own iterators won't be discussed in this tutorial. But there are two functions which are often used to create an iterator to iterate over a table. These are pairs and ipairs. t = { 10, 20, 30, 40, 50 } for key, value in ipairs(t) do print(key, value) end The difference between pairs and ipairs is the order in which the key/value pairs are iterated over, and which of the key/value pairs are iterated over. Where ipairs will always use numeric keys, starting at 1, and going up by 1 every time, until there is no entry in the table. This also means it won't iterate over anything key that is not numeric. t = { ["x"] = 5, [1] = 10, [2] = 20, [3] = 30, [5] = 50, } for key, value in ipairs(t) do print(key, value) end A pairs loop will iterate over any value in a table, but the order is not guaranteed. Meaning that between different runs of the script the order could be different. t = { ["x"] = 5, [1] = 10, [2] = 20, [3] = 30, [5] = 50, } for key, value in pairs(t) do print(key, value) end Callbacks Callbacks are when you pass a function as an argument to another function. To have the function you passed be called later on. This is used often within MTA. An example of a Lua function which uses a callback is table.sort. table.sort will sort a tables values, by default these values are sorted numerically. But you can use a callback to change this behaviour. values = { 5, 4, 3, 6, 8, 1, 2, 9, 7 } function sortFunction(a, b) return b > a end function reverseSortFunction(a, b) return a > b end table.sort(values, sortFunction) print("Sorted: ") for _, v in ipairs(values) do print(v) end table.sort(values, reverseSortFunction) print("\nReverse sorted: ") for _, v in ipairs(values) do print(v) end In the first call to table.sort (table.sort(values, sortFunction)) you can see the sortFunction function is passed as second argument to the function. Note that we don't write sortFunction() here (notice the brackets difference) because that would call the sortFunction function, and pass its return value to table.sort. table.sort will then call this function when it compares two different values, this function should return true or false depending on whether its second argument (b in this case) is larger than it's first argument (a), in the context of sorting. Anonymous functions It is also possible to use an "anonymous function" when passing a callback to a function. values = { 5, 4, 3, 6, 8, 1, 2, 9, 7 } table.sort(values, function(a, b) return b > a end) print("Sorted: ") for _, v in ipairs(values) do print(v) end table.sort(values, function(a, b) return a > b end) print("\nReverse sorted: ") for _, v in ipairs(values) do print(v) end MTA This part of the tutorial discusses MTA specific constructs. Code in this part of the tutorial won't run in the online Lua interpreter. You will need to set up a (local) server for this. Server & Resources By default when installing MTA:SA a server is installed as well. This server is located in the "server" directory of your MTA directory. This is usually at C:\Program Files (x86)\MTA San Andreas 1.5\server. This directory contains an MTA server.exe file, running this file will start a server. Scripts on a server are grouped by resources, a single resource can consist of multiple script files and other assets such as images, sounds, fonts, mods and more. Resources are placed in your mods\deathmatch\resources folder in your server folder. A resource is always in its own directory. A resource must always have a single meta.xml file. This file tells the server (among others) what script files to load. A typical meta.xml file looks like this: <meta> <script src="vehicleSystem.Lua" type="server"/> <script src="vehicleMods.Lua" type="client"/> </meta> You will need an entry for every .Lua file you want to have executed on the server. You can start a resource by typing start <resource name> in the server console (the window that opened when you started MTA Server.exe). The resource name is the name of the directory your meta.xml is in. (This may not have spaces). Starting a resource will start running the Lua scripts, if you've changed your scripts you will need to restart the resource for the changes to take effect. (restart <resource name>). Server vs client Lua code can be executed in one of two places. The server, or the client. Server sided scripts are executed on the actual machine that is running the MTA server.exe process. Client sided scripts are executed on the computer of every player that connects to your server. Server sided and client sided scripts have a distinct difference in responsibility and possibility. Some functions for example are only available on the client, whilst others are available only on the server (and many on both). Note: Some of these functions which are available both server sided and client sided are different on server and client MTA functions & wiki In plain Lua there's not much you can do to affect a game engine like MTA:SA. This is why MTA:SA offers a (large) list of functions available for you to use in your scripts which interact with the actual GTA world. You can find a list of all of these, what they do and how to use them on the MTA wiki. Server sided functions Client sided functions shared functions An example of such a function is createObject(). This function will create a physical object in the game. The wiki page contains information on how to use it (what arguments it expects, and in what order). And often shows an example of how to use it. createObject(1337, 0, 0, 3) Elements (userdata) At the start of this tutorial we quickly mentioned userdata data types. These are data types configurable by the implementation of Lua. In this case, MTA. MTA uses userdata to represent "elements". Many things in MTA are elements, like objects, markers, peds, players, user interfaces, vehicles, etc. On the MTA wiki you will notice many functions either return elements, or require an element as arguments. A list of different types of elements can be found on the MTA wiki. Elements also have a hierarchical structure to them. Elements can have a "parent", and multiple "children". This will result in a tree of elements, the element at the top of this tree (and thus the grandparent of all elements) is called the root element. Command handlers Often times in MTA you want certain things to happen when a player enters a command. This is done using command handlers, command handlers use a callback, which we previously discussed. The wiki contains a page for the addCommandHandler() function. For this example we will be using the server side version. function handler(player, command, argument) outputChatBox("You entered " .. command) if argument ~= nil then outputChatBox("You used the argument " .. argument, player) end end addCommandHandler("banana", handler) This example also uses the outputChatBox() function, this will output a piece of text to the chat.(in this case, only for the player who executed the command) The callback function passed to addCommandHandler will be called every time a player uses the /banana command ingame. Event handlers Besides having your script do things when a user executes a command you likely want the game to respond to many different types of things that happen in the game. Like players taking damage, coming close to something, etc. These things are called events. Whenever an event is triggered you can run a piece of Lua code. You do this using event handlers. Event handlers are created using the addEventHandler(). The first argument to the addEventHandler() function is the string name of the event. These can be found on the MTA wiki as well. Server sided events Client sided events An event is always triggered for a specific element, for example "onPlayerWasted" is triggered on the player that was wasted (killed). You can attach an event handler to a single element to only have your callback function be called when the event is triggered on that specific element. But you could also use the root element here and your function will be called for every element the event is triggered on. function handlePlayerDeath() outputChatBox("You died!!", source) end addEventHandler("onPlayerWasted", getRootElement(), handlePlayerDeath) The getRootElement() function used in this example returns the root element discussed earlier. You can also see source is used in this code snippet, we'll talk about that some more in the next section. predefined globals MTA has some predifined global variables for you to use in your script. A list of them can be found on the wiki. Here's a couple notable ones and what they're used for root The root element, same as the return value of getRootElement()) source The element an event handler was called on. An event's wiki page always describes what the event source will be for the event. localPlayer The player element for the player whose client the script is running on. (Thus only available client sided) client Will be discussed in the next section server <-> client communication As stated earlier in this tutorial scripts can run either on the server, or one of the connected clients. However often times you would want to trigger something on the server from a client, or the other way around. An example of this would be when the user clicks the login button on a GUI (Graphical user interface) the server should try to log that person in, and send him back whether or not this was successful. This can be done using events. MTA allows you to create and trigger your own events, and these events can be triggered from server to client (and the other way around). You can do this using the addEvent() function. Once an event has been added (and marked as remotely triggerable by passing true as second argument to addEvent()) you can call it from server/client. You do this using [triggerClientEvent()]https://wiki.multitheftauto.com/wiki/TriggerClientEvent() and triggerServerEvent() respectively. Server sided: function handlePlayerLogin(username, password) outputChatBox("You tried to log in with the username " .. username, client) end addEvent("LuaTutorial.Login", true) addEventHandler("LuaTutorial.Login", root, handlePlayerLogin) Client sided: function pretendLogin() triggerServerEvent("LuaTutorial.Login", localPlayer, "username", "password") end pretendLogin() This example will trigger the "LuaTutorial.Login" event from the client sided script. And passes the "username" and "password" arguments to the event. The server then handles this event in the handlePlayerLogin() function, which in our case just outputs something to the chatbox. In this example you can see the previously mentioned client global variable. This variable is set to the player element corresponding to the client the event was triggered from. (And is thus only usable when used in an event handler which has been triggered from a client). Now what? With this information you should be able to start scripting, and making things in MTA! If you didn't understand it all in one go, or you have any more questions there is no shame in that! You can find myself and many others willing to help you with your scripting questions in the #scripting channel on the MTA discord. Another good source for programming / scripting related questions is stack overflow.
    1 point
  13. Introduction A collision model is used by the game to understand the impact between two entities to then calculate physics. These models are physical based, opposed to game models which are visual based. A collision model can generate particles when interacted with by being assigned surface materials. This guide is 3D modeling based and does not cover any procedures that require scripting. Table of Contents Importing collision models from the base game Patching and correcting default game collisions Primitives and why they're beneficial General limitations Surface materials Volumetric shadows Vehicle collisions Importing collision models from the base game Collision files from the base game are stored in large collision archives. These archives have the ability to stores numerous collision files, although custom archives aren't compatible with MTA. In order to find these files, it is recommended to have extracted all game models. Reading the section Extracting game assets on this thread is necessary as a first step. An easy way of extracting all game collisions is to extract entire gta3.img into a new folder. Then, run Steve M's Collision Editor and import all .col files stored in the new folder. Once imported, select all contents and right click -> export single collisions to yet another new folder. This folder will now contain all collisions from gta3.img, with the same name as their .dff model. This makes it easy to make adjustments to both DFF and COL of an object. Another way of getting the name and location of an object's collision is using SAMP Editor. Simply double clicking the object shows the name of the collision and which .col it's stored inside. It's possible to have an instance of MTA running and then open SAMP Editor, by having a separate gta sa installation specifically for SAMP Editor. Collision files are named according to their area in the map, where lae_1 is Los Santos East part 1. Although SAMP Editor option is often better, Prineside also offers a way of getting to know the collision name. Clicking any object on Prineside shows its IDE name (item definition) which is the same name used for the collision file. For instance, ID 16003 drvin_ticket belongs in countn2.ide - this means that its collision is stored in countn2_1-20.col (has 20 files from that area). Finding which one contains collision-model drvin_ticket is like finding the needle in a haystack. Using Steve M's Collision Editor may speed up the process of finding the file containing the collision model. Using collisions from the base game and then modifying can be beneficial as they're often very optimised, and uses a lot more materials for both material brightness and various different surface properties. Such results may take a lot longer than one wishes to spend on a collision. Most collision models comes with primitives which are great to use and is explained in next part. Patching and correcting default game collisions Mappers might often enough come across of furniture objects which pivots around empty air rather than the corner or center of mass. Rockstar did this a lot, especially for beds. As if this wasn't the only issue, Rockstar actually added several broken collisions to the game leading to blue hell (void). These can be patched by 3D modelers. To get started with patching a broken collision model, find and load in vegasw_4.col to 3ds Max by following the above part of the guide. Scroll through and locate downvgnbild1 and import said collision. The problem with downvgnbild1's collision is that the store windows can be climbed over, as the ceiling and walls are nor solid. Every Default's are primitives - that is spheres or boxes. They can be adjusted in height, width, length and radius as well as segments. In this case, from bottom, number 4 and number 5 Default are models that represents the store windows - which are the ones causing the glitch. What exactly causes the glitch? The collision primitives representing the store windows are not tall enough to cover the walls, which essentially means anything can sneak through the gaps. From bottom to top, the 4th Default needs be raised in Z from a value of 3,8659m to 7,2524m. Exact same procedure needs be done for 5th Default. Now the collision is solid in every way, preventing characters from climbing in behind the store windows. To export this collision, follow below steps. Open COL IO. Where it says Export Setting ensure COL3 is highlighted. This is required for GTA SA collisions. The collision contains a collision mesh named CM_downvgnbild1. This must be registered as mesh by clicking the button Collision Mesh and then clicking the CM_downvgnbild1. Same procedure for Shadow Mesh if it has any (this doesn't). The Collision Mesh button should now read as CM_downvgnbild1. In the scene, select everything that's part of the collision. On COL IO there's a text field above the button Export. Here the collision model's name can be defined. Just name it downvgnbild1. Hit Export and find a place to save it. The file can be named anything the user likes. Primitives and why they're beneficial Most 3D applications has a library of basic 3D models; primitives. They are common shapes which on creation can have their quality modified and then remodeled afterwards. For GTA San Andreas, collisions support Box and Sphere as collision primitives. Primitives costs a lot less in terms of file sizes, as example, the below sphere's collision model would be 10.4 kb with collision mesh sphere, and 140 bytes with collision primitive sphere - both collision results are equal. Tall buildings with lots of details e.g support beams tend to use collision primitives as otherwise the collision would sky rocket in file size and polyrates. Some warehouses even use primitives for their gates, exterior fences and walls, while the more complex base models requires a collision mesh. Primitives are named 'Default', though, on export they can be named anything. General limitations Collision models has certain limits which may explain crashes and strange behavior of models. Collision models can be a maximum of 512 height, 512 width and 512 length. In short, a 512 radius sphere or cube would reach the dimensions limit. Polygon rates of collision meshes (not to be confused with primitives!) should reach no more than 3000. In cases of such high rates, the modeler should either split into multiple files or optimise the mesh further. A collision file can contain up to 65535 spheres, boxes and faces. Collision archives are not supported in MTA unlike in GTA. There are cases where invisible collisions appear even after following the rules of thumb for exporting collisions. Isolated edges and vertices can be contributors to invisible collision walls. For reproduction steps follow this Discord channel link to MTA discord. Surface materials GTA SA engine uses materials for collisions. These materials define what type of surface that the model represents. Kam's COL MTL has 179 ID's. Ranging from tarmac, destroyed tarmac, dry mud, wet mud to sea floor and grass. These surface properties generate sound fx and particle fx when walked, driven or shot upon. Collision material also has brightness setting, where 0 is dark and 150+ is bright. Entities affected by this setting are peds and vehicles. The below spoiler contains a list of all surface materials and their ID's. Volumetric shadows World objects and vehicles are able to cast shadows - dynamic ones. These are meshes stored inside the collision file. Generally the shadow mesh should be slightly smaller than the game mesh, as otherwise it'll glitch on the corners of the model. The setting to display shadows are found in MTA video settings. Vehicle collisions Where world objects uses separate .col files, vehicles require their collisions to be embedded or baked in the DFF. These collision models consists of mostly spheres but may also be found to contain boxes and of course their collision mesh (prefixed CM_vehicleName). The collisions use spheres due to the engine's ease of calculating physics with those primitives. They are named that of the material Brightness/Part section, e.g Boot or Rear bumper. Ones that are not used by vehicle components are named Default. In below screenshot, the white lines represents the collision mesh of a Tornado. Usually the collision meshes will suffice with 12 faces total for the hood, windshields, roof, boot and bottom.
    1 point
  14. Introduction Properly handling your user's credentials (username and password) is very important, this guide gives detailed information and code samples on how to (properly) implement an account "system". This guide assumes you are not using MTA's built in accounts. Disclaimer: Any code shown in this tutorial is purely for illustrative purposes, and is not finished code, you should use it as a guideline, not a solution. Content The following topics will be discussed in this tutorial: How to hash and salt passwords (register) How to validate a hashed password (login) How to add "remember-me" functionality How to offer password recovery How to migrate from an older hashing algorithm, to a newer one Using a password policy (Extra) How to handle database leaks (Extra) What even is hashing and salting? For the purpose of this tutorial we expect a database structure which is somewhat similar to this: How to hash and salt passwords When you have a user register on your service, that user expects of you to keep their password safe. Whilst it is generally bad practice to use the same password for multiple services there are many users that still do so. Because of this it's crucial that you save the user's passwords in a way that an attacker will be unable to find out the original password the user typed. This includes if they have full access to your database. In order to do this we do what is called "Password hashing" When a user registers, your server receives the user's intended username, (email) and password. Before you save that password to the database you have to hash and salt this, luckily MTA has a function that takes care of this. If you wish to know more about what exactly it does, there's more information at the end of this tutorial. In order to hash this password you use the passwordHash function. This function is relatively slow (by design), so it is highly recommended you pass a callback to this function, so your entire script doesn't wait for it to complete. https://wiki.multitheftauto.com/wiki/PasswordHash local mysqlHandle -- we're assuming this value is set somewhere else in code function register(username, email, password) local player = client passwordHash(password, "bcrypt", {}, function(hashedPassword) -- callback function for hashing the password local handle = dbQuery(function(handle) -- callback function for storing the user in the database if (handle) then triggerClientEvent(player, "registrationSuccess") -- inform the user that registration was successful else triggerClientEvent(player, "registrationFailed") end end,mysqlHandle, "INSERT INTO users (email, username, password) VALUES (?, ?, ?)", email, username, hashedPassword) end) end addEvent("passwordTutorial:register", true) addEventHandler("passwordTutorial:register", getRootElement(), register) How to validate a hashed password Once you've saved the hashed password to your database you need to do a little bit of additional work when authenticating the user. Luckily MTA offers a passwordVerify() function, which is the counterpart of the previously discussed passwordHash(). What this function does it basically hashes the password in the same way, resulting in the same output hash. https://wiki.multitheftauto.com/wiki/passwordVerify In order to get the account the user is trying to log in to you have to do a query for an account which has the user submitted username, and of which the password matches through passwordVerify. PasswordVerify is also a relatively slow function, thus you should use a callback. function login(username, password) local player = client dbQuery(function (handle) -- callback for the query selecting the user by username local results = dbPoll(handle, -1) if (#results == 0) then triggerClientEvent(player, "loginFailed") return end passwordVerify(password, results[1].password, {}, function(matches) -- callback function for the password verify if (matches) then -- Do anything you wish with the database result to log the player in with the rest of your scripts triggerClientEvent(player, "loginSuccess") else triggerClientEvent(player, "loginFailed") end end) end, mysqlHandle, "SELECT * FROM users WHERE username = ?", username) end addEvent("passwordTutorial:login", true) addEventHandler("passwordTutorial:login", getRootElement(), login) How to add "remember me" functionality When users on your server log in, they often do not want to have to enter their username and password every time they want to log in. In order to satisfy this need you can implement a "remember me" function. What I've seen happen in the past, is people would store the user's password (encrypted) on the client. This is NOT safe, and should never be done! In order to properly use remember me functionality what you would do is upon login in, generate a random string. The longer the better. This random string is what we call an access token. You would then allow the user to log in with such an access token, preferably only once generating a new access token each time one is used. To implement this you would generate that token every time the user logs in, whilst they have "remember me" enabled. You will have to save this token in your database alongside your user. For extra security you could also store the user's serial alongside the access token, you can then validate that the access token is being used from the same device. https://wiki.multitheftauto.com/wiki/Filepath function login(username, password) -- This code should be put in the callback to the dbQuery function, but to keep the example clean that's not shown here if (rememberMe) then local token = generateRandomToken() dbQuery(mysqlHandle, "INSERT INTO access_tokens (user_id, token) VALUES (?, ?)", results[1].id, token) triggerClientEvent(player, "loginSuccess", token) end end function rememberMeLogin(username, accessToken) -- this function handles a user's login attempt dbQuery(function(handle) local result = dbPoll(handle, -1) if (#result == 0) then triggerClientEvent(player, "loginFailed") else -- Do anything you wish with the database result to log the player in with the rest of your scripts triggerClientEvent(player, "loginSuccess") end end,mysqlHandle, "SELECT users.* FROM access_tokens JOIN users ON users.id = access_tokens.user_id WHERE users.username = ?", username) end addEvent("passwordTutorial:loginRememberMe", true) addEventHandler("passwordTutorial:loginRememberMe", getRootElement(), login) How to offer password recovery Offering password recovery requires a little bit more than just your MTA server. Generally password recovery is done with emails. So you would need access to an email server / service which you can use to send an email from an HTTP request. (Like you can do with fetchRemote()). When a user requests a password reset, have them enter the email you registered with. You then fetch a user from the database with this email address. You would then store a password recovery token for this user. This token, just like the remember me token, is a random string. Ideally, you would send the user a link with a password reset form that goes to a webpage where the user can reset their password. You could do this with an external service, like a webserver. Or you could use MTA's Resource web access for it, but if you do make sure you handle permissions properly for anything else that uses this. However another option would be to have the user copy paste the generated token from the email into you server's login window. Which of the two solutions you pick is up to you, my personal preference goes to the one with the link in the email. But in either case the server side logic is the same. When the user attempts to perform password recovery, verify that the token they give you belongs to a user, and then change the password to the newly requested password. Make sure you hash this password the same way you do in your login. function requestPasswordRecovery(email) dbQuery(function (handle)) local result = dbPoll(handle, -1) if (#result == 0) then triggerClientEvent(player, "passwordTutorial:passwordRecoveryRequestFailed") else local token = generateRandomToken() dbExec(mysqlHandle, "UPDATE user_data SET recovery_token = ?", token) -- mail the token to the user, mail implementation depends on the mail server/service you use triggerClientEvent(player, "passwordTutorial:passwordRecoveryRequestSuccess") end end, mysqlHandle, "SELECT * FROM users WHERE email = ?", email) end function recoverPassword(recoveryToken, password) dbQuery(function (handle) local result = dbPoll(handle, -1) if (#result == 0) then -- this is only valid if you have the user request password recovery from ingame triggerClientEvent(player, "passwordTutorial:passwordRecoveryFailed") else passwordHash(password, "bcrypt", {}, function(hashedPassword) -- callback function for hashing the password local handle = dbExec(function(handle) -- callback function for storing the new password in the database if (handle) then -- this is only valid if you have the user request password recovery from ingame triggerClientEvent(player, "passwordTutorial:passwordRecoverySuccess") -- inform the user that registration was successful else -- this is only valid if you have the user request password recovery from ingame triggerClientEvent(player, "passwordTutorial:passwordRecoveryFailed") end end,mysqlHandle, "UPDATE user_data SET password = ? WHERE recovery_token = ?", username, recoveryToken) end) end end, "SELECT * FROM users WHERE recovery_token = ?", recoveryToken) end Besides changing the password, it's important you also delete any access tokens that user might have if you're using remember me functionality. It is also good practice to make recovery tokens expiry after a certain amount of times, and not allow a recovery token to be created whilst one is already in progress. This prevents a user from sending a large number of emails from your service. How to migrate from an older hashing algorithm, to a newer one Maybe after reading this topic you realise that your password security is not what it should be. So you want to change your old password hashing / validation logic to the ones explained in this topic. And due to the nature that hashes can not be "unhashed", you can't simply migrate your passwords over. So in order to migrate the passwords what you have to do is when a user logs in, first validate their password with the old hashing algorithm. If this matches, then hash (and salt) it with your new hashing algorithm and save it in your database. Make sure to delete the old password otherwise your password security is not any better than before. Using a password policy Passwords policies are important to prevent your users from picking a password that is too easily cracked / brute forced. Many password policies come in the form of "Must have at least one capital letter, one digit and one number". But that discards that fact that the best way to make your password more difficult to crack, is making your password longer. So in the code snippet below is a function that measures the 'search space' of a password. The search space of a password is the amount of possible passwords there are with a certain combination of characters. In order to use this, you would have to set a minimum password search space when a user registers for an account. This minimum is up for you to set, but be reasonable, you shouldn't expect a user's password to be impossible to remember / create. I recommend playing with the function a bit to see what values you get out of it, and pick something you believe is sensible. function getPasswordSearchSpace(password) local lowerCase = password:find("%l") and 26 or 0 local upperCase = password:find("%u") and 26 or 0 local digits = password:find("%d") and 10 or 0 local symbols = password:find("%W") and 32 or 0 local length = password:len() return (lowerCase + upperCase + digits + symbols) ^ length end -- The below function calls are to indicate the difference in search space for a set of passwords print(getPasswordSearchSpace("a")) print(getPasswordSearchSpace("abc")) print(getPasswordSearchSpace("Abc")) print(getPasswordSearchSpace("Ab!")) print(getPasswordSearchSpace("Ab!0")) print(getPasswordSearchSpace("Mu#9A0h.")) print(getPasswordSearchSpace("This is a demonstration of how easy an incredibly strong password is to remember")) How to handle database leaks If you have reason to believe that your database has been leaked or otherwise compromised, it is important that your first course of action is removing any access tokens stored in your database. Once you have done that you have to inform your users. Whilst when properly hashed and salted it's extremely difficult / time consuming to find out a user's password it is still a possibility. So you should inform your users of the breach, tell them that their passwords were properly hashed, and they do not need to fear for their passwords immediately. However you should suggest to your users that they change their password either way, just in case. What even is hashing and salting? Hashing has been brought up several times in this tutorial, whilst you do not need to know what it is / does, you might be interested in knowing regardless. I won't be going too far in depth as I simply do not have the knowledge, but the basic idea of hashing is this: When you hash anything, you turn it into a string of characters (or other value) that has no relation to the original input, other than when you hash the original input again, it will always generate the same hash. For example, when you hash the string 'banana' using the sha512 hashing algorithm, it will always yield the output: "F8E3183D38E6C51889582CB260AB825252F395B4AC8FB0E6B13E9A71F7C10A80D5301E4A949F2783CB0C20205F1D850F87045F4420AD2271C8FD5F0CD8944BE3" Now hashing can not be reverted, you can not "unhash" a hash, so in order to verify someone's password you hash it again, and see if the two hashes are the exact same. Now this is great, passwords are safely stored. However there is still more to do, salting. Salting is adding some random data to your password prior to hashing it. This prevents when two users (on the same service, or on others) have the same password, that their hashes are also the same. Meaning if one password is compromised, the other password is not. It is important that a salt is random for every user in your application, not one salt for your entire application. Now you might think we didn't do any salting in the code / tutorial above. This is not true, we just didn't do it ourselves. MTA's passwordHash function actually hashes the passwords and salts it, this salt is then stored in the output hash it self, just before the actual password hash. In the case of bcrypt it actually stores a little bit more info in the resulting hash, but you need not worry about that.
    1 point
  15. I'm not familiar with CJ clothes. Can you have more than one clothing on the torso? Like a t-shirt and a jacket as well?
    1 point
×
×
  • Create New...