Initial commit
This commit is contained in:
432
gamemodes/darkrp/gamemode/modules/base/sh_util.lua
Normal file
432
gamemodes/darkrp/gamemode/modules/base/sh_util.lua
Normal file
@@ -0,0 +1,432 @@
|
||||
--[[---------------------------------------------------------------------------
|
||||
Utility functions
|
||||
---------------------------------------------------------------------------]]
|
||||
|
||||
local vector = FindMetaTable("Vector")
|
||||
local meta = FindMetaTable("Player")
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Decides whether the vector could be seen by the player if they were to look at it
|
||||
---------------------------------------------------------------------------]]
|
||||
function vector:isInSight(filter, ply)
|
||||
ply = ply or LocalPlayer()
|
||||
local trace = {}
|
||||
trace.start = ply:EyePos()
|
||||
trace.endpos = self
|
||||
trace.filter = filter
|
||||
trace.mask = -1
|
||||
local TheTrace = util.TraceLine(trace)
|
||||
|
||||
return not TheTrace.Hit, TheTrace.HitPos
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Turn a money amount into a pretty string
|
||||
---------------------------------------------------------------------------]]
|
||||
local function attachCurrency(str)
|
||||
local config = GAMEMODE.Config
|
||||
return config.currencyLeft and config.currency .. str or str .. config.currency
|
||||
end
|
||||
|
||||
function DarkRP.formatMoney(n)
|
||||
if not n then return attachCurrency("0") end
|
||||
|
||||
if n >= 1e14 then return attachCurrency(tostring(n)) end
|
||||
if n <= -1e14 then return "-" .. attachCurrency(tostring(math.abs(n))) end
|
||||
|
||||
local config = GAMEMODE.Config
|
||||
|
||||
local negative = n < 0
|
||||
|
||||
n = tostring(math.abs(n))
|
||||
|
||||
local dp = string.find(n, ".", 1, true) or #n + 1
|
||||
|
||||
for i = dp - 4, 1, -3 do
|
||||
n = n:sub(1, i) .. config.currencyThousandSeparator .. n:sub(i + 1)
|
||||
end
|
||||
|
||||
-- Make sure the amount is padded with zeroes
|
||||
if n[#n - 1] == "." then
|
||||
n = n .. "0"
|
||||
end
|
||||
|
||||
return (negative and "-" or "") .. attachCurrency(n)
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Find a player based on given information
|
||||
|
||||
Note that there is a searching priority:
|
||||
* UserID
|
||||
* SteamID64
|
||||
* SteamID
|
||||
* Nick
|
||||
* SteamName
|
||||
|
||||
Note also that there are _separate_ loops. This is to make sure the function
|
||||
gives the same result, regardless of the order in which players are iterated
|
||||
over.
|
||||
---------------------------------------------------------------------------]]
|
||||
function DarkRP.findPlayer(info)
|
||||
if not info or info == "" then return nil end
|
||||
local pls = player.GetAll()
|
||||
|
||||
local count = #pls
|
||||
local numberInfo = tonumber(info)
|
||||
|
||||
-- First check if the input matches a player by UserID or SteamID64. This is
|
||||
-- only necessary if the input can be parsed as a number.
|
||||
if numberInfo then
|
||||
for k = 1, count do
|
||||
local v = pls[k]
|
||||
|
||||
if numberInfo == v:UserID() then
|
||||
return v
|
||||
end
|
||||
end
|
||||
|
||||
for k = 1, count do
|
||||
local v = pls[k]
|
||||
|
||||
if info == v:SteamID64() then
|
||||
return v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local lowerInfo = string.lower(tostring(info))
|
||||
if string.StartsWith(lowerInfo, "steam_") then
|
||||
for k = 1, count do
|
||||
local v = pls[k]
|
||||
|
||||
if info == v:SteamID() then
|
||||
return v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for k = 1, count do
|
||||
local v = pls[k]
|
||||
|
||||
if string.find(string.lower(v:Nick()), lowerInfo, 1, true) ~= nil then
|
||||
return v
|
||||
end
|
||||
end
|
||||
|
||||
for k = 1, count do
|
||||
local v = pls[k]
|
||||
|
||||
if string.find(string.lower(v:SteamName()), lowerInfo, 1, true) ~= nil then
|
||||
return v
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Find multiple players based on a string criterium
|
||||
Taken from FAdmin]]
|
||||
---------------------------------------------------------------------------*/
|
||||
function DarkRP.findPlayers(info)
|
||||
if not info then return nil end
|
||||
local pls = player.GetAll()
|
||||
local found = {}
|
||||
local players
|
||||
|
||||
if string.lower(info) == "*" or string.lower(info) == "<all>" then return pls end
|
||||
|
||||
local InfoPlayers = {}
|
||||
for A in string.gmatch(info .. ";", "([a-zA-Z0-9:_.]*)[;(,%s)%c]") do
|
||||
if A ~= "" then
|
||||
table.insert(InfoPlayers, A)
|
||||
end
|
||||
end
|
||||
|
||||
for _, PlayerInfo in ipairs(InfoPlayers) do
|
||||
-- Playerinfo is always to be treated as UserID when it's a number
|
||||
-- otherwise people with numbers in their names could get confused with UserID's of other players
|
||||
if tonumber(PlayerInfo) then
|
||||
local foundPlayer = Player(PlayerInfo)
|
||||
if IsValid(foundPlayer) and not found[foundPlayer] then
|
||||
found[foundPlayer] = true
|
||||
players = players or {}
|
||||
table.insert(players, foundPlayer)
|
||||
end
|
||||
continue
|
||||
end
|
||||
|
||||
local stringPlayerInfo = string.lower(PlayerInfo)
|
||||
for _, v in ipairs(pls) do
|
||||
-- Prevent duplicates
|
||||
if found[v] then continue end
|
||||
local steamId = v:SteamID()
|
||||
|
||||
-- Find by Steam ID
|
||||
if (PlayerInfo == steamId or steamId == "UNKNOWN") or
|
||||
-- Find by Partial Nick
|
||||
string.find(string.lower(v:Nick()), stringPlayerInfo, 1, true) ~= nil or
|
||||
-- Find by steam name
|
||||
(v.SteamName and string.find(string.lower(v:SteamName()), stringPlayerInfo, 1, true) ~= nil) then
|
||||
found[v] = true
|
||||
players = players or {}
|
||||
table.insert(players, v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return players
|
||||
end
|
||||
|
||||
function meta:getEyeSightHitEntity(searchDistance, hitDistance, filter)
|
||||
searchDistance = searchDistance or 100
|
||||
hitDistance = (hitDistance or 15) * (hitDistance or 15)
|
||||
|
||||
filter = filter or function(p) return p:IsPlayer() and p ~= self end
|
||||
|
||||
self:LagCompensation(true)
|
||||
|
||||
local shootPos = self:GetShootPos()
|
||||
local entities = ents.FindInSphere(shootPos, searchDistance)
|
||||
local aimvec = self:GetAimVector()
|
||||
|
||||
local smallestDistance = math.huge
|
||||
local foundEnt
|
||||
|
||||
for _, ent in ipairs(entities) do
|
||||
if not IsValid(ent) or filter(ent) == false then continue end
|
||||
|
||||
local center = ent:GetPos()
|
||||
|
||||
-- project the center vector on the aim vector
|
||||
local projected = shootPos + (center - shootPos):Dot(aimvec) * aimvec
|
||||
|
||||
if aimvec:Dot((projected - shootPos):GetNormalized()) < 0 then continue end
|
||||
|
||||
-- the point on the model that has the smallest distance to your line of sight
|
||||
local nearestPoint = ent:NearestPoint(projected)
|
||||
local distance = nearestPoint:DistToSqr(projected)
|
||||
|
||||
if distance < smallestDistance then
|
||||
local trace = {
|
||||
start = self:GetShootPos(),
|
||||
endpos = nearestPoint,
|
||||
filter = {self, ent}
|
||||
}
|
||||
local traceLine = util.TraceLine(trace)
|
||||
if traceLine.Hit then continue end
|
||||
|
||||
smallestDistance = distance
|
||||
foundEnt = ent
|
||||
end
|
||||
end
|
||||
|
||||
self:LagCompensation(false)
|
||||
|
||||
if smallestDistance < hitDistance then
|
||||
return foundEnt, math.sqrt(smallestDistance)
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Print the currently available vehicles
|
||||
---------------------------------------------------------------------------]]
|
||||
local function GetAvailableVehicles(ply)
|
||||
if SERVER and IsValid(ply) and not ply:IsAdmin() then return end
|
||||
local print = SERVER and ServerLog or Msg
|
||||
|
||||
print(DarkRP.getPhrase("rp_getvehicles") .. "\n")
|
||||
for k in pairs(DarkRP.getAvailableVehicles()) do
|
||||
print("\"" .. k .. "\"" .. "\n")
|
||||
end
|
||||
end
|
||||
if SERVER then
|
||||
concommand.Add("rp_getvehicles_sv", GetAvailableVehicles)
|
||||
else
|
||||
concommand.Add("rp_getvehicles", GetAvailableVehicles)
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Whether a player has a DarkRP privilege
|
||||
---------------------------------------------------------------------------]]
|
||||
function meta:hasDarkRPPrivilege(priv)
|
||||
if FAdmin then
|
||||
return FAdmin.Access.PlayerHasPrivilege(self, priv)
|
||||
end
|
||||
return self:IsAdmin()
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Convenience function to return the players sorted by name
|
||||
---------------------------------------------------------------------------]]
|
||||
function DarkRP.nickSortedPlayers()
|
||||
local plys = player.GetAll()
|
||||
table.sort(plys, function(a,b) return a:Nick() < b:Nick() end)
|
||||
return plys
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Convert a string to a table of arguments
|
||||
---------------------------------------------------------------------------]]
|
||||
local bitlshift, stringgmatch, stringsub, tableinsert = bit.lshift, string.gmatch, string.sub, table.insert
|
||||
function DarkRP.explodeArg(arg)
|
||||
local args = {}
|
||||
|
||||
local from, to, diff = 1, 0, 0
|
||||
local inQuotes, wasQuotes = false, false
|
||||
|
||||
for c in stringgmatch(arg, '.') do
|
||||
to = to + 1
|
||||
|
||||
if c == '"' then
|
||||
inQuotes = not inQuotes
|
||||
wasQuotes = true
|
||||
|
||||
continue
|
||||
end
|
||||
|
||||
if c == ' ' and not inQuotes then
|
||||
diff = wasQuotes and 1 or 0
|
||||
wasQuotes = false
|
||||
tableinsert(args, stringsub(arg, from + diff, to - 1 - diff))
|
||||
from = to + 1
|
||||
end
|
||||
end
|
||||
diff = wasQuotes and 1 or 0
|
||||
|
||||
if from ~= to + 1 then tableinsert(args, stringsub(arg, from + diff, to + 1 - bitlshift(diff, 1))) end
|
||||
|
||||
return args
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Initialize Physics, throw an error on failure
|
||||
---------------------------------------------------------------------------]]
|
||||
function DarkRP.ValidatedPhysicsInit(ent, solidType, hint)
|
||||
solidType = solidType or SOLID_VPHYSICS
|
||||
|
||||
if ent:PhysicsInit(solidType) then return true end
|
||||
|
||||
local class = ent:GetClass()
|
||||
|
||||
if solidType == SOLID_BSP then
|
||||
DarkRP.errorNoHalt(string.format("%s has no physics and will be motionless", class), 2, {
|
||||
"Is this a brush model? SOLID_BSP physics cannot initialize on entities that don't have brush models",
|
||||
"The physics limit may have been hit",
|
||||
hint
|
||||
})
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
if solidType == SOLID_VPHYSICS then
|
||||
local mdl = ent:GetModel()
|
||||
|
||||
if not mdl or mdl == "" then
|
||||
DarkRP.errorNoHalt(string.format("Cannot init physics on entity \"%s\" because it has no model", class), 2, {hint})
|
||||
return false
|
||||
end
|
||||
|
||||
mdl = string.lower(mdl)
|
||||
|
||||
if util.IsValidProp(mdl) then
|
||||
-- Has physics, we must have hit the limit
|
||||
DarkRP.errorNoHalt(string.format("physics limit hit - %s will be motionless", class), 2, {hint})
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
if not file.Exists(mdl, "GAME") then
|
||||
DarkRP.errorNoHalt(string.format("%s has missing model \"%s\" and will be invisible and motionless", class, mdl), 2, {
|
||||
"Is the model path correct?",
|
||||
"Is the model from an addon that is not installed?",
|
||||
"Is the model from a game that isn't (properly) mounted? E.g. Counter Strike: Source",
|
||||
hint
|
||||
})
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
DarkRP.errorNoHalt(string.format("%s has model \"%s\" with no physics and will be motionless", class, mdl), 2, {
|
||||
"Does this model have an associated physics model (modelname.phy)?",
|
||||
"Is this model supposed to have physics? Many models, like effects and view models aren't made to have physics",
|
||||
hint
|
||||
})
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
DarkRP.errorNoHalt(string.format("Unable to initilize physics on entity \"%s\"", class, {hint}), 2)
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Like tonumber, but makes sure it's an integer
|
||||
---------------------------------------------------------------------------]]
|
||||
function DarkRP.toInt(value)
|
||||
value = tonumber(value)
|
||||
return value and math.floor(value)
|
||||
end
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Check the database for integrity errors. Use in cases when stuff doesn't load
|
||||
on restart, or you get corruption errors.
|
||||
---------------------------------------------------------------------------]]
|
||||
if SERVER then util.AddNetworkString("DarkRP_databaseCheckMessage") end
|
||||
if CLIENT then net.Receive("DarkRP_databaseCheckMessage", fc{print, net.ReadString}) end
|
||||
|
||||
local function checkDatabase(ply)
|
||||
local dbFile = SERVER and "sv.db" or "cl.db"
|
||||
local display = (CLIENT or not IsValid(ply)) and print or function(msg)
|
||||
net.Start("DarkRP_databaseCheckMessage")
|
||||
net.WriteString(msg)
|
||||
net.Send(ply)
|
||||
end
|
||||
|
||||
if SERVER and IsValid(ply) and not ply:IsSuperAdmin() then
|
||||
display("You must be superadmin")
|
||||
return
|
||||
end
|
||||
|
||||
if MySQLite and MySQLite.isMySQL() then
|
||||
display(string.format([[WARNING: DarkRP is using MySQL. This only
|
||||
checks the local SQLite database stored in the %s file in the
|
||||
garrysmod/ folder. The check will continue.]], dbFile))
|
||||
end
|
||||
|
||||
local check = sql.QueryValue("PRAGMA INTEGRITY_CHECK")
|
||||
if check == false then
|
||||
display([[The query to check the database failed. Shit's surely
|
||||
fucked, but the cause is unknown.]])
|
||||
return
|
||||
end
|
||||
|
||||
if check == "ok" then
|
||||
display(string.format("Your %s database file is good.", dbFile))
|
||||
return
|
||||
end
|
||||
|
||||
display(string.format([[There are errors in your %s database file. It's corrupt!
|
||||
|
||||
This can cause the following problems:
|
||||
- Data not loading, think of blocked models, doors, players' money and RP names
|
||||
- Settings resetting to their default values
|
||||
- Lua errors on startup
|
||||
|
||||
The cause of the problem is that the %s file in your garrysmod/ folder on
|
||||
%s is corrupt. How this came to be is unknown, but here's what you can do to solve it:
|
||||
|
||||
- Delete %s, and run a file integrity check. Warning: You will lose ALL data stored in it!
|
||||
- Take the file and try to repair it. This is sadly something that requires some technical knowledge,
|
||||
and may not always succeed.
|
||||
|
||||
The specific error, by the way, is as follows:
|
||||
%s
|
||||
]], dbFile, dbFile, SERVER and "the server" or "your own computer", dbFile, check))
|
||||
|
||||
end
|
||||
concommand.Add("darkrp_check_db_" .. (SERVER and "sv" or "cl"), checkDatabase)
|
||||
Reference in New Issue
Block a user