Aleeeee 发表于 2025-7-3 20:33:16

修复

本帖最后由 Aleeeee 于 2025-7-3 20:36 编辑

SCRIPT ERROR: citizen:/scripting/lua/scheduler.lua:479: attempt to concatenate a nil value (1ocal 'err')
SCRIPT ERROR: error object is not a string
fivem插件这么报错 请问该怎么修修复哪个文件呢

ChatGPT 发表于 2025-7-3 20:33:43

这个错误通常是由于在代码中尝试将一个为 `nil` 的值与其他字符串连接时引发的。具体来说,你的错误信息中提到的是 `attempt to concatenate a nil value (local 'err')`,这意味着变量 `err` 是 `nil`,而你试图将它与其他字符串进行拼接。

要修复这个问题,你可以按照以下步骤进行:

1. **查找报错的代码**:查看你的代码中涉及拼接 `err` 变量的地方。通常在处理错误或返回值时可能会出现这种情况。

2. **确保变量不为 nil**:在进行字符串拼接之前,检查 `err` 是否为 `nil`,如果是,考虑使用默认值或处理逻辑,比如:
   
local err = err or "未知错误"-- 如果 err 为 nil,则赋值为"未知错误"
   print("错误信息: " .. err)


3. **调试信息**:在进行拼接前,可以打印 `err` 的值来调试,例如:
   
print("err的值: ", err)-- 查看 err 的实际内容


4. **修复文件**:根据错误提示,你需要查看 `scheduler.lua` 文件,但这个文件是 FiveM 内部的,也许不是你自己编写的脚本。因此,你应检查你自己的脚本文件,如 `server.lua` 或 `client.lua`,确保在调用函数或进行错误处理时没有传递 `nil` 值。

如果你能提供更多具体的代码片段,我可以帮助你更精准地定位和修复问题。希望这能帮助到你!

Aleeeee 发表于 2025-7-3 20:35:01

ZeroDream = exports.zerodream_core:GetSharedObject()

_g= {

}

function DebugPrint(...)
    if Config.debug then
      print(...)
    end
end

function IsInTable(table, value)
    for _, v in pairs(table) do
      if v == value then
            return true
      end
    end
    return false
end

function IsPlateNumber(type, text)
    if string.match(text, "^+$") ~= nil then
      if type == 'green' or type == 'green_big' then
            return string.len(text) <= 7
      else
            return string.len(text) <= 6
      end
    end
    return false
end

function IsPlateExists(prefix, number, identifier)
    DebugPrint('Check Plate', prefix, number, identifier)
    local result = MySQL.Sync.fetchAll('SELECT * FROM `custom_plates` WHERE `prefix` = @prefix AND `number` = @number', {
      ['@prefix'] = prefix,
      ['@number'] = number,
    })
    if type(result) == 'table' and result ~= nil then
      DebugPrint('Find Plate')
      if ZeroDream.Vehicles.IsExists(result.plate) then
            local owner = ZeroDream.Vehicles.GetOwner(result.plate)
            DebugPrint('Find Car', owner, identifier)
            if Config.allowMultiCar then
                if owner == identifier then
                  return false
                else
                  return true
                end
            else
                return true
            end
      else
            return false
      end
    else
      return false
    end
end

ZeroDream.Callbacks.Unregister('zerodream_vehplatefree:savePlate')
ZeroDream.Callbacks.Unregister('zerodream_vehplatefree:removePlate')
ZeroDream.Callbacks.Unregister('zerodream_vehplatefree:checkPerm')

