fivem scripting did you write this?

local object_netID = nil
local objectModel = 'prop_beach_volball01'
local forwardForceIntensity = 20.0
local upwardForceIntensity = 30.0
local gamestate = {['Pickup'] = false, ['Serve'] = false, ['Playing'] = false}
local createdObject = nil
-- resets/changes the game state
RegisterNetEvent('main-volleyball:client:changegamestate')
AddEventHandler('main-volleyball:client:changegamestate', function(toChange)
    for k, v in pairs(gamestate) do
        gamestate[k] = false
    end
    if toChange then
        gamestate[toChange] = true
    end
end)

local function loadModel(model)
    local modelHash = GetHashKey(model)
    RequestModel(modelHash)

    while not HasModelLoaded(modelHash) do
        Citizen.Wait(100)
    end
    return modelHash
end

local function spawn_the_ball()
    local player = PlayerPedId()
    local position = Config.ballpos
    local modelHash = loadModel(objectModel)
    createdObject = CreateObjectNoOffset(modelHash, Config.ballpos.x, Config.ballpos.y, Config.ballpos.z - 1, true, false, true)
    attachObjectToPlayerLeftHand()
    ActivatePhysics(createdObject)
    local object_netID = ObjToNet(createdObject)
    TriggerServerEvent('main-volleyball:setObjectNetworkID', object_netID)
    gamestate['Serve'] = true
end
RegisterNetEvent('main-volleyball:client:spawnball', function()
    spawn_the_ball()
end)


local function isPlayerNearObject(object, distanceThreshold)
    local playerCoords = GetEntityCoords(PlayerPedId())
    local objCoords = GetEntityCoords(object)
    local distance = #(playerCoords - objCoords)
    return distance <= distanceThreshold
end

Citizen.CreateThread(function()
    local lastUse = 0
    local cooldown = 2000
    while true do
        Citizen.Wait(1)
        if object_netID then
            createdObject = NetworkGetEntityFromNetworkId(object_netID)
            -- Start of Pickup GameState
            if gamestate['Pickup'] then
                if not NetworkHasControlOfEntity(createdObject) then
                    local timeout = GetGameTimer() + 2000
                    NetworkRequestControlOfEntity(createdObject)
                    while (not NetworkHasControlOfEntity(createdObject)) and (GetGameTimer() < timeout) do
                        Citizen.Wait(0)
                    end
                end
                alert('Press ~INPUT_PICKUP~ to pickup the ball')

                if IsControlJustReleased(0, 38) and isPlayerNearObject(createdObject, 1.5) then
                    TriggerEvent('main-volleyball:client:animation', 'random@domestic', 'pickup_low', 3.0)
                    Citizen.Wait(1000)
                    attachObjectToPlayerLeftHand()
                    gamestate['Pickup'] = false
                    gamestate['Serve'] = true
                    TriggerServerEvent('main-volleyball:server:updategamestate')
                end
            end
            -- End of Pickup Gamestate

            -- Start of Serve Gamestate
            if gamestate['Serve'] then
                alert('Press ~INPUT_DETONATE~ to serve')
                if IsControlJustReleased(0, 47) then
                    gamestate['Serve'] = false
                    gamestate['Playing'] = true
                    local currentTime = GetGameTimer()
                    if currentTime > lastUse + cooldown then
                        lastUse = currentTime
                        if not NetworkHasControlOfEntity(createdObject) then
                            local timeout = GetGameTimer() + 2000
                            NetworkRequestControlOfEntity(createdObject)
                            while (not NetworkHasControlOfEntity(createdObject)) and (GetGameTimer() < timeout) do
                                Citizen.Wait(0)
                            end
                        end
                        DetachEntity(createdObject, true, false)
                        ClearPedTasks(PlayerPedId())
                        local playerRotation = GetEntityRotation(PlayerPedId(), 2)
                        local forwardVector = GetEntityForwardVector(PlayerPedId())
                        local forceVector = vector3(
                            forwardVector.x * forwardForceIntensity,
                            forwardVector.y * forwardForceIntensity,
                            upwardForceIntensity
                        )
                        TriggerEvent('main-volleyball:client:animation', 'misscommon@response', 'screw_you', 3.0)
                        ApplyForceToEntity(createdObject, 1, forceVector.x, forceVector.y, forceVector.z, 0.0, 0.0, 0.0, 0, false, true, true, false, true)
                        ClearPedTasks(PlayerPedId())
                    end
                end
            end
            -- End of Serve Gamestate

            -- Start of Playing Gamestate
            if gamestate['Playing'] then
                --alert('Press ~INPUT_PICKUP~ to hit the ball')
                if isPlayerNearObject(createdObject, 1.5) and IsControlJustReleased(0, 38) then -- 38 is the key code for 'E'
                    local currentTime = GetGameTimer()
                    if currentTime > lastUse + cooldown then
                        lastUse = currentTime
                        if not NetworkHasControlOfEntity(createdObject) then
                            local timeout = GetGameTimer() + 2000
                            NetworkRequestControlOfEntity(createdObject)
                            while (not NetworkHasControlOfEntity(createdObject)) and (GetGameTimer() < timeout) do
                                Citizen.Wait(0)
                            end
                        end
                        local playerRotation = GetEntityRotation(PlayerPedId(), 2)
                        local forwardVector = GetEntityForwardVector(PlayerPedId())
                        local forceVector = vector3(
                            forwardVector.x * forwardForceIntensity,
                            forwardVector.y * forwardForceIntensity,
                            upwardForceIntensity
                        )
                        TriggerEvent('main-volleyball:client:animation', 'amb@prop_human_movie_bulb@base', 'base', 4.0)
                        ApplyForceToEntity(createdObject, 1, forceVector.x, forceVector.y, forceVector.z, 0.0, 0.0, 0.0, 0, false, true, true, false, true)
                    end
                end
            end
            -- End of Playing GameState
        end
    end
end)


