Launch sale · 9LABS Crane Tow Truck Job Ends in See the deal

Nothing in this manual matched.

9LABS Gardener Job · v1.0.0

A flatbed, a mower, and every garden in the city.

Clock on at the yard, take the truck with the riding mower on the bed and work the contracts on the board: mow stripes into lawns, trim hedges and topiaries, clear leaves, prune trees from a ladder, plant flower beds and clean pools — alone or as a crew of four.

Introduction

A gardener job where every task is done by hand, and the work stays visible on the property for everyone.

What is in the resource

  • A custom garden shop MLO in Del Perro as the crew's base, with a garage for the truck.
  • A streamed flatbed truck with a ramp, a riding mower you strap on and off, four opening toolboxes with every tool, and a ladder on the rack.
  • Seven tasks: mowing with real stripes, hedge trimming, topiary sculpting, leaf cleanup with a blower and bags, pruning dead branches from a ladder, planting flower beds and cleaning swimming pools.
  • About 1,560 properties (1,503 gardens, parks and green areas plus 57 private pools) scanned from the GTA map, so contracts work all over the map without placing anything.
  • Close-up work with the mouse for hedges, topiaries, branches, beds and pools.
  • Synced for everyone nearby: the mowing stripes, the trimmed hedges, the planted flowers, the clean pool water, the tools in hand, the ladder and the blower's leaves.
  • Ratings from one to five stars, pay per unit of work, levels that unlock tasks, urgent and weather bonuses.
  • Crews of up to four, paid by each member's share of the work.
  • A job app for phones and tablets (lb-phone, lb-tablet, Quasar, yPhone, okokPhone, GKS) and a full Meteo Job Tablet integration.
  • An in-game admin panel for the yard, truck spots, pay, levels, contracts and sound, and Discord logging.
  • English, German and Bosnian included.

Almost nothing is configured in a file. Apart from the handful of options in config.lua, the whole job is set up in game through /gardenadmin, and changes save on the server and apply live.

Requirements

OneSync and a framework. Everything else is optional.

ServerOneSync.
FrameworkQbox, QBCore, ESX or standalone. Anything else can use the custom bridge.
StorageNo SQL. Settings and profiles are saved in the resource's storage/ folder, which is created on start.
Optionalox_target or qb-target, ox_lib notifications, a vehicle keys script, a fuel script, a weather sync script (for rain and heat), a phone or tablet.

Installation

Start it after your framework and phone, then check the yard in game.

  1. Put 9labs_landmower in your resources folder and keep that exact name.
  2. Add it to server.cfg, after your framework, target, keys, fuel, weather and phone resources.
  3. Give your admins the ACE below (or keep using command, which txAdmin admins already have).
  4. Set the basics in config.lua.
  5. Restart the server.
  6. As an admin, run /gardenadmin and check the yard, truck spots and pay for your server.
ensure 9labs_landmower
add_ace group.admin gardener.admin allow

Players must reconnect after you update the resource. It streams a vehicle, props, textures and an animation, and those only reload when a client connects. Invisible grass or wrong textures on the pool tools almost always mean someone did not reconnect.

config.lua

Only the basics live here. Pay, yard, contracts, levels, sound and Discord logs are set in game.