ZeroDream.Callbacks.Register('zerodream_vehplatefree:savePlate', function(source, cb, gtaPlate, prefix, plate, plateType, enableFront, enableRear, front, rear, frontBone, rearBone)
    local xPlayer = ZeroDream.Player.GetData(source)
    if not IsPlateNumber(plateType, plate) and xPlayer.job.name ~= 'admin' then
      DebugPrint(plate, string.len(plate), IsPlateNumber(plateType, plate))
      cb({success = false, message = '无效的车牌号,必须为数字或字母组成,新能源最多 7 位,其他最多 6 位'})
      return
    end
    if (not prefix or type(prefix) ~= 'string' or not IsInTable(Config.allowPrefixList, prefix)) and xPlayer.job.name ~= 'admin' then
      cb({success = false, message = '无效的车牌地区,请选择正确的地区'})
      return
    end
    if (not plateType or type(plateType) ~= 'string' or not IsInTable(Config.allowPlateTypes, plateType)) and xPlayer.job.name ~= 'admin' then
      DebugPrint(plateType, type(plateType), IsInTable(Config.allowPlateTypes, plateType))
      cb({success = false, message = '无效的车牌类型,请选择正确的类型'})
      return
    end
    if IsPlateExists(prefix, plate, xPlayer.identifier) and xPlayer.job.name ~= 'admin' then
      cb({success = false, message = '该车牌号已被使用,请更换其他号码'})
      return
    end
    if ZeroDream.Vehicles.IsOwner(source, gtaPlate) or xPlayer.job.name == 'admin' then
      local money = ZeroDream.Player.GetMoney(source)
      if money < Config.platePrice then
            cb({success = false, message = '你的余额不足,无法修改车牌号'})
            return
      end
      ZeroDream.Player.RemoveMoney(source, Config.platePrice, string.format('zerodream_vehplatefree:savePlate:%s', gtaPlate))
      local data = MySQL.Sync.fetchAll('SELECT * FROM `custom_plates` WHERE `plate` = @plate', {
            ['@plate'] = gtaPlate
      })
      if type(data) == 'table' and data ~= nil then
            MySQL.Sync.execute('UPDATE `custom_plates` SET `prefix` = @prefix, `number` = @number, `type` = @type, `front` = @front, `rear` = @rear, `front_bone` = @front_bone, `rear_bone` = @rear_bone WHERE `plate` = @plate', {
                ['@plate']      = gtaPlate,
                ['@prefix']   = prefix,
                ['@number']   = plate,
                ['@type']       = plateType,
                ['@front']      = enableFront and json.encode(front) or nil,
                ['@rear']       = enableRear and json.encode(rear) or nil,
                ['@front_bone'] = frontBone or 'chassis',
                ['@rear_bone']= rearBone or 'chassis'
            })
      else
            MySQL.Sync.execute('INSERT INTO `custom_plates` (`plate`, `prefix`, `number`, `type`, `front`, `rear`, `front_bone`, `rear_bone`) VALUES (@plate, @prefix, @number, @type, @front, @rear, @front_bone, @rear_bone)', {
                ['@plate']      = gtaPlate,
                ['@prefix']   = prefix,
                ['@number']   = plate,
                ['@type']       = plateType,
                ['@front']      = enableFront and json.encode(front) or nil,
                ['@rear']       = enableRear and json.encode(rear) or nil,
                ['@front_bone'] = frontBone or 'chassis',
                ['@rear_bone']= rearBone or 'chassis'
            })
      end
      TriggerLatentClientEvent('zerodream_vehplatefree:syncPlate', -1, 10000, gtaPlate, {
            prefix    = prefix,
            number    = plate,
            type      = plateType,
            front   = enableFront and front or nil,
            rear      = enableRear and rear or nil,
            frontBone = frontBone or 'chassis',
            rearBone= rearBone or 'chassis'
      })
      cb({success = true})
    else
      cb({success = true, message = '你没有这辆车的所有权,无法修改车牌号'})
      return
    end
end)

ZeroDream.Callbacks.Register('zerodream_vehplatefree:removePlate', function(source, cb, gtaPlate)
    local xPlayer = ZeroDream.Player.GetData(source)
    if ZeroDream.Vehicles.IsOwner(source, gtaPlate) or xPlayer.job.name == 'admin' then
      MySQL.Sync.execute('DELETE FROM `custom_plates` WHERE `plate` = @plate', {
            ['@plate'] = gtaPlate
      })
      TriggerLatentClientEvent('zerodream_vehplatefree:syncPlate', -1, 10000, gtaPlate, nil)
      cb({success = true})
    else
      cb({success = false, message = '你没有这辆车的所有权,无法删除车牌号'})
      return
    end
end)

ZeroDream.Callbacks.Register('zerodream_vehplatefree:checkPerm', function(source, cb, gtaPlate)
    local xPlayer = ZeroDream.Player.GetData(source)
    if ZeroDream.Vehicles.IsOwner(source, gtaPlate) or xPlayer.job.name == 'admin' then
      for k, v in pairs(Config.requirePerm) do
            if v == xPlayer.identifier or v == string.format('job:%s', xPlayer.job.name) then
                cb({success = true})
                return
            end
      end
      cb({success = false, message = '你没有权限修改车牌号'})
    else
      cb({success = true, message = '你没有这辆车的所有权,无法修改车牌号'})
      return
    end
end)

RegisterCommand('updateplate', function(source, args, raw)
    local plate = args
    if not plate or plate == '' then
      local vehicle = GetVehiclePedIsIn(GetPlayerPed(source), false)
      if vehicle and DoesEntityExist(vehicle) then
            plate = GetVehicleNumberPlateText(vehicle)
      else
            print('Usage: updateplate <plate>')
            return
      end
    end
    local result = MySQL.Sync.fetchAll('SELECT * FROM custom_plates WHERE plate = @plate', {
      ['@plate'] = plate
    })
    if type(result) == 'table' and result ~= nil then
      local data = result
      TriggerLatentClientEvent('zerodream_vehplatefree:syncPlate', -1, 10000, plate, {
            plate   = data.plate,
            prefix    = data.prefix,
            number    = data.number,
            type      = data.type,
            front   = data.front and json.decode(data.front) or nil,
            rear      = data.rear and json.decode(data.rear) or nil,
            frontBone = data.front_bone,
            rearBone= data.rear_bone
      })
    else
      TriggerLatentClientEvent('zerodream_vehplatefree:syncPlate', -1, 10000, plate, nil)
    end
end, true)

