mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-17 21:53:46 +03:00
Upload
This commit is contained in:
530
gamemodes/ixhl2rp/plugins/combineutilities/pda/sv_datafile.lua
Normal file
530
gamemodes/ixhl2rp/plugins/combineutilities/pda/sv_datafile.lua
Normal file
@@ -0,0 +1,530 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
local datafields = {"genericdata", "datafilelogs", "datafileviolations", "datafilemedicalrecords"}
|
||||
|
||||
function PLUGIN:SendFile(client, file)
|
||||
timer.Simple(0.05, function()
|
||||
netstream.Start(client, "OpenDatafileCl", file)
|
||||
end)
|
||||
end
|
||||
|
||||
-- Include new required data to datafiles of old characters
|
||||
function PLUGIN:UpdateOldVortData(character, genericdata)
|
||||
if (!character or !istable(genericdata)) then return end
|
||||
|
||||
genericdata.collarID = character:GetCollarID() or "N/A"
|
||||
genericdata.cohesionPoints = genericdata.socialCredits or 0
|
||||
genericdata.cohesionPointsDate = ix.config.Get("day").."/"..ix.config.Get("month").."/"..ix.config.Get("year")
|
||||
genericdata.nulled = "INACTIVE"
|
||||
genericdata.cid = "N/A"
|
||||
genericdata.name = nil
|
||||
|
||||
if (character:HasFlags("N") or character:GetBackground() == "Collaborator") then
|
||||
genericdata.nulled = "ACTIVE"
|
||||
end
|
||||
|
||||
for _, v in pairs(character:GetInventory():GetItems()) do
|
||||
if (table.HasValue({"Vortigaunt Collar", "Vortigaunt Collar (fake)"}, v.name) and v:GetData("equip") == true) then
|
||||
if (v:GetData("collarID", nil) and v:GetData("sterilizedCredits", nil)) then
|
||||
genericdata.cohesionPoints = v:GetData("sterilizedCredits", 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
character:SetGenericdata(genericdata)
|
||||
character:Save()
|
||||
|
||||
character:GetPlayer():Notify("New format successfully applied to your datafile.")
|
||||
end
|
||||
|
||||
-- Search the collarID through the database
|
||||
function PLUGIN:LookUpCollarID(client, id, bNotify)
|
||||
if (!client or !id) then return end
|
||||
|
||||
id = string.sub(id, 2)
|
||||
|
||||
local query = mysql:Select("ix_characters")
|
||||
query:Select("name")
|
||||
query:Select("faction")
|
||||
query:Select("id")
|
||||
query:Select("collarID")
|
||||
query:Select("background")
|
||||
query:Where("collarID", id)
|
||||
query:Where("schema", Schema and Schema.folder or "helix")
|
||||
query:Callback(function(result)
|
||||
if (!istable(result) or #result == 0) then
|
||||
return
|
||||
end
|
||||
|
||||
local resultFactionString = result[1].faction
|
||||
|
||||
if (resultFactionString != "vortigaunt") then
|
||||
return
|
||||
end
|
||||
|
||||
local dataSelect = mysql:Select("ix_characters_data")
|
||||
dataSelect:Where("id", result[1].id)
|
||||
dataSelect:WhereIn("key", datafields)
|
||||
dataSelect:Callback(function(dataSelectResult)
|
||||
if (!istable(dataSelectResult) or #dataSelectResult == 0) then
|
||||
return
|
||||
end
|
||||
|
||||
local file = {}
|
||||
for _, v in ipairs(dataSelectResult) do
|
||||
file[v.key] = util.JSONToTable(v.data or "")
|
||||
file["charID"] = result[1].id
|
||||
|
||||
if (v.key == "genericdata") then
|
||||
if file[v.key].name != result[1].name then
|
||||
file[v.key].name = result[1].name
|
||||
|
||||
local updateQuery = mysql:Update("ix_characters_data")
|
||||
updateQuery:Update("data", util.TableToJSON(file[v.key]) or "")
|
||||
updateQuery:Where("id", result[1].id)
|
||||
updateQuery:Where("key", "genericdata")
|
||||
updateQuery:Execute()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (bNotify) then
|
||||
local author = client:IsDispatch() and "OVERWATCH" or (ix.faction.Get(client:Team()).idInspectionText or "Unit") .. " " .. string.upper(client:GetCombineTag())
|
||||
|
||||
ix.combineNotify:AddNotification("LOG:// " .. author .. " performing identity inspection on " .. string.upper("biotic asset:") .. " #" .. result[1].collarID)
|
||||
end
|
||||
|
||||
self:SendFile(client, file)
|
||||
end)
|
||||
dataSelect:Execute()
|
||||
|
||||
end)
|
||||
query:Execute()
|
||||
end
|
||||
|
||||
function PLUGIN:RefreshDatafile(client, id, bIsLocal, bCachedID, bNotify)
|
||||
if !client then return end
|
||||
|
||||
if (client:Team() == FACTION_SERVERADMIN) then
|
||||
bNotify = false
|
||||
end
|
||||
|
||||
--If the player is trying to view his/her own datafile
|
||||
if (bIsLocal) then
|
||||
local character = client:GetCharacter()
|
||||
local file = {}
|
||||
|
||||
file["genericdata"] = character:GetGenericdata()
|
||||
file["datafileviolations"] = character:GetDatafileviolations()
|
||||
file["datafilemedicalrecords"] = character:GetDatafilemedicalrecords()
|
||||
file["datafilelogs"] = character:GetDatafilelogs()
|
||||
file["charID"] = character:GetID()
|
||||
|
||||
if character:IsVortigaunt() and !file["genericdata"].cohesionPoints then
|
||||
self:UpdateOldVortData(character, file["genericdata"])
|
||||
end
|
||||
|
||||
self:SendFile(client, file)
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
local target = false
|
||||
|
||||
if (bCachedID) then
|
||||
if isnumber(tonumber(id)) then
|
||||
if ix.char.loaded[id] then
|
||||
target = ix.char.loaded[id]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if !target then
|
||||
-- Check for cached characters
|
||||
if (tonumber(id) != nil and string.utf8len(tostring(id)) <= 5) then
|
||||
-- id to cid comparison
|
||||
for _, v in pairs(ix.char.loaded) do
|
||||
local cid = v.GetCid and v:GetCid()
|
||||
local genericdata = v:GetGenericdata()
|
||||
|
||||
if (v:IsVortigaunt() and v:GetCid() == id and genericdata.cid == "N/A") then return end
|
||||
|
||||
if (cid and id) then
|
||||
if (tostring(cid) == id) then
|
||||
target = v
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif (id != nil and string.sub(id, 1, 1) == "!") then
|
||||
id = string.sub(id, 2)
|
||||
|
||||
for _, v in pairs(ix.char.loaded) do
|
||||
if (v:IsVortigaunt() and v:GetCollarID() == id) then
|
||||
target = v
|
||||
break
|
||||
end
|
||||
end
|
||||
else
|
||||
-- id to name comparison
|
||||
for _, v in pairs(ix.char.loaded) do
|
||||
local genericdata = v:GetGenericdata()
|
||||
|
||||
if string.find(v:GetName(), id) then
|
||||
if (v:IsVortigaunt() and genericdata.cid == "N/A") then return end
|
||||
|
||||
target = v
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Make sure further code isn't ran
|
||||
if target then
|
||||
local file = {}
|
||||
file["genericdata"] = target:GetGenericdata()
|
||||
file["datafileviolations"] = target:GetDatafileviolations()
|
||||
file["datafilemedicalrecords"] = target:GetDatafilemedicalrecords()
|
||||
file["datafilelogs"] = target:GetDatafilelogs()
|
||||
file["charID"] = target:GetID()
|
||||
|
||||
if (target:IsVortigaunt() and !file["genericdata"].cohesionPoints) then
|
||||
self:UpdateOldVortData(target, file["genericdata"])
|
||||
end
|
||||
|
||||
if (bNotify) then
|
||||
local author = client:IsDispatch() and "OVERWATCH" or (ix.faction.Get(client:Team()).idInspectionText or "Unit") .. " " .. string.upper(client:GetCombineTag())
|
||||
|
||||
if (target:IsVortigaunt()) then
|
||||
if (file["genericdata"].cid == "N/A" and file["genericdata"].collarID != "N/A") then
|
||||
ix.combineNotify:AddNotification("LOG:// " .. author .. " performing identity inspection on " .. string.upper("biotic asset collar:") .. " #" .. target:GetCollarID())
|
||||
elseif (file["genericdata"].cid == "N/A" and file["genericdata"].collarID == "N/A") then
|
||||
ix.combineNotify:AddNotification("LOG:// " .. author .. " performing identity inspection on " .. string.upper("biotic asset collar:") .. " #" .. target:GetFakeCollarID())
|
||||
else
|
||||
ix.combineNotify:AddNotification("LOG:// " .. author .. " performing identity inspection on " .. string.upper("biotic asset identification card:") .. " #" .. file["genericdata"].cid)
|
||||
end
|
||||
else
|
||||
ix.combineNotify:AddNotification("LOG:// " .. author .. " performing identity inspection on '" .. string.upper(target:GetName()) .. "', #" .. target:GetCid())
|
||||
end
|
||||
end
|
||||
|
||||
self:SendFile(client, file)
|
||||
return
|
||||
end
|
||||
|
||||
-- If no cached character, search in datafile
|
||||
if (tonumber(id) != nil and string.utf8len( id ) <= 5) then
|
||||
local query = mysql:Select("ix_characters")
|
||||
query:Select("name")
|
||||
query:Select("faction")
|
||||
query:Select("id")
|
||||
query:Select("cid")
|
||||
query:Select("background")
|
||||
query:Where("cid", id)
|
||||
query:Where("schema", Schema and Schema.folder or "helix")
|
||||
query:Callback(function(result)
|
||||
if (!istable(result) or #result == 0) then
|
||||
return
|
||||
end
|
||||
|
||||
local resultFactionString = result[1].faction
|
||||
if (resultFactionString == "overwatch" and client:Team() != FACTION_OVERWATCH) then
|
||||
client:NotifyLocalized("You do not have access to this datafile!")
|
||||
return false
|
||||
end
|
||||
if (resultFactionString == "ota" or resultFactionString == "administrator" or resultFactionString == "guard") then
|
||||
local character = client:GetCharacter()
|
||||
if (client:Team() != FACTION_OVERWATCH and client:Team() != FACTION_OTA and character:GetClass() != FACTION_MCP and client:Team() != CLASS_CP_CMD and character:GetClass() != CLASS_CP_CPT and character:GetClass() != CLASS_CP_RL) then
|
||||
if client:Team() != FACTION_ADMIN then
|
||||
client:NotifyLocalized("You do not have access to this datafile!")
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local dataSelect = mysql:Select("ix_characters_data")
|
||||
dataSelect:Where("id", result[1].id)
|
||||
dataSelect:WhereIn("key", datafields)
|
||||
dataSelect:Callback(function(dataSelectResult)
|
||||
if (!istable(dataSelectResult) or #dataSelectResult == 0) then
|
||||
return
|
||||
end
|
||||
|
||||
local file = {}
|
||||
for _, v in ipairs(dataSelectResult) do
|
||||
file[v.key] = util.JSONToTable(v.data or "")
|
||||
file["charID"] = result[1].id
|
||||
|
||||
if (v.key == "genericdata") then
|
||||
if (resultFactionString == "vortigaunt" and util.JSONToTable(v.data).cid == "N/A") then return end
|
||||
|
||||
if file[v.key].name != result[1].name then
|
||||
file[v.key].name = result[1].name
|
||||
|
||||
local updateQuery = mysql:Update("ix_characters_data")
|
||||
updateQuery:Update("data", util.TableToJSON(file[v.key]) or "")
|
||||
updateQuery:Where("id", result[1].id)
|
||||
updateQuery:Where("key", "genericdata")
|
||||
updateQuery:Execute()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (bNotify) then
|
||||
local author = client:IsDispatch() and "OVERWATCH" or (ix.faction.Get(client:Team()).idInspectionText or "Unit") .. " " .. string.upper(client:GetCombineTag())
|
||||
|
||||
ix.combineNotify:AddNotification("LOG:// " .. author .. " performing identity inspection on '" .. string.upper(result[1].name) .. "', #" .. result[1].cid)
|
||||
end
|
||||
|
||||
self:SendFile(client, file)
|
||||
end)
|
||||
dataSelect:Execute()
|
||||
|
||||
end)
|
||||
query:Execute()
|
||||
else
|
||||
if (string.sub(id, 1, 1) == "!") then
|
||||
self:LookUpCollarID(client, id, bNotify)
|
||||
end
|
||||
|
||||
local query = mysql:Select("ix_characters")
|
||||
query:Select("id")
|
||||
query:Select("name")
|
||||
query:Select("faction")
|
||||
query:Select("background")
|
||||
query:Select("cid")
|
||||
query:WhereLike("name", id)
|
||||
query:Where("schema", Schema and Schema.folder or "helix")
|
||||
query:Callback(function(result)
|
||||
if (!istable(result) or #result == 0) then
|
||||
return
|
||||
end
|
||||
|
||||
local resultFactionString = result[1].faction
|
||||
if (resultFactionString == "overwatch" and client:Team() != FACTION_OVERWATCH) then
|
||||
client:NotifyLocalized("You do not have access to this datafile!")
|
||||
return false
|
||||
end
|
||||
if (resultFactionString == "ota" or resultFactionString == "administrator" or resultFactionString == "guard") then
|
||||
local character = client:GetCharacter()
|
||||
if (client:Team() != FACTION_OVERWATCH and client:Team() != FACTION_OTA and character:GetClass() != FACTION_MCP and client:Team() != CLASS_CP_CMD and character:GetClass() != CLASS_CP_CPT and character:GetClass() != CLASS_CP_RL) then
|
||||
if client:Team() != FACTION_ADMIN then
|
||||
client:NotifyLocalized("You do not have access to this datafile!")
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local dataSelect = mysql:Select("ix_characters_data")
|
||||
dataSelect:Where("id", result[1].id)
|
||||
dataSelect:WhereIn("key", datafields)
|
||||
dataSelect:Callback(function(dataSelectResult)
|
||||
if (!istable(dataSelectResult) or #dataSelectResult == 0) then
|
||||
return
|
||||
end
|
||||
|
||||
local file = {}
|
||||
for _, v in ipairs(dataSelectResult) do
|
||||
file[v.key] = util.JSONToTable(v.data or "")
|
||||
file["charID"] = result[1].id
|
||||
|
||||
if (v.key == "genericdata") then
|
||||
if (resultFactionString == "vortigaunt" and util.JSONToTable(v.data).cid == "N/A") then return end
|
||||
|
||||
if file[v.key].name != result[1].name then
|
||||
file[v.key].name = result[1].name
|
||||
|
||||
local updateQuery = mysql:Update("ix_characters_data")
|
||||
updateQuery:Update("data", util.TableToJSON(file[v.key]) or "")
|
||||
updateQuery:Where("id", result[1].id)
|
||||
updateQuery:Where("key", "genericdata")
|
||||
updateQuery:Execute()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (bNotify) then
|
||||
local author = client:IsDispatch() and "OVERWATCH" or (ix.faction.Get(client:Team()).idInspectionText or "Unit") .. " " .. string.upper(client:GetCombineTag())
|
||||
|
||||
ix.combineNotify:AddNotification("LOG:// " .. author .. " performing identity inspection on '" .. string.upper(result[1].name) .. "', #" .. result[1].cid)
|
||||
end
|
||||
self:SendFile(client, file)
|
||||
end)
|
||||
dataSelect:Execute()
|
||||
|
||||
end)
|
||||
query:Execute()
|
||||
end
|
||||
end
|
||||
|
||||
netstream.Hook("RequestCIDCreditsDatafile", function(client, cid)
|
||||
if (!PLUGIN:HasAccessToDatafile(client)) then return end
|
||||
|
||||
cid = Schema:ZeroNumber(cid, 5)
|
||||
local housing = ix.plugin.list["housing"]
|
||||
if !housing then return end
|
||||
|
||||
housing:LookUpCardItemIDByCID(tostring(cid), function(result)
|
||||
local idCardID = result[1].idcard or false
|
||||
if !result then return end
|
||||
|
||||
if !ix.item.instances[idCardID] then
|
||||
ix.item.LoadItemByID(idCardID, false, function(item)
|
||||
if !item then return end
|
||||
local credits = item:GetData("credits", 0)
|
||||
netstream.Start(client, "UpdateDatafileCredits", credits)
|
||||
end)
|
||||
else
|
||||
local credits = ix.item.instances[idCardID]:GetData("credits", 0)
|
||||
netstream.Start(client, "UpdateDatafileCredits", credits)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
netstream.Hook("ixDatafileRequestTransactionLogs", function(client, cid)
|
||||
if (!PLUGIN:HasAccessToDatafile(client)) then return end
|
||||
cid = Schema:ZeroNumber(cid, 5)
|
||||
|
||||
ix.plugin.list.cid:SelectTransactions(client, "cid", cid, ix.config.Get("TransactionLogDaysDatafile", 14), nil, function(result)
|
||||
netstream.Start(client, "ixDatafileReplyTransactionLogs", result)
|
||||
end)
|
||||
end)
|
||||
|
||||
netstream.Hook("TerminalReportCrime", function(client, name, cid, text, activeTerminal)
|
||||
if !text or !name or !cid then return false end
|
||||
if string.len(text) <= 0 then client:Notify("You cannot send an empty crime report.") return end
|
||||
if cid != activeTerminal.activeCID then
|
||||
return false
|
||||
end
|
||||
|
||||
local timestamp = ix.date.GetFormatted("%d.%m.%Y")
|
||||
local queryObj = mysql:Insert("ix_crimereports")
|
||||
queryObj:Insert("message_poster", name)
|
||||
queryObj:Insert("message_text", text)
|
||||
queryObj:Insert("message_date", timestamp)
|
||||
queryObj:Insert("message_cid", cid)
|
||||
queryObj:Execute()
|
||||
|
||||
ix.combineNotify:AddImportantNotification("WRN:// Crime report submitted by " .. name .. " #" .. cid, Color(0, 150, 255), client, activeTerminal:GetPos())
|
||||
end)
|
||||
|
||||
function PLUGIN:GetCrimeReports(client, bArchived, bResolved, curCollect)
|
||||
|
||||
local query = mysql:Select("ix_crimereports")
|
||||
query:Select("message_id")
|
||||
query:Select("message_text")
|
||||
query:Select("message_date")
|
||||
query:Select("message_poster")
|
||||
query:Select("message_cid")
|
||||
query:Callback(function(result)
|
||||
if (!istable(result) or #result == 0) then
|
||||
return
|
||||
end
|
||||
|
||||
local toSort = {}
|
||||
|
||||
for key, _ in pairs(result) do
|
||||
local posterIsTable = string.find(result[key].message_poster, "{")
|
||||
if posterIsTable then result[key].message_poster = util.JSONToTable(result[key].message_poster) end
|
||||
|
||||
if posterIsTable or (bArchived or bResolved) then
|
||||
if bArchived and result[key].message_poster.archived then
|
||||
toSort[#toSort + 1] = result[key]
|
||||
|
||||
continue
|
||||
end
|
||||
|
||||
if bResolved and result[key].message_poster.resolved then
|
||||
toSort[#toSort + 1] = result[key]
|
||||
|
||||
continue
|
||||
end
|
||||
|
||||
continue
|
||||
end
|
||||
|
||||
toSort[#toSort + 1] = result[key]
|
||||
end
|
||||
|
||||
local toSend = {}
|
||||
for i = curCollect, curCollect + 5 do
|
||||
if toSort[i] then
|
||||
toSend[#toSend + 1] = toSort[i]
|
||||
end
|
||||
end
|
||||
|
||||
netstream.Start(client, "ReplyCrimeReports", toSend)
|
||||
end)
|
||||
|
||||
query:Execute()
|
||||
end
|
||||
|
||||
netstream.Hook("GetCrimeReports", function(client, bArchived, bResolved, curCollect)
|
||||
if (!PLUGIN:HasAccessToDatafile(client)) then return end
|
||||
|
||||
PLUGIN:GetCrimeReports(client, bArchived, bResolved, curCollect)
|
||||
end)
|
||||
|
||||
netstream.Hook("DeleteCrimeReport", function(client, key, bDatapad, lastUsedTab, curCollect)
|
||||
if (!PLUGIN:HasAccessToDatafile(client)) then return end
|
||||
|
||||
local queryObj = mysql:Delete("ix_crimereports")
|
||||
queryObj:Where("message_id", key)
|
||||
queryObj:Execute()
|
||||
|
||||
PLUGIN:GetCrimeReports(client, (lastUsedTab == "archived" and true or false), (lastUsedTab == "resolved" and true or false), curCollect)
|
||||
end)
|
||||
|
||||
netstream.Hook("ResolveCrimeReport", function(client, key, poster, bUnResolve, lastUsedTab, curCollect)
|
||||
if (!PLUGIN:HasAccessToDatafile(client)) then return end
|
||||
|
||||
local author = client:IsDispatch() and "Overwatch" or client:GetCombineTag()
|
||||
ix.combineNotify:AddNotification("NTC:// Crime Report ID #" .. key .. " marked as " .. (bUnResolve and "unresolved" or "resolved") .. " by " .. string.upper(author), Color(171, 222, 47))
|
||||
|
||||
local queryObj = mysql:Update("ix_crimereports")
|
||||
queryObj:Where("message_id", key)
|
||||
queryObj:Update("message_poster", util.TableToJSON({poster = (istable(poster) and poster.poster or poster), resolved = (!bUnResolve and true or false), archived = (istable(poster) and poster.archived or false)}))
|
||||
queryObj:Execute()
|
||||
|
||||
PLUGIN:GetCrimeReports(client, (lastUsedTab == "archived" and true or false), (lastUsedTab == "resolved" and true or false), curCollect)
|
||||
end)
|
||||
|
||||
netstream.Hook("ArchiveCrimeReport", function(client, key, poster, bUnArchive, lastUsedTab, curCollect)
|
||||
if (!PLUGIN:HasAccessToDatafile(client)) then return end
|
||||
|
||||
local author = client:IsDispatch() and "Overwatch" or client:GetCombineTag()
|
||||
ix.combineNotify:AddNotification("NTC:// Crime Report ID #" .. key .. (bUnArchive and " unarchived" or " archived") .. " by " .. string.upper(author), Color(171, 222, 47))
|
||||
|
||||
local queryObj = mysql:Update("ix_crimereports")
|
||||
queryObj:Where("message_id", key)
|
||||
queryObj:Update("message_poster", util.TableToJSON({poster = (istable(poster) and poster.poster or poster), resolved = (istable(poster) and poster.resolved or false), archived = (!bUnArchive and true or false)}))
|
||||
queryObj:Execute()
|
||||
|
||||
PLUGIN:GetCrimeReports(client, (lastUsedTab == "archived" and true or false), (lastUsedTab == "resolved" and true or false), curCollect)
|
||||
end)
|
||||
|
||||
netstream.Hook("ResetDatafileToDefault", function(client, charID)
|
||||
if !CAMI.PlayerHasAccess(client, "Helix - Basic Admin Commands") then return end
|
||||
local character = ix.char.loaded[charID]
|
||||
if !character then
|
||||
client:NotifyLocalized("The owner of the character needs to be online for the reset to work.")
|
||||
return
|
||||
end
|
||||
|
||||
local player = character:GetPlayer()
|
||||
if !player then return end
|
||||
|
||||
PLUGIN:CreateDatafile(player)
|
||||
end)
|
||||
Reference in New Issue
Block a user