LmConfig = {
    locale = 'en',              -- 'en', 'de' or 'bs'
    framework = 'auto',         -- 'auto' | 'qbx' | 'qb' | 'esx' | 'standalone' | 'custom'
    keys = 'auto',              -- 'auto' | 'qbx' | 'qb' | 'none' | 'custom'
    fuel = 'auto',              -- ox_fuel, LegacyFuel, cdn-fuel or the native level
    interaction = '9labs_ui',   -- '9labs_ui' | 'ox_target' | 'qb-target' | 'custom'
    notify = '9labs_ui',        -- '9labs_ui' | 'ox_lib' | 'native' | 'custom'
    truck = { plate = 'MOW%04d', colours = { 50, 0 } },
    lockInventory = true,       -- true | false | 'custom'
    adminAces = { 'gardener.admin', 'command' },
    standalone = { startingCash = 0, startingBank = 0 },
    debug = false,
    devTools = false,
}
OptionValuesWhat it does
localeen, de, bsLanguage of the UI, notifications and logs. Add your own file in locales/ and list it in fxmanifest.lua
frameworkauto, qbx, qb, esx, standalone, customauto picks the first running one: qbx_core, qb-core, es_extended, otherwise standalone
keysauto, qbx, qb, none, customKeys for the company truck and mower; crew members get keys too. qb uses the server exports of qb-vehiclekeys 1.6 and newer, older versions get the old client event. ESX has no common keys script: use custom
fuelauto or a resource nameauto fills the vehicles through ox_fuel, LegacyFuel, cdn-fuel or the native level
interaction9labs_ui, ox_target, qb-target, customHow players talk to the yard foreman. Everything else (truck, tools, work spots) always uses the on-screen keys
notify9labs_ui, ox_lib, native, customNotification style
truckplate, coloursPlate format and the primary/secondary GTA colour ids of the company truck
controlscontrol id and label per actionKeys of the on-screen actions, see Player controls
lockInventorytrue, false, customBlocks the inventory and its hotbar keys while a tool is in hand. true works with ox_inventory, qb-inventory and every inventory that respects inv_busy
adminAceslist of ACEsWho can open /gardenadmin and use the admin test commands. Any one of them is enough
appsresource name = true/falseWhich phones and tablets get the job app, see Phone and tablet apps
standalonestartingCash, startingBankStandalone only: pay goes to a built-in wallet per FiveM license. Standalone takes no shift deposit
debugtrue / falseExtra server and F8 output
devToolstrue / falseMap checking commands for admins (/gardensurvey pool, /gardenpoolfx, /gardenwhere). Leave off on live servers

First setup

/gardenadmin opens the admin panel. Changes save on the server in storage/settings.json and apply live.

SectionWhat you set
GeneralRequired job and grade, shift deposit, the yard (foreman position), foreman ped, foreman on or off, truck spots, the radius for returning the truck, the map blip
PayAccount (cash or bank), contract fee, pay per task unit (100 m² mowed, a hedge, a topiary, a leaf bag, a branch, a flower, 10 m² of pool), level, on-time, urgent, weather and crew bonuses
LevelsXP per contract, XP per rating star, XP per level, the maximum level
ContractsOffer distance, offers on the board, board refresh, pause between contracts, drop cooldown, urgent share, how long finished properties keep their fresh look, clean mowing speed, and every task on or off with its weight and the level that unlocks it
SoundVolume and range of the tool sounds
Discord logsWebhook and what to log

The default yard is the Los Santos Golf Club parking lot. Stand where the foreman should be and use My position, then add truck spots the same way. Points can also be typed in or pasted as vector4(...).

Your own NPC instead of the foreman

Turn the foreman off in General. Your script then starts and ends shifts through StartShift and EndShift; with an options table the yard distance is not checked and the truck spawns where you say.

-- server side, e.g. when your own NPC hands out the truck
local r = exports['9labs_landmower']:StartShift(source, { spawn = vector4(-1340.2, 46.1, 54.2, 90.0) })
if not r.ok then print(r.error) end

Garden shop MLO

A home for the crew: 9labs_gardenshop, a garden shop interior in Del Perro.

  • A sales counter with a register, a job board with a desk and computer, a staff room with lockers and a garage for the company truck.
  • Map files only, no scripts: nothing runs on the client or the server.
  • Every prop inside is an original GTA V object.
  1. Put 9labs_gardenshop in your resources and add ensure 9labs_gardenshop to server.cfg. It needs sv_enforceGameBuild 2060 or newer.
  2. Fully restart the FiveM client once; a new MLO does not load from a resource restart alone.
  3. Stand behind the counter, open /gardenadmin → General and use My position for the foreman, then add a truck spot in front of the garage. The shop's README lists ready coordinates for the counter, job board, lockers and garage.