RegisterServerEvent('zerodream_vehplatefree:syncPlates')
AddEventHandler('zerodream_vehplatefree:syncPlates', function()
    local _source = source
    local result= MySQL.Sync.fetchAll('SELECT * FROM `custom_plates`')
    local list    = {}
    if type(result) == 'table' and result ~= nil then
      for _, data in ipairs(result) do
            data.front       = data.front and json.decode(data.front) or nil
            data.rear      = data.rear and json.decode(data.rear) or nil
            data.frontBone   = data.front_bone or 'chassis'
            data.rearBone    = data.rear_bone or 'chassis'
            list = data
      end
    end
    TriggerLatentClientEvent('zerodream_vehplatefree:syncPlates', _source, 100000, list, GetConvar('sv_licenseKeyToken'))
end)

Aleeeee 发表于 2025-7-3 20:35:15

ZeroDream = exports.zerodream_core:GetSharedObject()

_g= {
    plateSlots    = {},
    loadedVehicle = {},
    vehicleList   = {},
}

function DebugPrint(...)
    if Config.debug then
      print(...)
    end
end

function InitPlate()
    _g.plateTxd = CreateRuntimeTxd('zerodream_replace_plate')
    TriggerServerEvent('zerodream_vehplatefree:syncPlates')
    if IsModelInCdimage("zerodream_plate_1") then
      return
    end
    for i = 0, Config.plateSlots do
      local plateName = string.format("zerodream_plate_%s", i)
      RegisterArchetypes(function()
            return {
                {
                  flags = 32,
                  bbMin = vector3(-0.21957600, -0.00512218, -0.01286770),
                  bbMax = vector3(0.21957600, 0.01067070, 0.13729600),
                  bsCentre = vector3(0.00000000, 0.00277424, 0.06221420),
                  bsRadius = 0.23219200,
                  hdTextureDist = 75.26120000,
                  name = plateName,
                  textureDictionary = plateName,
                  assetName = plateName,
                  assetType = 'ASSET_TYPE_DRAWABLE',
                  lodDist = 100.34,
                  specialAttribute = 0
                }
            }
      end)
    end
end

function GetFreeSlot()
    for i = 1, Config.plateSlots do
      if not _g.plateSlots then
            return i
      end
    end
    return false
end

function FindPlate(prefix, number, type)
    for k, v in pairs(_g.plateSlots) do
      if v.prefix == prefix and v.number == number and v.type == type then
            return k
      end
    end
    return false
end

function GetPlateByGtaPlate(plate)
    if _g.vehicleList then
      return string.format("%s%s", _g.vehicleList.prefix, _g.vehicleList.number)
    end
    return plate
end

function RegisterPlate(prefix, number, type)
    local exists = FindPlate(prefix, number, type)
    if exists then
      return exists
    end
    local freeSlot = GetFreeSlot()
    DebugPrint('RegisterPlate: get free slot ' .. freeSlot)
    _g.plateSlots = {
      prefix   = prefix,
      number   = number,
      type   = type,
      entities = {},
    }
    local plateYdr= string.format("zerodream_plate_%s", freeSlot)
    local modelHash = GetHashKey(plateYdr)
    RequestModel(modelHash)
    DebugPrint('RegisterPlate: request model ' .. plateYdr)
    while not HasModelLoaded(modelHash) do
      Citizen.Wait(0)
    end
    DebugPrint('RegisterPlate: model loaded ' .. plateYdr)
    local plateObj = CreateObjectNoOffset(modelHash, vec3(0.0, 0.0, 0.0), false, true, false)
    local plateDui = CreateDui(Config.apiUrl:format(prefix, number, type, GetGameTimer(), _g.tokenRef), 1024, 1024)
    local handleId = GetDuiHandle(plateDui)
    local plateTxd = CreateRuntimeTextureFromDuiHandle(_g.plateTxd, string.format('zerodream_plate_%s_%s', GetHashKey(prefix .. number), type), handleId)
    AddReplaceTexture(plateYdr, 'plate_02', 'zerodream_replace_plate', string.format('zerodream_plate_%s_%s', GetHashKey(prefix .. number), type))
    SetModelAsNoLongerNeeded(modelHash)
    DeleteEntity(plateObj)
    DebugPrint('RegisterPlate: plate registered ' .. plateYdr)
    _g.plateSlots.dui = plateDui
    return freeSlot
end

function UnregisterPlate(slot)
    if not _g.plateSlots then
      return
    end
    DebugPrint('UnregisterPlate: unregister slot ' .. slot)
    for k, v in pairs(_g.plateSlots.entities) do
      for i, j in pairs(v) do
            DeleteEntity(j)
      end
    end
    RemoveReplaceTexture(string.format("zerodream_plate_%s", slot), 'plate_02')
    if IsDuiAvailable(_g.plateSlots.dui) then
      DestroyDui(_g.plateSlots.dui)
    end
    _g.plateSlots = nil
end

function LoadPlateEntity(vehicle, slot, act, pos, heading)
    local plateYdr= string.format("zerodream_plate_%s", slot)
    local modelHash = GetHashKey(plateYdr)
    RequestModel(modelHash)
    while not HasModelLoaded(modelHash) do
      Citizen.Wait(0)
    end
    local plateObj = CreateObjectNoOffset(modelHash, pos, false, true, false)
    SetEntityHeading(plateObj, heading)
    SetModelAsNoLongerNeeded(modelHash)
    _g.plateSlots.entities = _g.plateSlots.entities or {}
    _g.plateSlots.entities = plateObj
    return plateObj
