467 lines
14 KiB
Lua
467 lines
14 KiB
Lua
if not Config.EnableMetaKey then
|
|
return
|
|
end
|
|
|
|
local PlayerMetaKeyCache = {}
|
|
local META_KEY_CACHE_TIME = 5000
|
|
|
|
---@param source number
|
|
---@return table
|
|
function GetPlayerMetaKeys(source)
|
|
local player = GetPlayerFromId(source)
|
|
if not player then return {} end
|
|
|
|
local items = GetItems(player)
|
|
if not items then return {} end
|
|
|
|
local metaKeys = {}
|
|
for _, item in pairs(items) do
|
|
if item.name == Config.MetaKeyItem then
|
|
local meta = item.info or item.metadata or {}
|
|
if meta.houseId then
|
|
table.insert(metaKeys, {
|
|
houseId = meta.houseId,
|
|
houseName = meta.houseName or meta.houseId,
|
|
keyId = meta.keyId,
|
|
slot = item.slot,
|
|
givenBy = meta.givenBy,
|
|
givenAt = meta.givenAt
|
|
})
|
|
end
|
|
end
|
|
end
|
|
|
|
return metaKeys
|
|
end
|
|
|
|
---@param house string
|
|
---@return MetaKey[]
|
|
function GetHouseMetaKeys(house)
|
|
local result = db:getHouseMetaKeys(house)
|
|
|
|
if not result or #result == 0 then
|
|
return {}
|
|
end
|
|
|
|
local keys = {}
|
|
for _, key in pairs(result) do
|
|
table.insert(keys, {
|
|
id = key.id,
|
|
keyId = key.key_id,
|
|
house = key.house,
|
|
ownerIdentifier = key.owner_identifier,
|
|
createdAt = key.created_at
|
|
})
|
|
end
|
|
|
|
return keys
|
|
end
|
|
|
|
---@param source number
|
|
---@param house string
|
|
---@return boolean
|
|
function CheckHasMetaKey(source, house)
|
|
if not Config.EnableMetaKey then return false end
|
|
if not source or not house then return false end
|
|
|
|
local cacheKey = source .. ':' .. house
|
|
local cached = PlayerMetaKeyCache[cacheKey]
|
|
if cached and (GetGameTimer() - cached.time) < META_KEY_CACHE_TIME then
|
|
return cached.hasKey
|
|
end
|
|
|
|
local metaKeys = GetPlayerMetaKeys(source)
|
|
local hasKey = false
|
|
|
|
for _, key in pairs(metaKeys) do
|
|
if key.houseId == house and key.keyId then
|
|
local isValid = db:isMetaKeyValid(key.keyId, house)
|
|
if isValid then
|
|
hasKey = true
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
PlayerMetaKeyCache[cacheKey] = {
|
|
hasKey = hasKey,
|
|
time = GetGameTimer()
|
|
}
|
|
|
|
Debug('CheckHasMetaKey', 'Source:', source, 'House:', house, 'HasKey:', hasKey)
|
|
return hasKey
|
|
end
|
|
|
|
exports('CheckHasMetaKey', CheckHasMetaKey)
|
|
|
|
---@param source number
|
|
---@param house string
|
|
---@param identifier string
|
|
---@return string?
|
|
function CreateMetaKey(source, house, identifier)
|
|
local houseData = Config.Houses[house]
|
|
if not houseData then
|
|
if source then
|
|
Notification(source, i18n.t('metakey.house_not_found'), 'error')
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local keyId = house .. '_' .. os.time() .. '_' .. math.random(1000, 9999)
|
|
|
|
db:createMetaKey(house, keyId, identifier)
|
|
Debug('CreateMetaKey', 'Created key with ID:', keyId, 'for house:', house)
|
|
return keyId
|
|
end
|
|
|
|
---@param source number Player server ID
|
|
---@param house string House identifier
|
|
---@param keyId? string Optional key ID (if not provided, creates new)
|
|
---@return boolean Success
|
|
function GiveMetaKey(source, house, keyId)
|
|
local houseData = Config.Houses[house]
|
|
if not houseData then
|
|
Notification(source, i18n.t('metakey.house_not_found'), 'error')
|
|
return false
|
|
end
|
|
|
|
local identifier = GetIdentifier(source)
|
|
|
|
if not keyId then
|
|
local newKeyId = CreateMetaKey(source, house, identifier)
|
|
if not newKeyId or type(newKeyId) ~= 'string' then
|
|
Notification(source, i18n.t('metakey.create_failed'), 'error')
|
|
return false
|
|
end
|
|
keyId = newKeyId -- @type string
|
|
else
|
|
local isValid = db:isMetaKeyValid(keyId, house)
|
|
if not isValid then
|
|
Notification(source, i18n.t('metakey.invalid_key'), 'error')
|
|
return false
|
|
end
|
|
end
|
|
|
|
local meta = {
|
|
houseId = house,
|
|
houseName = houseData.address or house,
|
|
keyId = keyId,
|
|
givenBy = identifier,
|
|
givenAt = os.time()
|
|
}
|
|
|
|
local success = AddItem(source, Config.MetaKeyItem, 1, nil, meta)
|
|
if success then
|
|
Notification(source, i18n.t('metakey.received', { house = houseData.address or house }), 'success')
|
|
ClearMetaKeyCache(source)
|
|
Debug('GiveMetaKey', 'Source:', source, 'House:', house, 'KeyID:', keyId, 'Success')
|
|
|
|
SendLog(DiscordWebhook, {
|
|
title = 'Housing - Meta Key',
|
|
description = 'Player received a meta key',
|
|
fields = {
|
|
{ name = 'Player', value = GetPlayerName(source), inline = true },
|
|
{ name = 'House', value = house, inline = true },
|
|
{ name = 'Key ID', value = keyId, inline = true }
|
|
},
|
|
color = WebhookColor
|
|
})
|
|
return true
|
|
else
|
|
Notification(source, i18n.t('metakey.inventory_full'), 'error')
|
|
return false
|
|
end
|
|
end
|
|
|
|
exports('GiveMetaKey', GiveMetaKey)
|
|
|
|
---@param source number Player server ID
|
|
---@param house string House identifier
|
|
---@return boolean Success
|
|
function RemoveMetaKey(source, house)
|
|
local player = GetPlayerFromId(source)
|
|
if not player then return false end
|
|
|
|
local items = GetItems(player)
|
|
if not items then return false end
|
|
|
|
for _, item in pairs(items) do
|
|
if item.name == Config.MetaKeyItem then
|
|
local meta = item.info or item.metadata or {}
|
|
if meta.houseId == house then
|
|
RemoveItem(source, Config.MetaKeyItem, 1)
|
|
Notification(source, i18n.t('metakey.removed'), 'info')
|
|
ClearMetaKeyCache(source)
|
|
Debug('RemoveMetaKey', 'Source:', source, 'House:', house, 'Success')
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
exports('RemoveMetaKey', RemoveMetaKey)
|
|
|
|
---@param source number Player server ID
|
|
function ClearMetaKeyCache(source)
|
|
for key, _ in pairs(PlayerMetaKeyCache) do
|
|
if key:find('^' .. source .. ':') then
|
|
PlayerMetaKeyCache[key] = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
AddEventHandler('playerDropped', function()
|
|
local src = source
|
|
ClearMetaKeyCache(src)
|
|
end)
|
|
|
|
---@param house string House identifier
|
|
---@param keyId string Unique key ID
|
|
---@return boolean Success
|
|
function DeleteMetaKey(house, keyId)
|
|
db:deleteMetaKey(keyId, house)
|
|
Debug('DeleteMetaKey', 'Deleted key:', keyId, 'for house:', house)
|
|
return true
|
|
end
|
|
|
|
---@param source number
|
|
---@param house string House identifier
|
|
---@return boolean
|
|
lib.callback.register('housing:createMetaKey', function(source, house)
|
|
local identifier = GetIdentifier(source)
|
|
|
|
if not Config.EnableMetaKey then
|
|
Notification(source, i18n.t('metakey.system_disabled'), 'error')
|
|
return false
|
|
end
|
|
|
|
if HouseOwnerIdentifierList[house] ~= identifier and HouseOwnerCitizenidList[house] ~= identifier then
|
|
Notification(source, i18n.t('metakey.not_authorized'), 'error')
|
|
return false
|
|
end
|
|
|
|
local price = Config.MetaKeyCreatePrice or 500
|
|
local money = GetAccountMoney(source, Config.MoneyType)
|
|
|
|
if money < price then
|
|
Notification(source, i18n.t('no_money', { price = price }), 'error')
|
|
return false
|
|
end
|
|
|
|
RemoveAccountMoney(source, Config.MoneyType, price)
|
|
|
|
local newKeyId = CreateMetaKey(source, house, identifier)
|
|
if not newKeyId or type(newKeyId) ~= 'string' then
|
|
AddAccountMoney(source, Config.MoneyType, price)
|
|
Notification(source, i18n.t('metakey.create_failed'), 'error')
|
|
return false
|
|
end
|
|
local keyId = newKeyId -- @type string
|
|
|
|
local success = GiveMetaKey(source, house, keyId)
|
|
if success then
|
|
Notification(source, i18n.t('metakey.created', { price = price }), 'success')
|
|
|
|
SendLog(DiscordWebhook, {
|
|
title = 'Housing - Meta Key Created',
|
|
description = 'Player created a meta key',
|
|
fields = {
|
|
{ name = 'Player', value = GetPlayerName(source), inline = true },
|
|
{ name = 'House', value = house, inline = true },
|
|
{ name = 'Key ID', value = keyId, inline = true },
|
|
{ name = 'Price', value = tostring(price), inline = true }
|
|
},
|
|
color = WebhookColor
|
|
})
|
|
else
|
|
AddAccountMoney(source, Config.MoneyType, price)
|
|
Notification(source, i18n.t('metakey.create_failed'), 'error')
|
|
end
|
|
return true
|
|
end)
|
|
|
|
---@param source number
|
|
---@param house string
|
|
---@param keyId string
|
|
---@return boolean
|
|
lib.callback.register('housing:deleteMetaKey', function(source, house, keyId)
|
|
local identifier = GetIdentifier(source)
|
|
|
|
if not Config.EnableMetaKey then
|
|
Notification(source, i18n.t('metakey.system_disabled'), 'error')
|
|
return false
|
|
end
|
|
|
|
if HouseOwnerIdentifierList[house] ~= identifier and HouseOwnerCitizenidList[house] ~= identifier then
|
|
Notification(source, i18n.t('metakey.not_authorized'), 'error')
|
|
return false
|
|
end
|
|
|
|
local success = DeleteMetaKey(house, keyId)
|
|
if success then
|
|
Notification(source, i18n.t('metakey.deleted'), 'success')
|
|
|
|
SendLog(DiscordWebhook, {
|
|
title = 'Housing - Meta Key Deleted',
|
|
description = 'Player deleted a meta key',
|
|
fields = {
|
|
{ name = 'Player', value = GetPlayerName(source), inline = true },
|
|
{ name = 'House', value = house, inline = true },
|
|
{ name = 'Key ID', value = keyId, inline = true }
|
|
},
|
|
color = WebhookColor
|
|
})
|
|
else
|
|
Notification(source, i18n.t('metakey.delete_failed'), 'error')
|
|
end
|
|
return true
|
|
end)
|
|
|
|
RegisterServerCallback('housing:getHouseMetaKeys', function(source, cb, house)
|
|
local identifier = GetIdentifier(source)
|
|
|
|
if HouseOwnerIdentifierList[house] ~= identifier and HouseOwnerCitizenidList[house] ~= identifier then
|
|
cb({})
|
|
return
|
|
end
|
|
|
|
cb(GetHouseMetaKeys(house))
|
|
end)
|
|
|
|
RegisterNetEvent('housing:giveMetaKeyToPlayer', function(targetId, house)
|
|
local src = source
|
|
local identifier = GetIdentifier(src)
|
|
|
|
if HouseOwnerIdentifierList[house] ~= identifier and HouseOwnerCitizenidList[house] ~= identifier then
|
|
local isKeyholder = false
|
|
if HouseKeyholdersList[house] then
|
|
for _, cid in pairs(HouseKeyholdersList[house]) do
|
|
if cid == identifier then
|
|
isKeyholder = true
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
if not isKeyholder then
|
|
Notification(src, i18n.t('metakey.not_authorized'), 'error')
|
|
return
|
|
end
|
|
end
|
|
|
|
local targetPlayer = GetPlayerFromId(targetId)
|
|
if not targetPlayer then
|
|
Notification(src, i18n.t('metakey.player_offline'), 'error')
|
|
return
|
|
end
|
|
|
|
local success = GiveMetaKey(targetId, house)
|
|
if success then
|
|
local firstName, lastName = GetCharacterName(targetId)
|
|
Notification(src, i18n.t('metakey.given', { player = firstName .. ' ' .. lastName }), 'success')
|
|
|
|
SendLog(DiscordWebhook, {
|
|
title = 'Housing - Meta Key Transfer',
|
|
description = 'Player gave a meta key to another player',
|
|
fields = {
|
|
{ name = 'From', value = GetPlayerName(src), inline = true },
|
|
{ name = 'To', value = GetPlayerName(targetId), inline = true },
|
|
{ name = 'House', value = house, inline = true }
|
|
},
|
|
color = WebhookColor
|
|
})
|
|
end
|
|
end)
|
|
|
|
---@param source number Player server ID
|
|
---@param house string House identifier
|
|
---@return boolean Whether player has a meta key for this house
|
|
function HasMetaKeyInInventory(source, house)
|
|
local player = GetPlayerFromId(source)
|
|
if not player then return false end
|
|
|
|
local items = GetItems(player)
|
|
if not items then return false end
|
|
|
|
for _, item in pairs(items) do
|
|
if item.name == Config.MetaKeyItem then
|
|
local meta = item.info or item.metadata or {}
|
|
if meta.houseId == house then
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
RegisterUsableItem(Config.MetaKeyItem, function(source, item)
|
|
local src = source
|
|
local meta = item.info and item.info or item.metadata
|
|
|
|
if not meta or not meta.houseId then
|
|
Notification(src, i18n.t('metakey.invalid_key'), 'error')
|
|
return
|
|
end
|
|
|
|
UseMetaKey(src, item)
|
|
end)
|
|
|
|
---@param source number
|
|
---@param item table
|
|
function UseMetaKey(source, item)
|
|
local src = source
|
|
local meta = item.info or item.metadata or {}
|
|
|
|
if not meta.houseId or not meta.keyId then
|
|
Notification(src, i18n.t('metakey.invalid_key'), 'error')
|
|
return
|
|
end
|
|
|
|
local house = meta.houseId
|
|
local houseData = Config.Houses[house]
|
|
|
|
if not houseData then
|
|
Notification(src, i18n.t('metakey.house_not_exists'), 'error')
|
|
return
|
|
end
|
|
|
|
local isValid = db:isMetaKeyValid(meta.keyId, house)
|
|
|
|
if not isValid then
|
|
Notification(src, i18n.t('metakey.invalid_key'), 'error')
|
|
return
|
|
end
|
|
|
|
local playerPed = GetPlayerPed(src)
|
|
if playerPed and playerPed > 0 then
|
|
local playerCoords = GetEntityCoords(playerPed)
|
|
local enterCoords = houseData.coords.enter
|
|
|
|
if enterCoords then
|
|
local distance = #(playerCoords - vector3(enterCoords.x, enterCoords.y, enterCoords.z))
|
|
|
|
if distance <= 2.0 then
|
|
TriggerClientEvent('qb-houses:client:EnterHouse', src, houseData.ipl, house, {
|
|
isOwnedByMe = false,
|
|
haskey = true,
|
|
isOfficialOwner = false
|
|
})
|
|
Notification(src, i18n.t('metakey.entered_house', { house = meta.houseName or houseData.address or house }), 'success')
|
|
return
|
|
end
|
|
end
|
|
end
|
|
|
|
Notification(src, i18n.t('metakey.key_info', { house = meta.houseName or house }), 'info')
|
|
|
|
TriggerClientEvent('housing:showMetaKeyInfo', src, {
|
|
houseId = house,
|
|
houseName = meta.houseName or houseData.address or house,
|
|
address = houseData.address,
|
|
keyId = meta.keyId,
|
|
givenAt = meta.givenAt
|
|
})
|
|
end
|