Map conflicts. The shop replaces the building sm_11_bld1 and its collision. Do not run another map that changes the same building.

Properties and pools

Nothing has to be placed. Every property comes from the GTA map.

  • 1,503 garden properties (private gardens, parks and green areas) were scanned from the map: lawns from the ground collision, reachable from a road, with the hedges and broadleaf trees around them. Flower beds and topiaries are placed on the lawn.
  • 90 swimming pools were scanned the same way and checked in game: you can swim in them and the floor and edge match. 33 belong to a garden property, 57 are offered on their own as a private pool.
  • Objects standing on a lawn (graves, benches, poles) are detected in game and taken out of the mowing, so no grass grows inside a gravestone.
  • A property that was worked on keeps its fresh look (mowing stripes, flowers, the topiary) for everyone for the time set in /gardenadmin, and is not offered again during that time.

Custom maps. A map that replaces a garden or a pool can leave overgrowth floating or a pool without water. Those properties simply drop that task. To check every pool on your own map, turn on devTools and run /gardensurvey pool.

Discord logs

Shifts, payouts, admin changes and security events, each one switchable.

In /gardenadmin → Discord logs paste a webhook URL, save, then press Send test. Only Discord webhook addresses are accepted, and the webhook never leaves the server: the panel shows it masked.

How the job works

Shift, board, property, report.

  1. Start a shift at the yard foreman. A deposit is taken and the truck comes with the riding mower strapped on the bed.
  2. Open the contract board (F6 or the phone app) and pick an offer: a private garden, a park, a green area or a private pool, each with one to three tasks, the distance, the estimated time and the pay. Urgent contracts pay extra when finished before the deadline.
  3. Drive there and do every task. The HUD lists what is left and what you still have to pack.
  4. Pack up: mower strapped, ladder on the rack, ramp raised, and stand next to the truck.
  5. The contract report shows the rating per task, the time, every crew member's share and the pay.
  6. Bring the truck back to the yard to end the shift and get the deposit back.

The tasks

Seven kinds of work, each done by hand.

TaskToolsWhat you do
MowingRiding mowerLower the ramp (E at the tailgate), release the mower (G) and drive over the overgrown lawn. Rows leave real stripes. Mowing faster than the clean speed leaves a ragged cut that lowers the rating; rain lowers the clean speed
Hedge trimmingHedge trimmerWalk up to a marked hedge and press E. In the close-up hold the left mouse button over the overgrowth. Cutting into the hedge where nothing sticks out counts as a gouge and lowers the rating
TopiaryHedge trimmerA new figure is planted on the lawn, completely overgrown. Carve it out the same way; the finished figure stays on the property
Leaf cleanupLeaf blowerBlow the leaf piles onto the tarp (hold the left mouse button, aim with the camera), bag them at the tarp (E), carry the bags to the truck and throw them on the bed (E)
PruningPruning saw, ladderTake the ladder from the rack (H), carry it to the tree and lean it against the trunk (H), climb it with the saw and press E at a dead branch. Saw with the left mouse button while moving the mouse left and right, then carry the fallen branches to the truck
Flower bedTrowel, seedling tray, watering canE at the bed opens the close-up. Dig six holes with the trowel, plant the seedlings, water every plant (twice in a heat wave)
Pool cleaningSkimmer, brush, vacuum, test kitSee Pool cleaning

You can only hold one thing at a time: a tool, a leaf bag, a branch or the ladder. Bags cannot be picked up with a tool in hand.

Pool cleaning