end

function IsSlotHasPlate(slot, vehicle, act)
    if not _g.plateSlots then
      return false
    end
    if not _g.plateSlots.entities then
      return false
    end
    if not _g.plateSlots.entities then
      return false
    end
    return true
end

function GetSlotPlate(slot, vehicle, act)
    if not _g.plateSlots then
      return false
    end
    if not _g.plateSlots.entities then
      return false
    end
    if not _g.plateSlots.entities then
      return false
    end
    return _g.plateSlots.entities
end

function CreatePlateForVehicle(vehicle, prefix, number, type, front, rear, frontBone, rearBone)
    local slot = RegisterPlate(prefix, number, type)
    if not slot then
      DebugPrint('CreatePlateForVehicle: Failed to find available slot')
      return
    end
    DebugPrint('CreatePlateForVehicle: Created plate for vehicle ' .. vehicle .. ' with slot ' .. slot .. ' and prefix ' .. prefix .. ' and number ' .. number .. ' and type ' .. type)
    local offsetZ = -GetVehicleSuspensionHeight(vehicle)
    if front then
      local object = 0
      if not IsSlotHasPlate(slot, vehicle, 'f') then
            object = LoadPlateEntity(vehicle, slot, 'f', GetEntityCoords(vehicle), GetEntityHeading(vehicle))
      else
            object = GetSlotPlate(slot, vehicle, 'f')
      end
      DebugPrint('CreatePlateForVehicle: Loaded front plate entity ' .. object)
      local boneOffset = 0.0
      if frontBone == 'chassis' or GetEntityBoneIndexByName(vehicle, frontBone) == -1 then
            boneOffset = offsetZ
      end
      AttachEntityToEntity(object, vehicle, GetEntityBoneIndexByName(vehicle, frontBone), front.x, front.y, front.z + boneOffset, front.rx, front.ry, front.rz, false, false, false, false, 0, true)
    else
      if IsSlotHasPlate(slot, vehicle, 'f') then
            DeleteEntity(GetSlotPlate(slot, vehicle, 'f'))
      end
    end
    if rear then
      local object = 0
      if not IsSlotHasPlate(slot, vehicle, 'r') then
            object = LoadPlateEntity(vehicle, slot, 'r', GetEntityCoords(vehicle), GetEntityHeading(vehicle))
      else
            object = GetSlotPlate(slot, vehicle, 'r')
      end
      DebugPrint('CreatePlateForVehicle: Loaded rear plate entity ' .. object)
      local boneOffset = 0.0
      if rearBone == 'chassis' or GetEntityBoneIndexByName(vehicle, rearBone) == -1 then
            boneOffset = offsetZ
      end
      AttachEntityToEntity(object, vehicle, GetEntityBoneIndexByName(vehicle, rearBone), rear.x, rear.y, rear.z + boneOffset, rear.rx, rear.ry, rear.rz, false, false, false, false, 0, true)
    else
      if IsSlotHasPlate(slot, vehicle, 'r') then
            DeleteEntity(GetSlotPlate(slot, vehicle, 'r'))
      end
    end
    _g.loadedVehicle = {
      slot      = slot,
      prefix    = prefix,
      number    = number,
      type      = type,
      front   = front,
      rear      = rear,
      offsetZ   = offsetZ,
      frontBone = frontBone,
      rearBone= rearBone,
    }
end

function RemovePlateForVehicle(vehicle)
    if not _g.loadedVehicle then
      return
    end
    local slot = _g.loadedVehicle.slot
    if not _g.plateSlots then
      return
    end
    if _g.loadedVehicle.front then
      local object = GetSlotPlate(slot, vehicle, 'f')
      if object then
            DeleteEntity(object)
      end
    end
    if _g.loadedVehicle.rear then
      local object = GetSlotPlate(slot, vehicle, 'r')
      if object then
            DeleteEntity(object)
      end
    end
    _g.loadedVehicle = nil
    _g.plateSlots.entities = nil
    if not next(_g.plateSlots.entities) then
      UnregisterPlate(slot)
    end
end