RegisterNetEvent('main-volleyball:updateObjectNetworkID')
AddEventHandler('main-volleyball:updateObjectNetworkID', function(netID)
    object_netID = netID
    if netID then
        createdObject = NetworkGetEntityFromNetworkId(object_netID)
    end
end)


Citizen.CreateThread(function()
    TriggerServerEvent('main-volleyball:requestObjectNetworkID')
end)



-- Function to load animation dictionary in FiveM
function LoadAnimDict(dict)
    while (not HasAnimDictLoaded(dict)) do
        RequestAnimDict(dict)
        Citizen.Wait(5)
    end
end


function attachObjectToPlayerLeftHand()
    local playerPed = PlayerPedId()
    local boneIndex = GetPedBoneIndex(playerPed, 18905) -- 18905 is the bone index for the left hand
    LoadAnimDict('missfam4')
    TaskPlayAnim(playerPed, 'missfam4', 'base', 8.0, -8.0, -1, 51, 0, false, false, false)
    --TriggerEvent('main-volleyball:client:animation', 'missfam4', 'base', 3.0)
    AttachEntityToEntity(createdObject, playerPed, boneIndex, 0.15, 0.18, 0.05, 0.0, 0.0, 0.0, true, true, false, true, 1, true)
    Citizen.Wait(2000)
end






local objectThresholdZ = 0.95 -- The minimum Z-axis value to consider that the object is on the ground
local object = nil
local objectCoord = nil

Citizen.CreateThread(function()
    while true do
        Citizen.Wait(200) -- Always add a wait in your main loop to prevent freezing
        local playerPed = PlayerPedId()
        local playerCoord = GetEntityCoords(playerPed)
        if DoesEntityExist(createdObject) then
            objectCoord = GetEntityCoords(createdObject)
            --local unusedBool, groundZ = GetGroundZFor_3dCoord(objectCoord.x, objectCoord.y, 99999.0, 1)
            local unusedBool, groundZ = GetGroundZFor_3dCoord(objectCoord.x, objectCoord.y, objectCoord.z)
            if (objectCoord.z - groundZ) <= objectThresholdZ then
                TriggerServerEvent('main-volleyball:server:ballhitground')
            end

        else
            Citizen.Wait(0)
        end

    end
end)