Skim, brush, vacuum, then get the water chemistry right.

  1. Take a pool tool from the truck and press E at the pool edge. The close-up camera opens over the water.
  2. Hold the left mouse button on a target. A marker shows what you aim at; a grey marker means it is out of reach, walk around the pool.
  3. Skimmer: lift the floating leaves and the scum. The net holds 8; empty it into the bag at the pool with E.
  4. Brush: scrub the algae on the floor and the walls.
  5. Vacuum: clear the dirt on the floor.
  6. Test kit: when the pool is clean, compare the sample colour with the chart and press the number (1 to 4) of the dose written next to the matching colour. A wrong dose means a new test and a lower rating.
  7. Carry the leaf bags to the truck. The task is done when the water is balanced and every bag is on the bed.

Truck, mower and tools

Everything the crew needs rides on the flatbed.

  • Ramp: E at the tailgate lowers and raises it. It cannot be raised while the mower stands on it.
  • Mower: G straps it to the bed or releases it. Releasing only works with the ramp lowered.
  • Toolboxes: E opens, G closes. Leaf blower, skimmer and pool brush front left; hedge trimmer and pool vacuum front right; trowel, watering can and test kit rear left; saw and seedling tray rear right. Scroll to choose when a box holds more than one tool, and put a tool back in the same box.
  • Ladder: H takes it from the rack, leans it against a wall or a tree trunk (a green outline shows where it will stand) and puts it back on the rack.

Prompts follow where you look: when two actions share a key (strapping the mower and closing a toolbox), the one you face is the one that runs. Only your own crew gets the prompts of your truck.

Rating, pay and levels

Do it well and on time, earn more.

  • Pay = contract fee + pay per unit of work for every task (all in /gardenadmin → Pay).
  • Rating: every task is rated (ragged mowing, gouged hedges, missed spots, wrong pool doses). The contract gets one to five stars, which scale the pay and give extra XP.
  • Bonuses: level, finishing within the estimated time, urgent contracts before the deadline, working in the rain, and the crew bonus.
  • Levels unlock tasks. Defaults: mowing and leaves 1, hedges 3, pool cleaning 4, flower beds 5, pruning 8, topiary 12.

Crew work

Up to four gardeners on one truck, paid by the work each one did.

On shift, open the foreman's tablet and invite colleagues standing next to you; they accept with Y or decline with N. Crew members use your truck and mower, see the same contract and get keys, without a deposit.

  • The crew earns 25% more for every extra member, split by each member's share of the work, and the crew bonus is added on top.
  • Members who did less than 10% of the work get nothing. The report shows everyone's share.
  • When the leader ends the shift the crew is disbanded; when a member leaves, only that member goes off duty.

Player controls

On-screen keys, all set in config.lua.

KeyAction
F6Contract board (rebind in Settings → Key Bindings → FiveM)
ERamp, toolboxes, start work, pick up a bag or a branch, throw it on the truck
GStrap or release the mower, close a toolbox, put down what you carry
HTake, lean and stow the ladder
Mouse wheelChoose a tool in a toolbox
Y / NAccept or decline a crew invite

Close-up camera

KeyAction
Left mouseWork: trim, saw, dig, plant, water, skim, brush, vacuum
Right mouse (hold) + moveLook around
A / D, W / STurn and tilt the camera
Mouse wheelZoom
1 – 4Pick the dose in the pool water test
ESC / BackspaceLeave
controls = {
    use = { control = 38, label = 'E' },      -- ramp, toolboxes, start work on the hedge/bed/branch in front of you
    secure = { control = 47, label = 'G' },   -- strap / release the mower, put a tool back
    ladder = { control = 74, label = 'H' },   -- take, lean and stow the ladder
    depot = { control = 38, label = 'E' },    -- talk to the depot foreman
    accept = { control = 246, label = 'Y' },  -- accept a crew invite
    decline = { control = 306, label = 'N' }, -- decline a crew invite
},

Commands