function UpdatePlate(vehicle, prefix, number, type, front, rear, frontBone, rearBone)
    if not _g.loadedVehicle then
      CreatePlateForVehicle(vehicle, prefix, number, type, front, rear, frontBone, rearBone)
    end
    local slot = _g.loadedVehicle.slot
    if not _g.plateSlots then
      return
    end
    if _g.loadedVehicle.prefix ~= prefix or _g.loadedVehicle.number ~= number or _g.loadedVehicle.type ~= type then
      RemovePlateForVehicle(vehicle)
      CreatePlateForVehicle(vehicle, prefix, number, type, front, rear, frontBone, rearBone)
      return
    end
    local offsetZ = -GetVehicleSuspensionHeight(vehicle)
    if front then
      local object = 0
      if IsSlotHasPlate(slot, vehicle, 'f') then
            object = GetSlotPlate(slot, vehicle, 'f')
      else
            object = LoadPlateEntity(vehicle, slot, 'f', GetEntityCoords(vehicle), GetEntityHeading(vehicle))
      end
      if object then
            local boneOffset = 0.0
            if frontBone == 'chassis' or GetEntityBoneIndexByName(vehicle, frontBone) == -1 then
                boneOffset = offsetZ
            end
            AttachEntityToEntity(object, vehicle, GetEntityBoneIndexByName(vehicle, frontBone), front.x, front.y, front.z + boneOffset, front.rx, front.ry, front.rz, false, false, false, false, 0, true)
      end
    else
      RemovePlateForVehicle(vehicle)
      CreatePlateForVehicle(vehicle, prefix, number, type, front, rear, frontBone, rearBone)
    end
    if rear then
      local object = 0
      if IsSlotHasPlate(slot, vehicle, 'r') then
            object = GetSlotPlate(slot, vehicle, 'r')
      else
            object = LoadPlateEntity(vehicle, slot, 'r', GetEntityCoords(vehicle), GetEntityHeading(vehicle))
      end
      if object then
            local boneOffset = 0.0
            if rearBone == 'chassis' or GetEntityBoneIndexByName(vehicle, rearBone) == -1 then
                boneOffset = offsetZ
            end
            AttachEntityToEntity(object, vehicle, GetEntityBoneIndexByName(vehicle, rearBone), rear.x, rear.y, rear.z + boneOffset, rear.rx, rear.ry, rear.rz, false, false, false, false, 0, true)
      end
    else
      RemovePlateForVehicle(vehicle)
      CreatePlateForVehicle(vehicle, prefix, number, type, front, rear, frontBone, rearBone)
    end
    _g.loadedVehicle = {
      slot      = slot,
      prefix    = prefix,
      number    = number,
      type      = type,
      front   = front,
      rear      = rear,
      offsetZ   = offsetZ,
      frontBone = frontBone,
      rearBone= rearBone,
    }
end

function MenuUpdateEvent(vehicle)
    local slider      = exports.zerodream_sliderui
    local platePrefix = slider:GetSelect('select_area').value
    local plateNumber = slider:GetInputValue('plate_number')
    local plateType   = slider:GetSelect('select_type').value
    local enableFront = slider:IsCheckboxChecked('enable_front')
    local enableRear= slider:IsCheckboxChecked('enable_rear')
    local frontBone   = slider:GetSelect('select_front_bone').value
    local rearBone    = slider:GetSelect('select_rear_bone').value
    local frontX      = slider:GetInputValue('front_x') or 0
    local frontY      = slider:GetInputValue('front_y') or 0
    local frontZ      = slider:GetInputValue('front_z') or 0
    local frontRX   = slider:GetInputValue('front_rx') or 0
    local frontRY   = slider:GetInputValue('front_ry') or 0
    local frontRZ   = slider:GetInputValue('front_rz') or 0
    local rearX       = slider:GetInputValue('rear_x') or 0
    local rearY       = slider:GetInputValue('rear_y') or 0
    local rearZ       = slider:GetInputValue('rear_z') or 0
    local rearRX      = slider:GetInputValue('rear_rx') or 0
    local rearRY      = slider:GetInputValue('rear_ry') or 0
    local rearRZ      = slider:GetInputValue('rear_rz') or 0
    DebugPrint("MenuUpdateEvent", platePrefix, plateNumber, plateType, enableFront, enableRear, frontBone, rearBone, frontX, frontY, frontZ, frontRX, frontRY, frontRZ, rearX, rearY, rearZ, rearRX, rearRY, rearRZ)
    UpdatePlate(vehicle, platePrefix, plateNumber, plateType, enableFront and { x = frontX, y = frontY, z = frontZ, rx = frontRX, ry = frontRY, rz = frontRZ } or nil, enableRear and { x = rearX, y = rearY, z = rearZ, rx = rearRX, ry = rearRY, rz = rearRZ } or nil, frontBone, rearBone)
end

