--[[ | This file was obtained through the combined efforts | of Madbluntz & Plymouth Antiquarian Society. | | Credits: lifestorm, Gregory Wayne Rossel JR., | Maloy, DrPepper10 @ RIP, Atle! | | Visit for more: https://plymouth.thetwilightzone.ru/ --]] -- PLEASE READ -- PLEASE READ -- PLEASE READ -- PLEASE READ -- Set the timing module to import times from when importing users -- Supported modules: "utime" -- If your timing module is not here then STOP and make a support ticket so I can add support for it before you import your users local TIME_MODULE = "utime" -- Set this to nil if you don't want to import times -- PLEASE READ -- PLEASE READ -- PLEASE READ -- PLEASE READ --------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------- local sam, SQL = sam, sam.SQL local ulib_unescape_backslash = function(s) return s:gsub("\\\\", "\\") end local ulib_explode = function(separator, str, limit) local t = {} local curpos = 1 while true do -- We have a break in the loop local newpos, endpos = str:find(separator, curpos) -- find the next separator in the string if newpos ~= nil then -- if found then.. table.insert(t, str:sub(curpos, newpos - 1)) -- Save it in our table. curpos = endpos + 1 -- save just after where we found it for searching next time. else if limit and #t > limit then return t -- Reached limit end table.insert(t, str:sub(curpos)) -- Save what's left in our array. break end end return t end local ulib_split_args = function(args, start_token, end_token) args = args:Trim() local argv = {} local curpos = 1 -- Our current position within the string local in_quote = false -- Is the text we're currently processing in a quote? start_token = start_token or "\"" end_token = end_token or "\"" local args_len = args:len() while in_quote or curpos <= args_len do local quotepos = args:find(in_quote and end_token or start_token, curpos, true) -- The string up to the quote, the whole string if no quote was found local prefix = args:sub(curpos, (quotepos or 0) - 1) if not in_quote then local trimmed = prefix:Trim() if trimmed ~= "" then -- Something to be had from this... local t = ulib_explode("%s+", trimmed) table.Add(argv, t) end else table.insert(argv, prefix) end -- If a quote was found, reduce our position and note our state if quotepos ~= nil then curpos = quotepos + 1 in_quote = not in_quote else -- Otherwise we've processed the whole string now break end end return argv, in_quote end local ulib_parse_key_values = function(str, convert) local lines = ulib_explode("\r?\n", str) local parent_tables = {} -- Traces our way to root local current_table, n = {}, 0 local is_insert_last_op = false local tmp_string = string.char(01, 02, 03) -- Replacement for i, line in ipairs(lines) do local tokens = ulib_split_args((line:gsub("\\\"", tmp_string))) for i, token in ipairs(tokens) do tokens[ i ] = ulib_unescape_backslash(token):gsub(tmp_string, "\"") end local num_tokens = #tokens if num_tokens == 1 then local token = tokens[ 1 ] if token == "{" then local new_table = {} if is_insert_last_op then current_table[ table.remove(current_table) ] = new_table else table.insert(current_table, new_table) end is_insert_last_op = false table.insert(parent_tables, current_table) current_table = new_table elseif token == "}" then is_insert_last_op = false current_table = table.remove(parent_tables) if current_table == nil then return nil, "Mismatched recursive tables on line " .. i end else is_insert_last_op = true table.insert(current_table, tokens[ 1 ]) end elseif num_tokens == 2 then is_insert_last_op = false if convert and tonumber(tokens[ 1 ]) then tokens[ 1 ] = tonumber(tokens[ 1 ]) end current_table[ tokens[ 1 ] ] = tokens[ 2 ] elseif num_tokens > 2 then return nil, "Bad input on line " .. i end end if #parent_tables ~= 0 then return nil, "Mismatched recursive tables" end if convert and n == 1 and type(current_table.Out) == "table" then -- If we caught a stupid garry-wrapper current_table = current_table.Out end return current_table end local ulib_remove_comment_header = function(data, comment_char) comment_char = comment_char or ";" local lines = ulib_explode("\r?\n", data) local end_comment_line = 0 for _, line in ipairs(lines) do local trimmed = line:Trim() if trimmed == "" or trimmed:sub(1, 1) == comment_char then end_comment_line = end_comment_line + 1 else break end end local not_comment = table.concat(lines, "\n", end_comment_line + 1) return not_comment:Trim() end local no_bans_msg = "No bans to import from ulx." local import_bans = function() sam.print("Importing bans...") if not sql.TableExists("ulib_bans") then return sam.print(no_bans_msg) end local bans = sql.Query("SELECT `steamid`, `unban`, `reason`, `admin` FROM `ulib_bans`") if not sam.istable(bans) then return sam.print(no_bans_msg) end local bans_count = #bans if bans_count == 0 then return sam.print(no_bans_msg) end local began, imported_count = false, 0 for i = 1, bans_count do local ban = bans[i] local steamid = ban.steamid if sam.is_steamid64(steamid) then steamid = util.SteamIDFrom64(ban.steamid) end if sam.is_steamid(steamid) then if not began then began = true SQL.Begin() SQL.Query([[ DELETE FROM `sam_bans` ]]) end local name, reason, admin, unban_date = ban.name, ban.reason, ban.admin, tonumber(ban.unban) if name == "NULL" then name = "" end if reason == "NULL" then reason = "none" end if sam.isstring(admin) and admin ~= "NULL" and admin ~= "(Console)" then admin = admin:match("%(STEAM_%w:%w:%w*%)"):sub(2, -2) else admin = "Console" end if not sam.isnumber(unban_date) or unban_date < 0 then unban_date = 0 end SQL.FQuery([[ INSERT INTO `sam_bans`( `steamid`, `reason`, `admin`, `unban_date` ) VALUES ({1}, {2}, {3}, {4}) ]], {steamid, reason, admin, unban_date}) imported_count = imported_count + 1 end end if began then SQL.Commit(function() sam.print("Imported " .. imported_count .. " ban(s).") end) else sam.print(no_bans_msg) end end local groups_path = "ulib/groups.txt" local no_ranks_msg = "No ranks to import from ulx." local imported_ranks local function add_rank(rank, info, ranks) if not sam.ranks.is_rank(rank) then local inherit = info.inherit_from if inherit and ranks[inherit] and not sam.ranks.is_rank(inherit) then add_rank(inherit, ranks[inherit], ranks) end imported_ranks = imported_ranks + 1 sam.ranks.add_rank(rank, inherit) end end local import_ranks = function() sam.print("Importing ranks...") if not file.Exists(groups_path, "DATA") then return sam.print(no_ranks_msg) end local ranks_text = file.Read(groups_path, "DATA") if not sam.isstring(ranks_text) then return sam.print(no_ranks_msg) end local ranks, err = ulib_parse_key_values(ulib_remove_comment_header(ranks_text, "/")) if not ranks then return sam.print("ULX ranks file was not formatted correctly, make a support ticket. (include the error message if there is one)" .. (err and (" | error: " .. err) or "")) end imported_ranks = 0 for rank, info in pairs(ranks) do if not sam.ranks.is_rank(rank) then add_rank(rank, info, ranks) end end if imported_ranks == 0 then sam.print(no_ranks_msg) else sam.print("Imported " .. imported_ranks .. " rank(s).") end end local users_path = "ulib/users.txt" local no_users_msg = "No users to import from ulx." local import_users = function() sam.print("Importing users...") if not file.Exists(users_path, "DATA") then return sam.print(no_users_msg) end local users_text = file.Read(users_path, "DATA") if not sam.isstring(users_text) then return sam.print(no_users_msg) end local users, err = ulib_parse_key_values(ulib_remove_comment_header(users_text, "/")) if not users then return sam.print("ULX users file was not formatted correctly, make a support ticket. (include the error message if there is one)" .. (err and (" | error: " .. err) or "")) end local current_time = os.time() local imported_users = 0 local times = false if TIME_MODULE == "utime" then local utime = sql.Query("SELECT `player`, `lastvisit`, `totaltime` FROM `utime`") if sam.istable(utime) then times = {} for i = 1, #utime do local v = utime[i] times[v.player] = v end end end local began = false for steamid, info in pairs(users) do if sam.is_steamid64(steamid) then steamid = util.SteamIDFrom64(steamid) end if sam.is_steamid(steamid) then if not began then began = true SQL.Begin() SQL.Query([[ DELETE FROM `sam_players` ]]) end local first_join, last_join, play_time = current_time, current_time, 0 if times and TIME_MODULE == "utime" then local utime_user = times[util.CRC("gm_" .. steamid .. "_gm")] if utime_user then last_join, play_time = utime_user.lastvisit, math.floor(utime_user.totaltime) first_join = last_join end end local rank = info.group if not rank or not sam.ranks.is_rank(rank) then rank = "user" end SQL.FQuery([[ INSERT INTO `sam_players`( `steamid`, `name`, `rank`, `expiry_date`, `first_join`, `last_join`, `play_time` ) VALUES ({1}, {2}, {3}, {4}, {5}, {6}, {7}) ]], {steamid, info.name or "", rank, 0, first_join, last_join, play_time}) imported_users = imported_users + 1 end end if began then SQL.Commit(function() sam.print("Imported " .. imported_users .. " user(s).") end) else sam.print(no_users_msg) end end if not sam.ranks.ranks_loaded() then return sam.print("Server is connecting to the Database, try again when it connects.") end for k, v in ipairs(player.GetAll()) do v:Kick("Importing data.") end for k, v in pairs(sam.get_connected_players()) do sam.player.kick_id(k, "Importing data.") end hook.Add("CheckPassword", "SAM.ImportingData", function() return false, "Importing data." end) sam.print("Starting to import data from ulx...") timer.Simple(0, function() import_bans() import_ranks() import_users() hook.Remove("CheckPassword", "SAM.ImportingData") end)