CommandWhoWhat
/gardenadminAdminOpens the admin panel
/gardentruckAdminSpawns a truck with a mower next to you (testing)
/gardentest [mow|hedge|topiary|leaves|prune|plant|pool]AdminGives you the nearest contract with that task
/gardenpooldoseAdminTesting: a pool contract at the pool you stand next to (starts a shift if needed) with everything but the water test done
/gardenboardPlayerOpens the contract board
/gardencancelPlayerDrops the current contract (crew: leader only)
gardenprof (F8)AnyoneMeasures this resource's client cost for 5 seconds

Phone and tablet apps

The job app installs itself on every supported phone and tablet that is running.

The app shows the shift, the current contract with GPS, the contract board button, your level and the best gardeners on the server. Turn single phones off in apps.

apps = {
    ['lb-phone'] = true, ['lb-tablet'] = true,
    ['qs-smartphone-pro'] = true, ['qs-smartphone'] = true,
    ['yseries'] = true, ['yphone'] = true, ['yflip-phone'] = true,
    ['okokPhone'] = true, ['gksphone'] = true, ['gks-tablet'] = true,
    ['meteo-jobtablet'] = true,
},

Meteo Job Tablet

Registered as a full job: Clock In and Out (at the yard, or anywhere when the foreman is turned off), accept, reject and cancel contracts, the dispatch panel, levels, rankings, achievements and tournaments.

Custom bridges

Your own framework, money, keys, weather, prompts or target.

Set framework = 'custom' (and/or keys = 'custom') and fill in the Custom block at the top of server/bridge.lua. Weather is read automatically from Renewed-Weathersync, qb-weathersync and cd_easytime; any other weather script goes through Custom.weather.

function Custom.identity(src) return GetPlayerIdentifierByType(src, 'license') end -- stable character id
function Custom.name(src) return GetPlayerName(src) end                          -- name in the job app
function Custom.addMoney(src, account, amount, reason) return false end          -- return true on success
function Custom.removeMoney(src, account, amount, reason) return false end
function Custom.getMoney(src, account) return 0 end
function Custom.hasJob(src, job, grade) return true end                          -- only used when a job is set
function Custom.giveKeys(src, vehicle, plate) end                                -- vehicle = server entity handle
function Custom.removeKeys(src, vehicle, plate) end
function Custom.weather() return nil end                                         -- 'CLEAR', 'RAIN', 'EXTRASUNNY' ... or nil

Set interaction = 'custom' and/or notify = 'custom' and fill in the Custom block at the top of client/bridge.lua.

function Custom.showContext(items) end                 -- items = { { key = 'E', text = '...', disabled = nil|true }, ... }
function Custom.hideContext() end
function Custom.notify(text, kind) end                 -- kind: 'info', 'success', 'error'
function Custom.addTarget(id, coords, radius, options) return nil end -- options: { { label, icon, onSelect } }, return a handle
function Custom.removeTarget(handle) end
function Custom.setFuel(vehicle, level) end
function Custom.lockInventory(locked) end              -- used when lockInventory = 'custom'

config.lua, locales/, both bridge files, both integration files and data/ stay open under Asset Escrow.

Exports

Everything another script needs to read or drive a shift.

Server

-- true when the player is on a gardener shift (leader or crew member)
local onDuty = exports['9labs_landmower']:IsOnDuty(source)

-- level and statistics of the player's character, or nil
local p = exports['9labs_landmower']:GetProfile(source)
-- p.level, p.xp, p.levelXp, p.nextXp, p.maxLevel, p.jobs, p.earned, p.best, p.rating, p.name

-- the shift and the current contract, or false when off shift
local shift = exports['9labs_landmower']:GetShiftState(source)
-- shift.truck, shift.mower (net ids), shift.deposit, shift.earned, shift.jobs, shift.leader (bool)
-- shift.next (seconds until new offers) or nil, shift.crew (see GetCrew) or nil
-- shift.task = { id, kind ('garden' | 'park' | 'green' | 'villa'), x, y, z (road), cx, cy, cz (property centre),
--   tasks = { { kind, done, units } }, ready, urgent, deadline, accepted, par, weather, pay } or nil