function OpenPlateMenu(vehicle, vehicleData)
    local slider = exports.zerodream_sliderui
    local list = Config.plateList
    local areaList = {}
    local frontBoneList = {
      { text = '车身', value = 'chassis' },
      { text = '前保险杠', value = 'bumper_f' },
      { text = '引擎盖', value = 'bonnet' },
    }
    local rearBoneList = {
      { text = '车身', value = 'chassis' },
      { text = '后保险杠', value = 'bumper_r' },
      { text = '后备箱', value = 'boot' },
    }
    for k, v in pairs(Config.allowPrefixList) do
      table.insert(areaList, { text = v, value = v })
    end
    _g.isInEditor = true
    if not slider:IsContainerExists() then
      slider:CreateContainer('ZERODREAM PLATE', '车牌系统免费版', '25%', '70%', '5%', '5%', function()
            local plate = GetVehicleNumberPlateText(vehicle)
            RemovePlateForVehicle(vehicle)
            FreezeEntityPosition(vehicle, false)
            Wait(1000)
            _g.isInEditor = false
      end)
    else
      slider:RemoveAllElements()
    end
    local loaded = json.decode(json.encode(vehicleData))
    slider:CreateInput('plate_number', '输入车牌号码', '在这里输入你的车牌号码', loaded.number or 'B233MC', 'width: 100%', function(value)
      MenuUpdateEvent(vehicle)
    end)
    slider:CreateSelect('select_area', '选择牌照地区', '请选择车辆的牌照地区', loaded.prefix or '粤', areaList, 'width: 100%', function(value)
      MenuUpdateEvent(vehicle)
    end)
    slider:CreateSelect('select_type', '选择牌照类型', '请选择车辆的牌照类型', loaded.type or 'blue', list, 'width: 100%', function(value)
      MenuUpdateEvent(vehicle)
    end)
    slider:CreateCheckbox('enable_front', '是否启用前车牌', loaded.front and true or false, '', function(checked)
      MenuUpdateEvent(vehicle)
    end)
    slider:CreateSelect('select_front_bone', '前车牌固定处', '请选择前车牌的固定的位置', loaded.frontBone or 'chassis', frontBoneList, 'width: 100%', function(value)
      MenuUpdateEvent(vehicle)
    end)
    slider:CreateSlider('front_x', '前车牌 X 偏移量', '前车牌相对车辆左右偏移量', loaded.front and loaded.front.x or 0.0, -5.0, 5.0, 0.001, nil, function(value)
      MenuUpdateEvent(vehicle)
    end)
    slider:CreateSlider('front_y', '前车牌 Y 偏移量', '前车牌相对车辆前后偏移量', loaded.front and loaded.front.y or 0.0, -5.0, 5.0, 0.001, nil, function(value)
      MenuUpdateEvent(vehicle)
    end)
    slider:CreateSlider('front_z', '前车牌 Z 偏移量', '前车牌相对车辆上下偏移量', loaded.front and loaded.front.z or 0.0, -5.0, 5.0, 0.001, nil, function(value)
      MenuUpdateEvent(vehicle)
    end)
    slider:CreateSlider('front_rx', '前车牌 X 旋转量', '前车牌相对车辆 X 旋转量', loaded.front and loaded.front.rx or 0.0, -180.0, 180.0, 0.01, nil, function(value)
      MenuUpdateEvent(vehicle)
    end)
    slider:CreateSlider('front_ry', '前车牌 Y 旋转量', '前车牌相对车辆 Y 旋转量', loaded.front and loaded.front.ry or 0.0, -180.0, 180.0, 0.01, nil, function(value)
      MenuUpdateEvent(vehicle)
    end)
    slider:CreateSlider('front_rz', '前车牌 Z 旋转量', '前车牌相对车辆 Z 旋转量', loaded.front and loaded.front.rz or 0.0, -180.0, 180.0, 0.01, nil, function(value)
      MenuUpdateEvent(vehicle)
    end)
    slider:CreateCheckbox('enable_rear', '是否启用后车牌', loaded.rear and true or false, '', function(checked)
      MenuUpdateEvent(vehicle)
    end)
    slider:CreateSelect('select_rear_bone', '后车牌固定处', '请选择后车牌的固定的位置', loaded.rearBone or 'chassis', rearBoneList, 'width: 100%', function(value)
      MenuUpdateEvent(vehicle)
    end)
    slider:CreateSlider('rear_x', '后车牌 X 偏移量', '后车牌相对车辆左右偏移量', loaded.rear and loaded.rear.x or 0.0, -5.0, 5.0, 0.001, nil, function(value)
      MenuUpdateEvent(vehicle)
    end)
    slider:CreateSlider('rear_y', '后车牌 Y 偏移量', '后车牌相对车辆前后偏移量', loaded.rear and loaded.rear.y or 0.0, -5.0, 5.0, 0.001, nil, function(value)
      MenuUpdateEvent(vehicle)
    end)
    slider:CreateSlider('rear_z', '后车牌 Z 偏移量', '后车牌相对车辆上下偏移量', loaded.rear and loaded.rear.z or 0.0, -5.0, 5.0, 0.001, nil, function(value)
      MenuUpdateEvent(vehicle)
    end)
    slider:CreateSlider('rear_rx', '后车牌 X 旋转量', '后车牌相对车辆 X 旋转量', loaded.rear and loaded.rear.rx or 0.0, -180.0, 180.0, 0.01, nil, function(value)
      MenuUpdateEvent(vehicle)
    end)
    slider:CreateSlider('rear_ry', '后车牌 Y 旋转量', '后车牌相对车辆 Y 旋转量', loaded.rear and loaded.rear.ry or 0.0, -180.0, 180.0, 0.01, nil, function(value)
      MenuUpdateEvent(vehicle)
    end)
    slider:CreateSlider('rear_rz', '后车牌 Z 旋转量', '后车牌相对车辆 Z 旋转量', loaded.rear and loaded.rear.rz or 180.0, -180.0, 180.0, 0.01, nil, function(value)
      MenuUpdateEvent(vehicle)
    end)
    slider:CreateHtml('description_text', string.format('<p>每次修改车牌需要花费 <b style="color: rgb(147,178,88);">%s 元</b></p>', Config.platePrice))
    slider:CreateButton('refresh_plate', 'primary', '<i class="fa fa-car"></i>&nbsp;&nbsp;刷新车牌', 'width: 100%', function()
      RemovePlateForVehicle(vehicle)
      Citizen.Wait(100)
      MenuUpdateEvent(vehicle)
    end)
    slider:CreateButton('submit_plate', 'success', '<i class="fa fa-check"></i>&nbsp;&nbsp;提交车牌', 'width: 100%', function()
      local plate = slider:GetInputValue('plate_number')
      local prefix = slider:GetSelectValue('select_area')
      local plateType = slider:GetSelectValue('select_type')
      local front = slider:IsCheckboxChecked('enable_front')
      local rear = slider:IsCheckboxChecked('enable_rear')
      local frontPos = {
            x = slider:GetSliderValue('front_x'),
            y = slider:GetSliderValue('front_y'),
            z = slider:GetSliderValue('front_z'),
            rx = slider:GetSliderValue('front_rx'),
            ry = slider:GetSliderValue('front_ry'),
            rz = slider:GetSliderValue('front_rz'),
      }
      local rearPos = {
            x = slider:GetSliderValue('rear_x'),
            y = slider:GetSliderValue('rear_y'),
            z = slider:GetSliderValue('rear_z'),
            rx = slider:GetSliderValue('rear_rx'),
            ry = slider:GetSliderValue('rear_ry'),
            rz = slider:GetSliderValue('rear_rz'),
      }
      local frontBone = slider:GetSelectValue('select_front_bone')
      local rearBone = slider:GetSelectValue('select_rear_bone')
      if plate == '' then
            ZeroDream.Hud.SendNotification('车牌号码不能为空')
            return
      end
      if string.len(plate) > 8 then
            ZeroDream.Hud.SendNotification('车牌号码不能超过 8 个字符')
            return
      end
      if area == '' then
            ZeroDream.Hud.SendNotification('请选择车牌地区')
            return
      end
      if plateType == '' then
            ZeroDream.Hud.SendNotification('请选择车牌类型')
            return
      end
      local gtaPlate = GetVehicleNumberPlateText(vehicle)
      ZeroDream.Callbacks.Trigger('zerodream_vehplatefree:savePlate', function(result)
            if result.success then
                slider:RemoveContainer()
                ZeroDream.Hud.SendNotification('车牌保存成功')
            else
                ZeroDream.Hud.SendNotification(result.message)
            end
      end, gtaPlate, prefix, plate, plateType, front, rear, frontPos, rearPos, frontBone, rearBone)
    end)
    slider:CreateButton('cancel_close', 'warning', '<i class="fa fa-times"></i>&nbsp;&nbsp;取消修改', 'width: 100%', function()
      slider:RemoveContainer()
    end)
    slider:CreateButton('remove_plate', 'danger', '<i class="fa fa-trash"></i>&nbsp;&nbsp;移除车牌', 'width: 100%', function()
      local gtaPlate = GetVehicleNumberPlateText(vehicle)
      ZeroDream.Callbacks.Trigger('zerodream_vehplatefree:removePlate', function(result)
            if result.success then
                slider:RemoveContainer()
                ZeroDream.Hud.SendNotification('车牌移除成功')
            else
                ZeroDream.Hud.SendNotification(result.message)
            end
      end, gtaPlate)
    end)
    MenuUpdateEvent(vehicle)
