Guest User

Untitled

a guest
Jun 3rd, 2018
163
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 33.14 KB | None | 0 0
  1.  
  2.  
  3. -- Events
  4. addEvent( "onCharacterLogin", false )
  5. addEvent( "onCharacterLogout", false )
  6.  
  7. --
  8.  
  9. local team = createTeam( "SGC" ) -- this is used as a dummy team. We need this for faction chat to work.
  10. p = { }
  11.  
  12. -- Import Groups
  13. local groups = {
  14.     { groupName = "MTA Moderators", groupID = false, aclGroup = "Moderator", displayName = "Moderator", nametagColor = { 255, 255, 127 }, priority = 5 },
  15.     { groupName = "MTA Administrators", groupID = false, aclGroup = "Admin", displayName = "Administrator", nametagColor = { 255, 255, 0 }, priority = 10, defaultForFirstUser = true },
  16.     { groupName = "Developers", groupID = false, aclGroup = "Developer", displayName = "Developer", nametagColor = { 127, 255, 127 }, priority = 20, defaultForFirstUser = true },
  17. }
  18.  
  19. local function updateNametagColor( player )
  20.     local nametagColor = { 127, 127, 127, priority = 0 }
  21.     if p[ player ] and isLoggedIn( player ) then
  22.         nametagColor = { 255, 255, 255, priority = 0 }
  23.         if getOption( player, "staffduty" ) then
  24.             for key, value in ipairs( groups ) do
  25.                 if isObjectInACLGroup( "user." .. p[ player ].username, aclGetGroup( value.aclGroup ) ) and value.nametagColor then
  26.                     if value.priority > nametagColor.priority then
  27.                         nametagColor = value.nametagColor
  28.                         nametagColor.priority = value.priority
  29.                     end
  30.                 end
  31.             end
  32.         end
  33.     end
  34.     setPlayerNametagColor( player, unpack( nametagColor ) )
  35. end
  36.  
  37. function getGroups( player )
  38.     local g = { }
  39.     if p[ player ] then
  40.         for key, value in ipairs( groups ) do
  41.             if isObjectInACLGroup( "user." .. p[ player ].username, aclGetGroup( value.aclGroup ) ) then
  42.                 table.insert( g, value )
  43.             end
  44.         end
  45.         table.sort( g, function( a, b ) return a.priority > b.priority end )
  46.     end
  47.     return g
  48. end
  49.  
  50. local function aclUpdate( player, saveAclIfChanged )
  51.     local saveAcl = false
  52.    
  53.     if player then
  54.         local info = p[ player ]
  55.         if info and info.username then
  56.             local shouldHaveAccount = false
  57.             local account = getAccount( info.username )
  58.             local groupinfo = exports.sql:query_assoc( "SELECT groupID FROM wcf1_user_to_groups WHERE userID = " .. info.userID )
  59.             if groupinfo then
  60.                 -- loop through all retrieved groups
  61.                 for key, group in ipairs( groupinfo ) do
  62.                     for key2, group2 in ipairs( groups ) do
  63.                         -- we have a acl group of interest
  64.                         if group.groupID == group2.groupID then
  65.                             -- mark as person to have an account
  66.                             shouldHaveAccount = true
  67.                            
  68.                             -- add an account if it doesn't exist
  69.                             if not account then
  70.                                 outputServerLog( tostring( info.username ) .. " " .. tostring( info.mtasalt ) )
  71.                                 account = addAccount( info.username, info.mtasalt ) -- due to MTA's limitations, the password can't be longer than 30 chars
  72.                                 if not account then
  73.                                     outputDebugString( "Account Error for " .. info.username .. " - addAccount failed.", 1 )
  74.                                 else
  75.                                     outputDebugString( "Added account " .. info.username, 3 )
  76.                                 end
  77.                             end
  78.                            
  79.                             if account then
  80.                                 -- if the player has a different account password, change it
  81.                                 if not getAccount( info.username, info.mtasalt ) then
  82.                                     setAccountPassword( account, info.mtasalt )
  83.                                 end
  84.                                
  85.                                 if isGuestAccount( getPlayerAccount( player ) ) and not logIn( player, account, info.mtasalt ) then
  86.                                     -- something went wrong here
  87.                                     outputDebugString( "Account Error for " .. info.username .. " - login failed.", 1 )
  88.                                 else
  89.                                     -- show him a message
  90.                                     outputChatBox( "You are now logged in as " .. group2.displayName .. ".", player, 0, 255, 0 )
  91.                                     if aclGroupAddObject( aclGetGroup( group2.aclGroup ), "user." .. info.username ) then
  92.                                         saveAcl = true
  93.                                         outputDebugString( "Added account " .. info.username .. " to " .. group2.aclGroup .. " ACL", 3 )
  94.                                     end
  95.                                 end
  96.                             end
  97.                         end
  98.                     end
  99.                 end
  100.             end
  101.             if not shouldHaveAccount and account then
  102.                 -- remove account from all ACL groups we use
  103.                 for key, value in ipairs( groups ) do
  104.                     if aclGroupRemoveObject( aclGetGroup( value.aclGroup ), "user." .. info.username ) then
  105.                         saveAcl = true
  106.                         outputDebugString( "Removed account " .. info.username .. " from " .. value.aclGroup .. " ACL", 3 )
  107.                         outputChatBox( "You are no longer logged in as " .. group.displayName .. ".", player, 255, 0, 0 )
  108.                     end
  109.                 end
  110.                
  111.                 -- remove the account
  112.                 removeAccount( account )
  113.                 outputDebugString( "Removed account " .. info.username, 3 )
  114.             end
  115.            
  116.             if saveAcl then
  117.                 updateNametagColor( player )
  118.             end
  119.         end
  120.     else
  121.         -- verify all accounts and remove invalid ones
  122.         local checkedPlayers = { }
  123.         local accounts = getAccounts( )
  124.         for key, account in ipairs( accounts ) do
  125.             local accountName = getAccountName( account )
  126.             local player = getAccountPlayer( account )
  127.             if player then
  128.                 checkedPlayers[ player ] = true
  129.             end
  130.             if accountName ~= "Console" then -- console may exist untouched
  131.                 local user = exports.sql:query_assoc_single( "SELECT userID FROM wcf1_user WHERE username = '%s'", accountName )
  132.                 if user then
  133.                     -- account should be deleted if no group is found
  134.                     local shouldBeDeleted = true
  135.                     local userChanged = false
  136.                    
  137.                     if user.userID then -- if this doesn't exist, the user does not exist in the db
  138.                         -- fetch all of his groups groups
  139.                         local groupinfo = exports.sql:query_assoc( "SELECT groupID FROM wcf1_user_to_groups WHERE userID = " .. user.userID )
  140.                         if groupinfo then
  141.                             -- look through all of our pre-defined groups
  142.                             for key, group in ipairs( groups ) do
  143.                                 -- user does not have this group
  144.                                 local hasGroup = false
  145.                                
  146.                                 -- check if he does have it
  147.                                 for key2, group2 in ipairs( groupinfo ) do
  148.                                     if group.groupID == group2.groupID then
  149.                                         -- has the group
  150.                                         hasGroup = true
  151.                                        
  152.                                         -- shouldn't delete his account
  153.                                         shouldBeDeleted = false
  154.                                        
  155.                                         -- make sure acl rights are set correctly
  156.                                         if aclGroupAddObject( aclGetGroup( group.aclGroup ), "user." .. accountName ) then
  157.                                             outputDebugString( "Added account " .. accountName .. " to ACL " .. group.aclGroup, 3 )
  158.                                             saveAcl = true
  159.                                             userChanged = true
  160.                                             if player then
  161.                                                 outputChatBox( "You are now logged in as " .. group.displayName .. ".", player, 0, 255, 0 )
  162.                                             end
  163.                                         end
  164.                                     end
  165.                                 end
  166.                                
  167.                                 -- doesn't have it
  168.                                 if not hasGroup then
  169.                                     -- make sure acl rights are removed
  170.                                     if aclGroupRemoveObject( aclGetGroup( group.aclGroup ), "user." .. accountName ) then
  171.                                         outputDebugString( "Removed account " .. accountName .. " from ACL " .. group.aclGroup, 3 )
  172.                                         saveAcl = true
  173.                                         userChanged = true
  174.                                        
  175.                                         if player then
  176.                                             outputChatBox( "You are no longer logged in as " .. group.displayName .. ".", player, 255, 0, 0 )
  177.                                         end
  178.                                     end
  179.                                 end
  180.                             end
  181.                         end
  182.                     end
  183.                    
  184.                     -- has no relevant group, thus we don't need the MTA account
  185.                     if shouldBeDeleted then
  186.                         if player then
  187.                             logOut( player )
  188.                         end
  189.                         outputDebugString( "Removed account " .. accountName, 3 )
  190.                         removeAccount( account )
  191.                     elseif player and isGuestAccount( getPlayerAccount( player ) ) and not logIn( player, account, p[ player ].mtasalt ) then
  192.                         -- something went wrong here
  193.                         outputDebugString( "Account Error for " .. accountName .. " - login failed.", 1 )
  194.                     end
  195.                    
  196.                     -- update the color since we have none
  197.                     if player and ( shouldBeDeleted or userChanged ) then
  198.                         updateNametagColor( player )
  199.                     end
  200.                 else
  201.                     -- Invalid user
  202.                    
  203.                     -- remove account from all ACL groups we use
  204.                     for key, value in ipairs( groups ) do
  205.                         if aclGroupRemoveObject( aclGetGroup( value.aclGroup ), "user." .. accountName ) then
  206.                             saveAcl = true
  207.                             outputDebugString( "Removed account " .. accountName .. " from " .. value.aclGroup .. " ACL", 3 )
  208.                            
  209.                             if player then
  210.                                 outputChatBox( "You are no longer logged in as " .. group.displayName .. ".", player, 255, 0, 0 )
  211.                             end
  212.                         end
  213.                     end
  214.                    
  215.                     -- remove the account
  216.                     if player then
  217.                         logOut( player )
  218.                     end
  219.                     removeAccount( account )
  220.                     outputDebugString( "Removed account " .. accountName, 3 )
  221.                 end
  222.             end
  223.         end
  224.        
  225.         -- check all players not found by this for whetever they now have an account
  226.         for key, value in ipairs( getElementsByType( "player" ) ) do
  227.             if not checkedPlayers[ value ] then
  228.                 local success, needsAclUpdate = aclUpdate( value, false )
  229.                 if needsAclUpdate then
  230.                     saveAcl = true
  231.                 end
  232.             end
  233.         end
  234.     end
  235.     -- if we should save the acl, do it (permissions changed)
  236.     if saveAclIfChanged and saveAcl then
  237.         aclSave( )
  238.     end
  239.     return true, saveAcl
  240. end
  241.  
  242. addCommandHandler( "reloadpermissions",
  243.     function( player )
  244.         if aclUpdate( nil, true ) then
  245.             outputServerLog( "Permissions have been reloaded. (Requested by " .. ( not player and "Console" or getAccountName( getPlayerAccount( player ) ) or getPlayerName(player) ) .. ")" )
  246.             if player then
  247.                 outputChatBox( "Permissions have been reloaded.", player, 0, 255, 0 )
  248.             end
  249.         else
  250.             outputServerLog( "Permissions reload failed. (Requested by " .. ( not player and "Console" or getAccountName( getPlayerAccount( player ) ) or getPlayerName(player) ) .. ")" )
  251.             if player then
  252.                 outputChatBox( "Permissions reload failed.", player, 255, 0, 0 )
  253.             end
  254.         end
  255.     end,
  256.     true
  257. )
  258.  
  259. addEventHandler( "onResourceStart", resourceRoot,
  260.     function( )
  261.         -- create all mysql tables
  262.         if not exports.sql:create_table( 'characters',
  263.             {
  264.                 { name = 'characterID', type = 'int(10) unsigned', auto_increment = true, primary_key = true },
  265.                 { name = 'characterName', type = 'varchar(22)' },
  266.                 { name = 'userID', type = 'int(10) unsigned' },
  267.                 { name = 'x', type = 'float' },
  268.                 { name = 'y', type = 'float' },
  269.                 { name = 'z', type = 'float' },
  270.                 { name = 'interior', type = 'tinyint(3) unsigned' },
  271.                 { name = 'dimension', type = 'int(10) unsigned' },
  272.                 { name = 'skin', type = 'int(10) unsigned' },
  273.                 { name = 'rotation', type = 'float' },
  274.                 { name = 'health', type = 'tinyint(3) unsigned', default = 100 },
  275.                 { name = 'armor', type = 'tinyint(3) unsigned', default = 0 },
  276.                 { name = 'money', type = 'bigint(20) unsigned', default = 100 },
  277.                 { name = 'created', type = 'timestamp', default = 'CURRENT_TIMESTAMP' },
  278.                 { name = 'lastLogin', type = 'timestamp', default = '0000-00-00 00:00:00' },
  279.                 { name = 'weapons', type = 'varchar(255)', null = true },
  280.                 { name = 'job', type = 'varchar(20)', null = true },
  281.                 { name = 'languages', type = 'text', null = true },
  282.             } ) then cancelEvent( ) return end
  283.        
  284.         if not exports.sql:create_table( 'wcf1_user',
  285.             {
  286.                 { name = 'userID', type = 'int(10) unsigned', auto_increment = true, primary_key = true },
  287.                 { name = 'username', type = 'varchar(255)' },
  288.                 { name = 'password', type = 'varchar(40)' },
  289.                 { name = 'salt', type = 'varchar(40)' },
  290.                 { name = 'banned', type = 'tinyint(1) unsigned', default = 0 },
  291.                 { name = 'activationCode', type = 'int(10) unsigned', default = 0 },
  292.                 { name = 'banReason', type = 'mediumtext', null = true },
  293.                 { name = 'banUser', type = 'int(10) unsigned', null = true },
  294.                 { name = 'lastIP', type = 'varchar(15)', null = true },
  295.                 { name = 'lastSerial', type = 'varchar(32)', null = true },
  296.                 { name = 'userOptions', type = 'text', null = true },
  297.             } ) then cancelEvent( ) return end
  298.        
  299.         local success, didCreateTable = exports.sql:create_table( 'wcf1_group',
  300.             {
  301.                 { name = 'groupID', type = 'int(10) unsigned', auto_increment = true, primary_key = true },
  302.                 { name = 'groupName', type = 'varchar(255)', default = '' },
  303.                 { name = 'canBeFactioned', type = 'tinyint(1) unsigned', default = 1 }, -- if this is set to 0, you can't make a faction from this group.
  304.             } )
  305.         if not success then cancelEvent( ) return end
  306.         if didCreateTable then
  307.             -- add default groups
  308.             for key, value in ipairs( groups ) do
  309.                 value.groupID = exports.sql:query_insertid( "INSERT INTO wcf1_group (groupName, canBeFactioned) VALUES ('%s', 0)", value.groupName )
  310.             end
  311.         else
  312.             -- import all groups
  313.             local data = exports.sql:query_assoc( "SELECT groupID, groupName FROM wcf1_group" )
  314.             if data then
  315.                 for key, value in ipairs( data ) do
  316.                     for key2, value2 in ipairs( groups ) do
  317.                         if value.groupName == value2.groupName then
  318.                             value2.groupID = value.groupID
  319.                         end
  320.                     end
  321.                 end
  322.             end
  323.         end
  324.        
  325.         local success, didCreateTable = exports.sql:create_table( 'wcf1_user_to_groups',
  326.             {
  327.                 { name = 'userID', type = 'int(10) unsigned', default = 0, primary_key = true },
  328.                 { name = 'groupID', type = 'int(10) unsigned', default = 0, primary_key = true },
  329.             } )
  330.         if not success then cancelEvent( ) return end
  331.         if didCreateTable then
  332.             for key, value in ipairs( groups ) do
  333.                 if value.defaultForFirstUser then
  334.                     exports.sql:query_free( "INSERT INTO wcf1_user_to_groups (userID, groupID) VALUES (1, " .. value.groupID .. ")" )
  335.                 end
  336.             end
  337.         end
  338.        
  339.         aclUpdate( nil, true )
  340.     end
  341. )
  342. --
  343.  
  344. local function showLoginScreen( player, screenX, screenY, token, ip )
  345.     -- we need at least 800x600 for proper display of all GUI
  346.     if screenX and screenY then
  347.         if screenX < 800 or screenY < 600 then
  348.             kickPlayer( player, "Use 800x600 or a larger resolution." )
  349.             return
  350.         end
  351.     end
  352.    
  353.     -- remove the player from his vehicle if any
  354.     if isPedInVehicle( player ) then
  355.         removePedFromVehicle( player )
  356.     end
  357.    
  358.     -- hide the current view (will be faded in client-side)
  359.     fadeCamera( player, false, 0 )
  360.     toggleAllControls( player, false, true, false )
  361.    
  362.     -- spawn the player etc.
  363.     spawnPlayer( source, -1504, 1376, 3.75, 315, 0, 0, 1 )
  364.     setPedFrozen( source, true )
  365.     setElementAlpha( source, 0 )
  366.    
  367.     setCameraInterior( source, 0 )
  368.     setCameraMatrix( source, 2270.16, 801.89, 64.81,2069.7, 1058.37, 17.03 ) -- background position
  369.    
  370.     setPlayerNametagColor( source, 127, 127, 127 )
  371.    
  372.     -- check for ip/serial bans
  373.     if exports.sql:query_assoc_single( "SELECT * FROM wcf1_user WHERE banned = 1 AND ( lastIP = '%s' OR lastSerial = '%s' )", getPlayerIP( player ), getPlayerSerial( player ) ) then
  374.         showChat( player, false )
  375.         setTimer( triggerClientEvent, 300, 1, player, getResourceName( resource ) .. ":loginResult", player, 2 ) -- Banned
  376.         return false
  377.     end
  378.    
  379.     triggerClientEvent( player, getResourceName( resource ) .. ":spawnscreen", player )
  380.     if token and #token > 0 then
  381.         performLogin( source, token, false, ip )
  382.     end
  383. end
  384.  
  385. addEvent( getResourceName( resource ) .. ":ready", true )
  386. addEventHandler( getResourceName( resource ) .. ":ready", root,
  387.     function( ... )
  388.         if source == client then
  389.             showLoginScreen( source, ... )
  390.         end
  391.     end
  392. )
  393.  
  394. --
  395. -- tweak to allow multiple servers running paradise without interfering login files.
  396. -- this is also used to calculate an individual player's hash so even players with same ip/serial/username/password on two servers won't get the same hash to login with.
  397. -- If it's ever stolen/abused - if that's even possible? -, simply delete the token.xml file and restart this resource, this is going to prompt each player to login again.
  398. --
  399.  
  400. local serverToken = nil
  401. addEventHandler( "onResourceStart", resourceRoot,
  402.     function( )
  403.         local xml = xmlLoadFile( "token.xml" )
  404.         if xml then
  405.             serverToken = xmlNodeGetValue( xml )
  406.             xmlUnloadFile( xml )
  407.         end
  408.        
  409.         if not serverToken then
  410.             -- this should ideally be unique
  411.             serverToken = md5( getServerName( ) .. math.random( 1, 2^30 ) .. getServerPort( ) .. "~paradise" ):lower( )
  412.             local xml = xmlCreateFile( "token.xml", "token" )
  413.             if xml then
  414.                 if xmlNodeSetValue( xml, serverToken ) then
  415.                     xmlSaveFile( xml )
  416.                     setTimer( outputConsole, 100, 1, "Created Server Token: " .. serverToken )
  417.                 else
  418.                     serverToken = nil
  419.                 end
  420.                 xmlUnloadFile( xml )
  421.             else
  422.                 serverToken = nil
  423.             end
  424.         end
  425.        
  426.         addEvent( getResourceName( resource ) .. ":requestServerToken", true )
  427.         addEventHandler( getResourceName( resource ) .. ":requestServerToken", root,
  428.             function( ... )
  429.                 if source == client then
  430.                     triggerClientEvent( source, getResourceName( resource ) .. ":receiveServerToken", source, serverToken and md5( serverToken ):lower( ) )
  431.                 end
  432.             end
  433.         )
  434.     end
  435. )
  436.  
  437. --
  438.  
  439. local loginAttempts = { }
  440. local triedTokenAuth = { }
  441.  
  442. local function getPlayerHash( player, remoteIP )
  443.     local ip = getPlayerIP( player ) or "255.255.255.0"
  444.     if ip == "127.0.0.1" and remoteIP then -- we don't really care about a provided ip unless we want to connect from localhost
  445.         ip = exports.sql:escape_string( remoteIP )
  446.     end
  447.     return ip:sub(ip:find("%d+%.%d+%.")) .. ( getPlayerSerial( player ) or "R0FLR0FLR0FLR0FLR0FLR0FLR0FLR0FL" ) .. tostring( serverToken )
  448. end
  449.  
  450. addEvent( getResourceName( resource ) .. ":login", true )
  451. addEventHandler( getResourceName( resource ) .. ":login", root,
  452.     function( username, password )
  453.         if source == client then
  454.             triedTokenAuth[ source ] = true
  455.             if username and password and #username > 0 and #password > 0 then
  456.                 local info = exports.sql:query_assoc_single( "SELECT CONCAT(SHA1(CONCAT(username, '%s')),SHA1(CONCAT(salt, SHA1(CONCAT('%s',SHA1(CONCAT(salt, SHA1(CONCAT(username, SHA1(password)))))))))) AS token FROM wcf1_user WHERE `username` = '%s' AND password = SHA1(CONCAT(salt, SHA1(CONCAT(salt, '" .. sha1(password) .. "'))))", getPlayerHash( source ), getPlayerHash( source ), username )
  457.                 p[ source ] = nil
  458.                 if not info then
  459.                     triggerClientEvent( source, getResourceName( resource ) .. ":loginResult", source, 1 ) -- Wrong username/password
  460.                     loginAttempts[ source ] = ( loginAttempts[ source ] or 0 ) + 1
  461.                     if loginAttempts[ source ] >= 5 then
  462.                         -- ban for 15 minutes
  463.                         local serial = getPlayerSerial( source )
  464.                        
  465.                         banPlayer( source, true, false, false, root, "Too many login attempts.", 900 )
  466.                         if serial then
  467.                             addBan( nil, nil, serial, root, "Too many login attempts.", 900 )
  468.                         end
  469.                     end
  470.                 else
  471.                     loginAttempts[ source ] = nil
  472.                     performLogin( source, info.token, true )
  473.                 end
  474.             end
  475.         end
  476.     end
  477. )
  478.  
  479. function performLogin( source, token, isPasswordAuth, ip )
  480.     if source and ( isPasswordAuth or not triedTokenAuth[ source ] ) then
  481.         triedTokenAuth[ source ] = true
  482.         if token then
  483.             if #token == 80 then
  484.                 local info = exports.sql:query_assoc_single( "SELECT userID, username, banned, activationCode, SUBSTRING(LOWER(SHA1(CONCAT(userName,SHA1(CONCAT(password,salt))))),1,30) AS salts, userOptions FROM wcf1_user WHERE CONCAT(SHA1(CONCAT(username, '%s')),SHA1(CONCAT(salt, SHA1(CONCAT('%s',SHA1(CONCAT(salt, SHA1(CONCAT(username, SHA1(password)))))))))) = '%s' LIMIT 1", getPlayerHash( source, ip ), getPlayerHash( source, ip ), token )
  485.                 p[ source ] = nil
  486.                 if not info then
  487.                     if isPasswordAuth then
  488.                         triggerClientEvent( source, getResourceName( resource ) .. ":loginResult", source, 1 ) -- Wrong username/password
  489.                     end
  490.                     return false
  491.                 else
  492.                     if info.banned == 1 then
  493.                         triggerClientEvent( source, getResourceName( resource ) .. ":loginResult", source, 2 ) -- Banned
  494.                         return false
  495.                     elseif info.activationCode > 0 then
  496.                         triggerClientEvent( source, getResourceName( resource ) .. ":loginResult", source, 3 ) -- Requires activation
  497.                         return false
  498.                     else
  499.                         -- check if another user is logged in on that account
  500.                         for player, data in pairs( p ) do
  501.                             if data.userID == info.userID then
  502.                                 triggerClientEvent( source, getResourceName( resource ) .. ":loginResult", source, 5 ) -- another player with that account found
  503.                                 return false
  504.                             end
  505.                         end
  506.                        
  507.                         local username = info.username
  508.                         p[ source ] = { userID = info.userID, username = username, mtasalt = info.salts, options = info.userOptions and fromJSON( info.userOptions ) or { } }
  509.                        
  510.                         -- check for admin rights
  511.                         aclUpdate( source, true )
  512.                        
  513.                         -- show characters
  514.                         local chars = exports.sql:query_assoc( "SELECT characterID, characterName, skin FROM characters WHERE userID = " .. info.userID .. " ORDER BY lastLogin DESC" )
  515.                         if isPasswordAuth then
  516.                             triggerClientEvent( source, getResourceName( resource ) .. ":characters", source, chars, true, token, getPlayerIP( source ) ~= "127.0.0.1" and getPlayerIP( source ) )
  517.                         else
  518.                             triggerClientEvent( source, getResourceName( resource ) .. ":characters", source, chars, true )
  519.                         end
  520.                        
  521.                         outputServerLog( "PARADISE LOGIN: " .. getPlayerName( source ) .. " logged in as " .. info.username .. " (IP: " .. getPlayerIP( source ) .. ", Serial: " .. getPlayerSerial( source ) .. ")" )
  522.                         exports.server:message( "%C04[" .. getID( source ) .. "]%C %B" .. info.username .. "%B logged in (Nick: %B" .. getPlayerName( source ):gsub( "_", " " ) .. "%B)." )
  523.                         exports.sql:query_free( "UPDATE wcf1_user SET lastIP = '%s', lastSerial = '%s' WHERE userID = " .. tonumber( info.userID ), getPlayerIP( source ), getPlayerSerial( source ) )
  524.                        
  525.                         return true
  526.                     end
  527.                 end
  528.             end
  529.         end
  530.     end
  531.     return false
  532. end
  533.  
  534. local function getWeaponString( player )
  535.     local weapons = { }
  536.     local hasAnyWeapons = false
  537.     for slot = 0, 12 do
  538.         local weapon = getPedWeapon( player, slot )
  539.         if weapon > 0 then
  540.             local ammo = getPedTotalAmmo( player, slot )
  541.             if ammo > 0 then
  542.                 weapons[weapon] = ammo
  543.                 hasAnyWeapons = true
  544.             end
  545.         end
  546.     end
  547.     if hasAnyWeapons then
  548.         return "'" .. exports.sql:escape_string( toJSON( weapons ):gsub( " ", "" ) ) .. "'"
  549.     else
  550.         return "NULL"
  551.     end
  552. end
  553.  
  554. local function savePlayer( player )
  555.     if not player then
  556.         for key, value in ipairs( getElementsByType( "player" ) ) do
  557.             savePlayer( value )
  558.         end
  559.     else
  560.         if isLoggedIn( player ) then
  561.             -- save character since it's logged in
  562.             local x, y, z = getElementPosition( player )
  563.             local dimension = getElementDimension( player )
  564.             local interior = getElementInterior( player )
  565.            
  566.             if hasObjectPermissionTo( player, "command.spectate", false ) and type( getElementData( player, "collisionless" ) ) == "table" then
  567.                 -- spectating
  568.                 x, y, z, dimension, interior = unpack( getElementData( player, "collisionless" ) )
  569.             end
  570.            
  571.             exports.sql:query_free( "UPDATE characters SET x = " .. x .. ", y = " .. y .. ", z = " .. z .. ", dimension = " .. dimension .. ", interior = " .. interior .. ", rotation = " .. getPedRotation( player ) .. ", health = " .. math.floor( getElementHealth( player ) ) .. ", armor = " .. math.floor( getPedArmor( player ) ) .. ", weapons = " .. getWeaponString( player ) .. ", lastLogin = NOW() WHERE characterID = " .. tonumber( getCharacterID( player ) ) )
  572.         end
  573.     end
  574. end
  575. setTimer( savePlayer, 300000, 0 ) -- Auto-Save every five minutes
  576.  
  577. addEventHandler( "onResourceStop", resourceRoot,
  578.     function( )
  579.         -- logout all players
  580.         for key, value in ipairs( getElementsByType( "player" ) ) do
  581.             savePlayer( value )
  582.            
  583.             if p[ value ] and p[ value ].charID then
  584.                 triggerEvent( "onCharacterLogout", value )
  585.                 setPlayerTeam( value, nil )
  586.                 takeAllWeapons( value )
  587.             end
  588.            
  589.             if not isGuestAccount( getPlayerAccount( value ) ) then
  590.                 logOut( value )
  591.             end
  592.         end
  593.     end
  594. )
  595.  
  596. addEvent( getResourceName( resource ) .. ":logout", true )
  597. addEventHandler( getResourceName( resource ) .. ":logout", root,
  598.     function( )
  599.         if source == client then
  600.             savePlayer( source )
  601.             if p[ source ].charID then
  602.                 triggerEvent( "onCharacterLogout", source )
  603.                 setPlayerTeam( source, nil )
  604.                 takeAllWeapons( source )
  605.             end
  606.             p[ source ] = nil
  607.             showLoginScreen( source )
  608.            
  609.             if not isGuestAccount( getPlayerAccount( source ) ) then
  610.                 logOut( source )
  611.             end
  612.         end
  613.     end
  614. )
  615.  
  616. addEventHandler( "onPlayerJoin", root,
  617.     function( )
  618.         setPlayerNametagColor( source, 127, 127, 127 )
  619.     end
  620. )
  621.  
  622. addEventHandler( "onPlayerQuit", root,
  623.     function( )
  624.         if p[ source ] then
  625.             savePlayer( source )
  626.             if p[ source ].charID then
  627.                 triggerEvent( "onCharacterLogout", source )
  628.             end
  629.             p[ source ] = nil
  630.             loginAttempts[ source ] = nil
  631.             triedTokenAuth[ source ] = nil
  632.         end
  633.     end
  634. )
  635.  
  636. addEvent( getResourceName( resource ) .. ":spawn", true )
  637. addEventHandler( getResourceName( resource ) .. ":spawn", root,
  638.     function( charID )
  639.         if source == client and ( not isPedDead( source ) or not isLoggedIn( source ) ) then
  640.             local userID = p[ source ] and p[ source ].userID
  641.             if tonumber( userID ) and tonumber( charID ) then
  642.                 -- if the player is logged in, save him
  643.                 savePlayer( source )
  644.                 if p[ source ].charID then
  645.                     triggerEvent( "onCharacterLogout", source )
  646.                     setPlayerTeam( source, nil )
  647.                     takeAllWeapons( source )
  648.                     p[ source ].charID = nil
  649.                     p[ source ].money = nil
  650.                     p[ source ].job = nil
  651.                 end
  652.                
  653.                 --
  654.                 local char = exports.sql:query_assoc_single( "SELECT * FROM characters WHERE userID = " .. tonumber( userID ) .. " AND characterID = " .. tonumber( charID ) )
  655.                 if char then
  656.                     local mtaCharName = char.characterName:gsub( " ", "_" )
  657.                     local otherPlayer = getPlayerFromName( mtaCharName )
  658.                     if otherPlayer and otherPlayer ~= source then
  659.                         kickPlayer( otherPlayer )
  660.                     end
  661.                     setPlayerName( source, mtaCharName )
  662.                    
  663.                     -- spawn the player, as it's a valid char
  664.                     spawnPlayer( source, char.x, char.y, char.z, char.rotation, char.skin, char.interior, char.dimension )
  665.                     fadeCamera( source, true )
  666.                     setCameraTarget( source, source )
  667.                     setCameraInterior( source, char.interior )
  668.                    
  669.                     toggleAllControls( source, true, true, false )
  670.                     setPedFrozen( source, false )
  671.                     setElementAlpha( source, 255 )
  672.                    
  673.                     setElementHealth( source, char.health )
  674.                     setPedArmor( source, char.armor )
  675.                    
  676.                     p[ source ].money = char.money
  677.                     setPlayerMoney( source, char.money )
  678.                    
  679.                     p[ source ].charID = tonumber( charID )
  680.                     p[ source ].characterName = char.characterName
  681.                     updateNametag( source )
  682.                    
  683.                     -- restore weapons
  684.                     if char.weapons then
  685.                         local weapons = fromJSON( char.weapons )
  686.                         if weapons then
  687.                             for weapon, ammo in pairs( weapons ) do
  688.                                 giveWeapon( source, weapon, ammo )
  689.                             end
  690.                         end
  691.                     end
  692.                    
  693.                     p[ source ].job = char.job
  694.                    
  695.                     -- restore the player's languages, remove invalid ones
  696.                     p[ source ].languages = fromJSON( char.languages )
  697.                     if not p[ source ].languages then
  698.                         -- default is English with full skill
  699.                         p[ source ].languages = { en = { skill = 1000, current = true } }
  700.                         saveLanguages( source, p[ source ].languages )
  701.                     else
  702.                         local changed = false
  703.                         local languages = 0
  704.                         for key, value in pairs( p[ source ].languages ) do
  705.                             if isValidLanguage( "en" ) then
  706.                                 changed = true
  707.                                 languages = languages + 1
  708.                                 if not isValidLanguage( key ) then
  709.                                     p[ source ].languages[ key ] = nil
  710.                                     languages = languages - 1
  711.                                 elseif type( value.skill ) ~= 'number' then
  712.                                     value.skill = 0
  713.                                 elseif value.skill < 0 then
  714.                                     value.skill = 0
  715.                                 elseif value.skill > 1000 then
  716.                                     value.skill = 1000
  717.                                 else
  718.                                     changed = false
  719.                                 end
  720.                             else
  721.                                 languages = languages + 1
  722.                             end
  723.                         end
  724.                        
  725.                         if languages == 0 then
  726.                             -- player has no language at all
  727.                             p[ source ].languages = { en = { skill = 1000, current = true } }
  728.                             changed = true
  729.                         end
  730.                        
  731.                         if changed then
  732.                             saveLanguages( source, p[ source ].languages )
  733.                         end
  734.                     end
  735.                    
  736.                     setPlayerTeam( source, team )
  737.                     triggerClientEvent( source, getResourceName( resource ) .. ":onSpawn", source, p[ source ].languages )
  738.                     triggerEvent( "onCharacterLogin", source )
  739.                    
  740.                     showCursor( source, false )
  741.                    
  742.                     -- set last login to now
  743.                     exports.sql:query_free( "UPDATE characters SET lastLogin = NOW() WHERE characterID = " .. tonumber( charID ) )
  744.                    
  745.                     outputServerLog( "PARADISE CHARACTER: " .. p[ source ].username .. " is now playing as " .. char.characterName )
  746.                     exports.server:message( "%C04[" .. getID( source ) .. "]%C %B" .. p[ source ].username .. "%B is now playing in as %B" .. char.characterName .. "%B." )
  747.                 end
  748.             end
  749.         end
  750.     end
  751. )
  752.  
  753. addEventHandler( "onPlayerChangeNick", root,
  754.     function( )
  755.         if isLoggedIn( source ) then
  756.             cancelEvent( )
  757.         end
  758.     end
  759. )
  760.  
  761. -- exports
  762. function getCharacterID( player )
  763.     return player and p[ player ] and p[ player ].charID or false
  764. end
  765.  
  766. function isLoggedIn( player )
  767.     return getCharacterID( player ) and true or false
  768. end
  769.  
  770. function getUserID( player )
  771.     return player and p[ player ] and p[ player ].userID or false
  772. end
  773.  
  774. function getUserName( player )
  775.     return player and p[ player ] and p[ player ].username or false
  776. end
  777.  
  778. -- retrieves a character name from the database id
  779. function getCharacterName( characterID )
  780.     if type( characterID ) == "number" then
  781.         -- check if the player is online, if so we don't need to query
  782.         for player, data in pairs( p ) do
  783.             if data.charID == characterID then
  784.                 local name = getPlayerName( player ):gsub( "_", " " )
  785.                 return name
  786.             end
  787.         end
  788.        
  789.         local data = exports.sql:query_assoc_single( "SELECT characterName FROM characters WHERE characterID = " .. characterID )
  790.         if data then
  791.             return data.characterName
  792.         end
  793.     end
  794.     return false
  795. end
  796.  
  797. -- money functions
  798. function setMoney( player, amount )
  799.     amount = tonumber( amount )
  800.     if amount >= 0 and isLoggedIn( player ) then
  801.         if exports.sql:query_free( "UPDATE characters SET money = " .. amount .. " WHERE characterID = " .. p[ player ].charID ) then
  802.             p[ player ].money = amount
  803.             setPlayerMoney( player, amount )
  804.             return true
  805.         end
  806.     end
  807.     return false
  808. end
  809.  
  810. function giveMoney( player, amount )
  811.     return amount >= 0 and setMoney( player, getMoney( player ) + amount )
  812. end
  813.  
  814. function takeMoney( player, amount )
  815.     return amount >= 0 and setMoney( player, getMoney( player ) - amount )
  816. end
  817.  
  818. function getMoney( player, amount )
  819.     return isLoggedIn( player ) and p[ player ].money or 0
  820. end
  821.  
  822. --
  823.  
  824. function updateCharacters( player )
  825.     if player and p[ player ].userID then
  826.         local chars = exports.sql:query_assoc( "SELECT characterID, characterName, skin FROM characters WHERE userID = " .. p[ player ].userID .. " ORDER BY lastLogin DESC" )
  827.         triggerClientEvent( player, getResourceName( resource ) .. ":characters", player, chars, false )
  828.         return true
  829.     end
  830.     return false
  831. end
  832.  
  833. function createCharacter( player, name, skin )
  834.     if player and p[ player ].userID then
  835.         if exports.sql:query_assoc_single( "SELECT characterID FROM characters WHERE characterName = '%s'", name ) then
  836.             triggerClientEvent( player, "players:characterCreationResult", player, 1 )
  837.         elseif exports.sql:query_free( "INSERT INTO characters (characterName, userID, x, y, z, interior, dimension, skin, rotation) VALUES ('%s', " .. p[ player ].userID .. ", -1984.5, 138, 27.7, 0, 0, " .. tonumber( skin ) .. ", 270)", name ) then
  838.             updateCharacters( player )
  839.             triggerClientEvent( player, "players:characterCreationResult", player, 0 )
  840.            
  841.             exports.server:message( "%C04[" .. getID( player ) .. "]%C %B" .. p[ player ].username .. "%B created character %B" .. name .. "%B." )
  842.            
  843.             return true
  844.         end
  845.     end
  846.     return false
  847. end
  848.  
  849. --
  850.  
  851. function updateNametag( player )
  852.     if player then
  853.         local text = "[" .. getID( player ) .. "] "
  854.         local vehicle = getPedOccupiedVehicle( player )
  855.         if vehicle and exports.vehicles:hasTintedWindows( vehicle ) then
  856.             text = text .. "? (Tinted Windows)"
  857.         else
  858.             text = text .. ( p[ player ] and p[ player ].characterName or getPlayerName( player ):gsub( "_", " " ) )
  859.         end
  860.        
  861.         if getPlayerNametagText( player ) ~= tostring( text ) then
  862.             setPlayerNametagText( player, tostring( text ) )
  863.         end
  864.         updateNametagColor( player )
  865.         return true
  866.     end
  867.     return false
  868. end
  869.  
  870. addEventHandler( "onPlayerSpawn", root,
  871.     function( )
  872.         updateNametag( source )
  873.     end
  874. )
  875.  
  876. setTimer(
  877.     function( )
  878.         for player in pairs( p ) do
  879.             updateNametag( player )
  880.         end
  881.     end,
  882.     15000,
  883.     0
  884. )
  885.  
  886. --
  887.  
  888. function getJob( player )
  889.     return isLoggedIn( player ) and p[ player ].job or nil
  890. end
  891.  
  892. function setJob( player, job )
  893.     local charID = getCharacterID( player )
  894.     if charID and exports.sql:query_free( "UPDATE characters SET job = '%s' WHERE characterID = " .. charID, job ) then
  895.         p[ player ].job = job
  896.         return true
  897.     end
  898.     return false
  899. end
  900.  
  901. --
  902.  
  903. function getOption( player, key )
  904.     return player and p[ player ] and p[ player ].options and key and p[ player ].options[ key ] or nil
  905. end
  906.  
  907. function setOption( player, key, value )
  908.     if player and p[ player ] and p[ player ].options and type( key ) == 'string' then
  909.         -- update the option
  910.         local oldValue = p[ player ].options[ key ]
  911.         p[ player ].options[ key ] = value
  912.        
  913.        
  914.         local str = toJSON( p[ player ].options )
  915.         if str then
  916.             if str == toJSON( { } ) then
  917.                 local success = exports.sql:query_free( "UPDATE wcf1_user SET userOptions = NULL WHERE userID = " .. getUserID( player ) )
  918.                 return success
  919.             elseif exports.sql:query_free( "UPDATE wcf1_user SET userOptions = '%s' WHERE userID = " .. getUserID( player ), str ) then
  920.                 return true
  921.             end
  922.         end
  923.        
  924.         -- if it failed, restore the old value
  925.         p[ player ].options[ key ] = oldValue
  926.     end
  927.     return false
  928. end
Add Comment
Please, Sign In to add comment