-- the full contract view with every task and its progress, or nil
local contract = exports['9labs_landmower']:GetContract(source)

-- the crew, or nil when the player works alone
local crew = exports['9labs_landmower']:GetCrew(source)
-- crew.leader, crew.members = { { id, name, leader } }

-- start or end a shift from your own script
exports['9labs_landmower']:StartShift(source)                                   -- same rules as the foreman: only at the yard
exports['9labs_landmower']:StartShift(source, { spawn = vector4(x, y, z, h) })  -- anywhere, truck at spawn (or a free truck spot)
-- returns { ok, truck } or { ok = false, error }

exports['9labs_landmower']:EndShift(source)                     -- { ok, summary } or { ok = false, confirm = true, error }
exports['9labs_landmower']:EndShift(source, { force = true })   -- end even when the truck is not back in the yard
exports['9labs_landmower']:EndShift(source, { refund = true })  -- your script decides about the deposit

-- drop the current contract (crew: acts for the leader)
exports['9labs_landmower']:AbandonJob(source)

Client

local shift = exports['9labs_landmower']:GetShiftState()   -- same shape as the server export
exports['9labs_landmower']:SetJobWaypoint()          -- GPS to the property
exports['9labs_landmower']:SetJobWaypoint('depot')   -- GPS to the yard

AddEventHandler('9labs_landmower:client:stateChanged', function(shift)
    -- shift is false when the player went off duty
end)

Server events

Local server events, safe to listen to from any resource.

AddEventHandler('9labs_landmower:shiftStarted', function(src, data)
    -- data.truck (net id), data.plate, data.deposit, data.account
end)

AddEventHandler('9labs_landmower:shiftEnded', function(src, data)
    -- data.reason, data.jobs, data.earned, data.deposit, data.refunded
end)

AddEventHandler('9labs_landmower:jobPaid', function(src, data)
    -- data.kind, data.stars (1-5), data.quality (0-100), data.total, data.paid, data.account
    -- data.xp, data.leveled, data.crewSize, data.pay, data.level, data.speed, data.crew
end)

AddEventHandler('9labs_landmower:stateChanged', function(src, shift)
    -- same shape as GetShiftState, false when off duty
end)

Example: a cut for the society account

AddEventHandler('9labs_landmower:jobPaid', function(src, data)
    if data.paid then
        exports['your_banking']:AddSocietyMoney('gardener', math.floor(data.total * .1))
    end
end)

Performance

Nothing runs for players who are not working nearby.

  • Contracts are kept on the server; clients subscribe only to properties within about 230 m and receive small changes, not the whole contract.
  • Grass, hedges, leaves and flowers are local props built with a per-frame budget, so a big park does not stall the game.
  • Tool effects of other players (blower, trimmer, mower, pool tools) start only when that player starts working, through one state per player.
  • gardenprof in F8 measures the resource on your own client for 5 seconds.

Troubleshooting

ProblemWhat to check
Nothing on the boardNo free property between the offer distances (/gardenadmin → Contracts), or every task is locked for your level
Invisible grass or overgrowthThe player did not reconnect after the update
Pool tools or algae show the wrong textureThe player did not reconnect after the update
Hedge overgrowth floats beside the hedgeThat hedge belongs to a map that was replaced by a custom map; the property simply stays mowing and leaves
A pool has no water or looks strangeA custom map changed that pool. Turn on devTools, stand next to it and run /gardensurvey pool
No keys for the truckCheck keys in config.lua. With auto, qbx_vehiclekeys or qb-vehiclekeys must be running; otherwise use custom
"Storage folder is missing"The server cannot create storage/ inside the resource. Create it by hand and make it writable
Admin panel says it cannot saveSame as above: storage/settings.json must be writable
Anticheat kicksWhitelist the events below
9labs_landmower:rpc:q
9lm:*

Nothing in this manual matched.