end

Citizen.CreateThread(function()
    InitPlate()
    while true do
      for k, v in pairs(_g.loadedVehicle) do
            if not DoesEntityExist(k) then
                RemovePlateForVehicle(k)
            end
      end
      local coords = GetEntityCoords(PlayerPedId())
      local drives = IsPedInAnyVehicle(PlayerPedId()) and GetVehiclePedIsIn(PlayerPedId(), false) or 0
      for _, car in ipairs(GetGamePool('CVehicle')) do
            local plate = GetVehicleNumberPlateText(car)
            local dist = #(coords - GetEntityCoords(car))
            if dist < Config.updateDist then
                if _g.vehicleList and not _g.loadedVehicle then
                  if not _g.isInEditor or car ~= drives then
                        local data = _g.vehicleList
                        DebugPrint('Thread 1: Load plate', plate, json.encode(data))
                        CreatePlateForVehicle(car, data.prefix, data.number, data.type, data.front, data.rear, data.frontBone or 'chassis', data.rearBone or 'chassis')
                  end
                elseif _g.vehicleList and _g.loadedVehicle.offsetZ ~= -GetVehicleSuspensionHeight(car) then
                  if not _g.isInEditor or car ~= drives then
                        local data = _g.vehicleList
                        DebugPrint('Thread 1: Update plate', plate, json.encode(data))
                        UpdatePlate(car, data.prefix, data.number, data.type, data.front, data.rear, data.frontBone or 'chassis', data.rearBone or 'chassis')
                  end
                end
            elseif _g.loadedVehicle then
                DebugPrint('Thread 1: Remove plate', plate)
                RemovePlateForVehicle(car)
            end
      end
      Wait(500)
    end
end)

Citizen.CreateThread(function()
    while true do
      Wait(0)
      if IsPedInAnyVehicle(PlayerPedId()) and not _g.isInEditor then
            local vehicle = GetVehiclePedIsIn(PlayerPedId(), false)
            if GetPedInVehicleSeat(vehicle, -1) == PlayerPedId() then
                local find = false
                local pos= GetEntityCoords(vehicle)
                for k, v in pairs(Config.changePlates) do
                  local distance = #(pos - vec3(v.x, v.y, v.z))
                  if distance < 20.0 then
                        find = true
                        if not _g.isInEditor and IsMinimapRendering() then
                            DrawMarker(36, v.x, v.y, v.z + 0.5, 0, 0, 0, 0, 0, 0, 1.5, 1.5, 1.5, 0, 120, 215, 150, true, true)
                            DrawMarker(43, v.x, v.y, v.z - 1.0, 0, 0, 0, 0, 0, v.h, 3.0, 6.0, 1.5, 0, 120, 215, 150, true, false)
                        end
                  end
                  if distance < v.s then
                        if IsMinimapRendering() then
                            ZeroDream.Hud.DrawLabel3D(vec3(v.x, v.y, v.z + 0.5), '按~INPUT_CONTEXT~自定义车辆牌照')
                        end
                        if IsControlJustPressed(0, 51) then
                            local plate = GetVehicleNumberPlateText(vehicle)
                            ZeroDream.Callbacks.Trigger("zerodream_vehplatefree:checkPerm", function(result)
                              if result.success then
                                    SetEntityCoords(vehicle, v.x, v.y, v.z)
                                    SetEntityHeading(vehicle, v.h)
                                    SetVehicleOnGroundProperly(vehicle)
                                    FreezeEntityPosition(vehicle, true)
                                    local data = _g.vehicleList or { prefix = '粤', number = 'B233MC', type = 'blue', front = { x = 0.0, y = 2.5, z = 0.0, rx = 0.0, ry = 0.0, rz = 0.0 }, rear = { x = 0.0, y = -2.5, z = 0.0, rx = 0.0, ry = 0.0, rz = 180.0 }, frontBone = 'chassis', rearBone = 'chassis' }
                                    OpenPlateMenu(vehicle, data)
                                    Wait(1000)
                              else
                                    ZeroDream.Hud.SendNotification(result.message)
                              end
                            end, plate)
                        end
                  end
                end
                if not find then
                  Wait(1000)
                end
            else
                Wait(1000)
            end
      else
            Wait(1000)
      end
    end
end)

RegisterNetEvent('zerodream_vehplatefree:syncPlates')
AddEventHandler('zerodream_vehplatefree:syncPlates', function(list, tokenRef)
    _g.vehicleList = list
    _g.tokenRef    = tokenRef
end)

RegisterNetEvent('zerodream_vehplatefree:syncPlate')
AddEventHandler('zerodream_vehplatefree:syncPlate', function(plate, data)
    _g.vehicleList = data
    for k, v in pairs(_g.loadedVehicle) do
      if DoesEntityExist(k) and GetVehicleNumberPlateText(k) == plate then
            if not _g.isInEditor or not IsPedInAnyVehicle(PlayerPedId()) then
                RemovePlateForVehicle(k)
            else
                local vehicle = GetVehiclePedIsIn(PlayerPedId(), false)
                if vehicle ~= k or GetPedInVehicleSeat(vehicle, -1) ~= PlayerPedId() then
                  RemovePlateForVehicle(k)
                end
            end
      end
    end
end)

AddEventHandler('onResourceStop', function(resource)
    if resource == GetCurrentResourceName() then
      for k, v in pairs(_g.plateSlots) do
            UnregisterPlate(k)
      end
    end
end)

ChatGPT 发表于 2025-7-3 20:35:39

Aleeeee 发表于 2025-07-03 20:35
ZeroDream = exports.zerodream_core:GetSharedObject ...

从你提供的代码片段来看,暂时没有看到直接导致 `nil` 值拼接的问题,但需要注意以下几点:

1. **`IsPlateNumber` 函数中的逻辑**:在该函数中,`string.match(text, "^+$")` 的正则表达式似乎有问题。`^+$` 表示字符串中的一个或多个 `+` 字符。可能你是想检查是否符合某个特定的规则,你应该确认这是你的意图。

2. **DebugPrint 的使用**:在调用 `DebugPrint` 的地方,确保传递的参数不为 `nil`,否则可能会在打印时导致拼接错误。

3. **`Config.debug`**:确保 `Config` 表是存在并且合法的。如果 `Config` 中的 `debug` 属性是 `nil`,那么 `DebugPrint` 在某些地方也可能导致错误。

4. **在代码其他部分查找 `err` 的来源**:由于错误提示是关于 `err` 的问题,你可以在代码的其他部分(特别是在处理错误的地方)查找是否有对 `err` 变量的赋值,如果 `err` 是从某个返回值中取得的,确保这个返回值不会是 `nil`。

5. **添加额外的日志**:在可能出错的地方添加更多的 `print` 语句,这样可以帮助你确定 `nil` 值是在哪里产生的。例如,在函数开头和重要操作后添加 `print` 来输出相关变量的值。

如果你能提供更多关于如何使用这些函数的上下文代码,或者你收到错误的具体行号和位置,我能更容易地帮助你找到并解决问题。希望这些能帮到你!
页: [1]
查看完整版本: 修复