# Context: HL2 Linux Internal Cheat (`hl2hake`) ## What this is A 32-bit Linux internal for Half-Life 2 (native `hl2_linux`), loaded with `LD_PRELOAD` through Steam's bundled 32-bit runtime. It started as a port of the Windows `hake` reference (auto-bhop / auto-ABH / auto-climb) and now also covers ESP, aimbot, triggerbot and silent aim. ## Build / run - `./build.sh` -> `libhl2hake.so` (`g++ -m32 -O2 -std=c++17 -Wall -Wextra -Werror -fPIC -shared`, sources: `hl2h modules cinput netvars menu esp server bones vischeck`, `-ldl`). - `./play.sh` -> Steam runtime + `hl2.sh -steam -novid -console +map d1_town_01`. - Logs: `/tmp/hl2hake.log` (`logf`) and stderr. `./play.sh > /tmp/hl2run.log 2>&1` captures everything. - Debug env: - `HL2H_DRAW_TEST=1` — red probe rect + text, no menu needed. - `HL2H_BONE_DEBUG=1` — white marker at each NPC head hitbox. - `HL2H_NO_SERVER=1` — disable server-side health reads. - Config: `$XDG_CONFIG_HOME/hl2hake/config`, falling back to `~/.config/hl2hake/config`. Plain INI, saved on every change. ## Runtime architecture - Modules from `/proc/self/maps`: `client.so`, `engine.so`, `server.so`. PROT_NONE ranges excluded; file-backed bss tails merged. - Calling convention: cdecl (i386); `this` is the first stack argument. - Hooks (copy-and-swap vtable, 256 slots): - `CHLClient` slot 21 `CreateMove`; slot 35 `FrameStageNotify`. - `ISurface` slot 114 `PaintTraverseEx` (primary draw); slot 22 `DrawPrintText` fallback. Draws wrapped in `PushMakeCurrent`/`PopMakeCurrent`. - `CInput*` discovered by byte-scanning the `CHLClient::CreateMove` prologue. Usercmd CRC-32 is repaired after any cmd edit (`note_modified`). - Console output via `ConMsg` from `libtier0.so` (`_Z6ConMsgPKcz`), not `IVEngineClient::ClientCmd`. ## Interfaces / indices | Interface | Version | Notes | |---|---|---| | Client | `VClient017` | | | Entity list | `VClientEntityList003` | | | Engine | `VEngineClient014` | | | Model info | `VModelInfoClient006` | | | Engine server | `VEngineServer021` | edicts / listen-server entities | | Debug overlay | `VDebugOverlay003` | accurate world->screen | | Engine trace | `EngineTraceClient003` | vischeck / trigger ray | | Surface | `VGUI_Surface030` | | Relevant vtable slots (this build): - Engine: `GetLocalPlayer` 12, `Time` 14, `GetViewAngles` 19, `SetViewAngles` 20. - Entity list: `GetClientNetworkable` 0, `GetClientEntity` 3, `GetHighestEntityIndex` 6. - `IClientNetworkable`: `GetClientClass` 2. `ClientClass::m_pNetworkName` at +8. - `IClientUnknown`: `GetClientRenderable` 6. - `IClientRenderable`: `GetModel` 9, `SetupBones` 16. - `IVModelInfo`: `GetModel` 2, `GetModelName` 4, `GetStudiomodel` 29. - `IVEngineServer`: `PEntityOfEntIndex` 19. Edict `m_pUnk` at +0xC; `IServerUnknown::GetBaseEntity` 6. - `IVDebugOverlay`: `ScreenPosition` 9. - `IEngineTrace`: `TraceRay` 4. `Ray_t` uses 16-byte `VectorAligned`; `trace_t`: `fraction` 44, `startsolid` 55, `m_pEnt` 76. ## Netvars / entity data (this build) | Field | Client offset | |---|---| | `m_fFlags` | `0x33C` | | `m_vecVelocity[0]` | `0xE0` | | `m_MoveType` (prediction datamap) | `0x164` | | `m_iHealth` | `0x80` | | `m_iMaxHealth` | not networked | | `m_iTeamNum` | `0x88` | | `m_vecOrigin` | `0x324` | | `m_vecViewOffset[0]` | `0xD4` | | `m_vecMins` / `m_vecMaxs` | `0x1A8` / `0x1B4` | | `m_lifeState` | `0x7F` | | `m_nModelIndex` | `0x7C` | | `m_vecAbsVelocity` (client local, not netvar) | `0x11C` | | `m_hActiveWeapon` (client netvar; server fallback `0x824`) | resolved by name | | `m_hOwnerEntity` / `m_hOwner` (held weapons) | resolved by name | - Eye position = `m_vecOrigin + m_vecViewOffset` on **all three axes**. (The old code added the X component to Z, which left the eye ~64 units low.) - NPC health is not networked. Alive state comes from the networked client `m_lifeState`; real HP is read from the listen-server `CBaseEntity` found via `PEntityOfEntIndex -> edict->m_pUnk -> GetBaseEntity`, using offsets scanned from `server.so`'s datamap: - `m_iHealth` `0xF4`, `m_iMaxHealth` `0xF0`, `m_lifeState` `0xF8`, `m_nModelIndex` `0x7A` (short). ## Menu There is a single menu: the custom GUI, toggled with `Insert`. The old text-tree menu was removed after the GUI reached parity. Every feature still has an active flag and a bind (SDL scancode; mouse buttons 600..604; None = always-on), and all settings are persisted in the INI described under GUI vocabulary / decisions. ## GUI vocabulary / decisions - **GUI**: custom immediate-mode settings surface drawn through the existing `ISurface` paint path. No Dear ImGui, no OpenGL hook, no new runtime dependency. - **Category**: sidebar group. Movement, Combat, Visuals, Weapons, System. - **Card**: per-feature panel. Header always shows Active + Bind; body is a collapsible set of settings. - **Text layout**: measured with `ISurface::GetTextSize` and wrapped to the available width. Card headers, control rows, info rows, tooltips and toasts grow/wrap instead of truncating; there is no ellipsis truncation. The config path is shown on the System page (not duplicated in the footer). - **Freeze**: while the GUI is open, look/attack/use/impulse/weapon selection are blocked and combat features are skipped, but normal WASD/jump/duck/walk movement is allowed. Mouse deltas are discarded before `CInput::CreateMove` can apply them (`CInput::ClearStates`), so the view cannot drift for a frame. Bind capture blocks all game input until it ends. The game keeps simulating and ESP/FOV circle/speed graph keep drawing. No invulnerability. - **Native cursor**: on open, leave SDL relative-mouse mode and show the OS cursor; restore on close. Absolute mouse coordinates are used. - **Toast**: transient top-right notification drawn by the overlay even when the GUI is closed. Info/warn/error colors; lifetimes 6/9/12 s. Toasts queued before the first paint (load errors) start their timer when first drawn. Used for the load message, config problems, missing interfaces/netvars, bind changes, Reload and Reset. - **Bind capture**: click Bind, press a key or any mouse button; Esc cancels capture but does not close the GUI; the small x clears the bind (None = always-on). Insert is reserved for the GUI. - **UI scale**: `clamp(min(screen_w/1920, screen_h/1080), 0.75, 1.5)`; base window 960x620, body/title fonts 15/20 px at scale 1. - Every feature gets Active + Bind; bind None means always-on. - System page: real config path/build info, Reload config (re-reads the INI), and a two-click Reset that restores feature settings/binds but keeps GUI layout. - Window: fixed-size, draggable by title bar, clamped to screen, position, category and expanded cards persisted under `[gui]` in the same INI. Esc does not close the GUI; the toggle key (Insert) is the only close path. There is no title-bar X. - GUI controls write the shared feature state; sliders persist when the drag ends, toggles/binds/layout persist immediately. `[gui]` stores `x`, `y`, `category` and an `expanded` bitmask (card index order). Every feature now persists an `active` and `bind` pair, including movement, melee and spread. - All card contents are clipped to the scrolling content rect, including checkboxes and the expand chevron; only clipped fills/text were handled before, so cards scrolled out of view leaked header widgets outside the window. - **Colour control (`CK_COLOR`)**: row shows a live swatch; clicking the row expands an inline picker (saturation/value square + hue bar + hex/RGB preview) instead of three RGB sliders, growing the card in place so it scrolls and clips like any other row. The picker keeps HSV state so grey colours do not lose their hue; dragging writes the 0-255 RGB triple shared with the config and saves on mouse release. Only one picker is open at a time; closing the GUI or collapsing the card hides it. - Config writes are atomic: `config.tmp` is written and flushed, then `rename()`d over the real file. A failed write raises an error toast. ## NPC friendliness - ESP colours, aimbot/silent-aim/melee target filtering use the server AI relationship (`m_Relationship` CUtlVector) against the local player: entity-specific rows first, then the CLASS_PLAYER class row. D_LI is friendly, D_NU neutral, D_HT/D_FR hostile. - This makes map-scripted friendly turrets and other relationship overrides dynamic instead of model-name guesses. When no row exists, the old model heuristic is used. - `m_Relationship` is declared with `DEFINE_UTLVECTOR`, so its datamap entry is `FIELD_CUSTOM` (not `FIELD_EMBEDDED`). Resolving it as embedded silently returned `-1` for every NPC. - Floor turrets are special: citizen-built turrets (`SF_FLOOR_TURRET_CITIZEN`, spawnflags `0x200`) and Alyx-hacked turrets (`m_bHackedByAlyx`) report `CLASS_PLAYER_ALLY` from their overridden `Classify()` and never add a relationship row. They are classified from those server fields instead. - Bugbait-controlled antlions are also treated as friendly when their server `m_hFollowTarget` is set, covering antlions that spawned before the global `antlion_allied` state changed. - Stock NPCs have no `m_Relationship` rows at all: their disposition comes from the global `CHL2GameRules::InitDefaultRelationships()` table indexed by `Classify()` (e.g. `CLASS_PLAYER_ALLY -> D_LI`, `CLASS_HEADCRAB -> D_HT`). That table is not stored per entity, so `relation=-1` is normal and the model heuristic is the fallback for those NPCs (Kleiner, Combine, ...). - `m_NPCState` (`FIELD_INTEGER`, server datamap) is read as well: `NPC_STATE_SCRIPT` (4) marks scripted/passive actors as neutral, so they are not aimbot targets and draw with the neutral colour. Lamarr (`models/Lamarr.mdl`) is additionally listed as neutral by model. ## Bones / ESP - `SetupBones(BONE_USED_BY_ANYTHING)` with engine `Time()`; studio header via `GetStudiomodel`; hitboxes and bone names/parents from that header. - HL2 strips many ValveBiped bone names (the zombie's spine/legs/left arm are unnamed), so the humanoid skeleton is built from the **union of hitbox bone parent paths** (`HITGROUP_*`). Head group 1 includes the zombie headcrab body and its `HC_Head` child; fingers, toes and other `HC_*` limbs are excluded. - Non-humanoid NPCs (headcrabs, crows, antlions, ...) draw the full bone tree. - `aim_points` = head hitbox/bone + hitbox centers for the selected groups; the aim features pick the nearest visible point. ## Aim features - Shared target selection (`select_aim_target`): live hostile NPCs, skip friendly/neutral, rank by angle to the crosshair, then pick the nearest selected hitgroup point. Target candidates are tried nearest-first. - Vischeck: `EngineTraceClient003::TraceRay`, `MASK_VISIBLE`, `TRACE_WORLD_ONLY` (world only; static props are not tested — deliberate). - Aimbot: bounded smoothing — 0.05 deg deadzone, max `Smoothness` deg/tick, shortest-path yaw, pitch clamped to ±89; pushes the new view with `SetViewAngles`. - Triggerbot: crosshair ray with `MASK_SHOT` + `TRACE_EVERYTHING` (filter ignores the local player). First hit must be a live hostile NPC; sets `IN_ATTACK`. - Silent aim: exact target angle written to `cmd->viewangles`, `IN_ATTACK`, then the rendered view is restored immediately **and** at `FRAME_RENDER_START`; `forwardmove`/`sidemove` are rewritten to preserve the world-space movement wish vector. Because `IN_ATTACK` is set after `CInput::CreateMove`, the minimal-spread seed is applied again in `do_move` (same for triggerbot). Crossbow bolts and RPG rockets are led by target velocity and travel time; the crossbow also gets its 0.05 gravity drop. - ABH: on the landing/jump tick, face opposite the horizontal velocity with zero forward/side movement; HL2's jump speed-bound subtraction then accelerates along the velocity. Camera restored at `FRAME_RENDER_START`. ## Melee killbot - Active only while the local player's active weapon is the crowbar. The client `m_hActiveWeapon` handle is used first; if it is missing, the authoritative server `m_hActiveWeapon` handle (0x824) is used to get the weapon entity index. - Map transitions often switch the active weapon away from the crowbar. The state change is logged as `melee: active ... crowbar=0/1` (normal log), and a one-time toast says to equip the crowbar. If the client weapon model is missing/0, the server model index is used to recognize it. - Bone caches and aim locks are cleared when the local player entity changes (map load/respawn). - Crowbar range is `CROWBAR_RANGE` 75 units from `Weapon_ShootPosition`. The selector computes the closest point on each hostile NPC's AABB to the eye, rejects anything farther than 75, sorts by that distance, and picks the nearest visible one. - The usercmd view is pointed at the target for one command with `IN_ATTACK`; movement keys are rotated with `preserve_movement`, and the rendered view is restored immediately and at `FRAME_RENDER_START` (same silent-aim path). This gives a 360-degree silent melee attack. - While the crowbar is active and the killbot feature is on, the gun aimbot, triggerbot and silent aim are skipped to avoid fighting over the usercmd view. - Vischeck uses the shared `MASK_VISIBLE`/world-only path (static props are ignored). ## Spread control - HL2's `CBaseEntity::FireBullets` seeds its RNG from `CBaseEntity::GetPredictionRandomSeed(info.m_bUseServerRandomSeed)`; HL2 weapons leave `m_bUseServerRandomSeed` false, so the server uses the client usercmd's `random_seed` (masked to 8 bits). This is the TF2-style client-side seed hook. - `Spread control -> No spread` precomputes, with the engine's own `RandomSeed`/`RandomFloat`, the 8-bit seed whose `CShotManipulator::ApplySpread` offset is smallest. It sets `cmd->random_seed` on attack commands; `note_modified` repairs the CRC. Silent aim and triggerbot set `IN_ATTACK` after the CInput hook, so they re-apply the best seed in `do_move`. - Shotguns use consecutive seeds per pellet. The primary's first pellet is `FIRE_BULLETS_FIRST_SHOT_ACCURATE`, so the table excludes it; the secondary uses all 12. Separate best starting seeds are precomputed for 1, 8 and 12 pellets. - This attacks the random cone offset only. The cone size, the pistol's `m_flAccuracyPenalty` growth, the server view punch added to `GetAutoaimVector`, and any damage falloff are server-side state. In single-player those can be written/hooked locally; stock HL2 has no distance damage falloff. ## Jumpbug - Automatic while falling when the predicted landing speed `sqrt(vz^2 + 2*600*x)` exceeds `PLAYER_MAX_SAFE_FALL_SPEED` (526.5). - The engine only applies fall damage when `CheckFalling` sees a ground entity at the end of the frame. `PlayerMove` runs `Duck()` **before** `FullWalkMove`'s `CheckJumpButton`, so a same-frame `FinishUnDuck` sets ground via `CategorizePosition`, the jump executes, and the fall-damage test is skipped. - In air, `FinishDuck` raises the origin by 36 (standing hull 72, duck hull 36) and `CanUnduck` only succeeds when the floor is at least 36 below; after unducking, `CategorizePosition` needs it within 2 more units. The unduck window is therefore a 2-unit band `[36,38]` above the floor. - The feature presses duck when the standing distance `x` is within `drop + [0,2]` (where `drop` is the previous command's measured fall), then releases duck + `IN_JUMP` on the next command when `x` is in `[36,38]`. It also clears `IN_JUMP` while waiting so the pogo latch is clear. - `drop` is measured from consecutive predicted origins (not a hard-coded tick interval). At high fall speeds the 2-unit band can be skipped between commands; a higher frame rate improves reliability. Best-effort by design. - **Assist timing** (off by default): when the natural next command would jump over the 2-unit window, force the post-command distance to 37 by writing the vertical velocity. It needs both the authoritative server velocity and the client prediction velocity: - server `CBaseEntity::m_vecAbsVelocity` (datamap offset 0x228), read by `SetupMove` via `GetAbsVelocity`; - client `C_BaseEntity::m_vecAbsVelocity` (datamap offset 0x11C) and the networked `m_vecVelocity` (0xE0 + 8). The velocity is `dz / 0.015 + 0.5 * 600 * 0.015` where `dz` is the required displacement for this command. This turns the ~20% timing success at 625 ups into a repeatable setup, at the cost of one command with a nudged vertical speed. Pure jumpbug remains available with Assist off. - The server datamap has two `m_vecVelocity` entries (0x1C and 0x29C), so the assist deliberately writes only `m_vecAbsVelocity`; `SetAbsVelocity` syncs `m_vecVelocity` after the command. ## World transformations ### Fullbright - Visuals card with the usual Active + Bind. Unbound and Active means always on; a bind makes it a hold-to-brighten key. - Applies the client `mat_fullbright` cvar to 1/0 through `IVEngineClient::ClientCmd` (slot 7), which bypasses the console cheat gate and avoids the crashing direct `ConVar` vtable path. No server-side writes. - Re-applied after map loads/respawns (`fullbright_invalidate` on local entity change) and saved as `[fullbright] active/bind`. - `fullbright: on/off` is logged; a missing cvar interface raises an error toast instead of failing silently. ### World tint (material colour modulation) - Same idea as Amalgam's world modulation: find `IMaterialSystem` in the already-loaded `materialsystem.so` and walk every material with `FirstMaterial` / `NextMaterial` / `GetMaterial`. - `IMaterial::ColorModulate(r,g,b)` is applied to materials whose `GetTextureGroupName()` is `World textures` (sky names excluded), to `SkyBox textures` (or names containing `sky`), and to prop materials whose name starts with `models/props` (static props and prop_physics share those model materials; NPC/player/weapon materials are excluded). Lightmaps are not touched. - Slot map (2013 SDK layout, registered under `VMaterialSystem080`): `IMaterialSystem` 73/74/75/76/77 = First/Next/Invalid/Get/GetNumMaterials; `IMaterial` 0/1/28 = GetName/GetTextureGroupName/ColorModulate. - `src/world.cpp`; the walk runs only when the enabled groups or colours change, and re-applies after level changes via `world_tint_invalidate()`. Disabling restores white, so no reload is needed. - Validation before any walk: `GetNumMaterials()` must return 1..200000; the first material's name/group must be printable strings. A mismatch logs and toasts instead of poking garbage. - Saved as `[worldtint] active/bind/world/sky/prop/wr..pb`. - Particle tint is deliberately not implemented: HL2's old particle manager draws per-vertex coloured IMesh quads, and SpriteCard/TF2-style particles take colour from per-particle vertex data, so `IMaterial::ColorModulate` does nothing for them. It needs a hook in the client particle render path. ### Fog - `src/world.cpp` `fog_update()`, driven by the `fog_override` engine command family through `IVEngineClient::ClientCmd` (same safe path as fullbright). No render hooks. - Enabled: `fog_override 1`, `fog_enable 1`, `fog_color`, `fog_start`, `fog_end`, plus `fog_enableskybox` / `fog_colorskybox` / `fog_startskybox` / `fog_endskybox` when skybox fog is on. "Remove fog" issues `fog_enable 0` instead. - Disabled: `fog_override 0` restores the map's own fog controller. - End is clamped above Start. Re-applied on level change via `fog_invalidate()`. Saved as `[fog] active/bind/r/g/b/start/end/skybox/disable`. ### Skybox image - Replaces the map skybox with `/skybox.png` (same directory as the INI). Not `sv_skyname`: that did not take effect at runtime in this build, so the skybox card now works on materials instead. - The PNG is decoded by our own reader (`zlib`'s `uncompress` + PNG filters; 8-bit non-interlaced RGB/RGBA/gray/palette). Do **not** use the system libpng: `client.so`, `engine.so` and `vguimatsurface.so` each export their own `png_create_read_struct`, so libpng16's internal PLT calls bind to the game's older libpng with a different `png_struct` layout and crash in `png_safe_warning` (error_ptr at +0xb0 reads NULL). - This build's `IMaterialSystem` interface ends at `GetDisplayDeviceName` (the slots after it belong to `CMaterialSystem` itself), so there is **no `CreateTextureFromBits`**. The pixels are uploaded through `CreateProceduralTexture` (vtable 81) + an `ITextureRegenerator` (`SetTextureRegenerator` 12, `Download` 13), which fills mip 0 through `IVTFTexture::ImageData(frame,face,mip)` (39). `IVTFTexture`'s virtual destructor occupies two slots, so Width=15, Height=16, RowSizeInBytes=19. - The texture is assigned with `IMaterialVar::SetTextureValue` to `$basetexture` and `$hdrbasetexture` on every material in the `SkyBox textures` group or named `skybox/*`. The same image maps onto all six faces. - HDR sky materials use the `sky` shader, whose texture binding happens in `SHADER_INIT`: it samples `$hdrcompressedtexture` (DXT5) when defined and otherwise `$hdrbasetexture`. Putting an RGBA8 texture into the DXT5 slot renders black, so `$hdrcompressedtexture` (+ its `0/1/2` variants) is `SetUndefined()` and `$hdrbasetexture` is pointed at our texture instead. The shader state is then rebuilt with `IMaterial::Refresh()` (slot 37), otherwise the old bindings stay cached. - Original textures are recorded per material/var (texture pointer plus the string as a fallback) and restored when the feature is disabled. Records survive level changes so a reused sky material can still be restored; the override is re-applied to the new map automatically. - The card shows the image path and has a Reload image button. A missing or unreadable PNG raises an error toast; the image is only re-read on Reload. - Saved as `[skybox] active/bind`. ## Logging - `/tmp/hl2hake.log` is opened once at startup with `"w"` (so it only contains the current session), line-buffered, and closed via `atexit`. - `logf` is always on: lifecycle, warnings/errors, config, GUI open/close and item-crate detections. - `logd` is off unless `HL2H_DEBUG=1`; it carries per-scan ESP/NPC/item-prop, aimbot-target, bone and netvar noise. Run `HL2H_DEBUG=1 ./play.sh` to enable. ## Gotchas / lessons - Never pipe-mask build failures: `./build.sh > /tmp/build.log 2>&1; echo rc=$?`. - `CHLClient` slot numbers: `CreateMove` 21, `FrameStageNotify` 35. - Networked EHANDLE values use an 11-bit entity index in the low bits; bits 11+ are the serial. Mask with `0x7FF`, not the local `CBaseHandle` 16-bit mask. This caused active-weapon lookups to produce bogus indices after a map change once the weapon serial was nonzero (melee killbot looked dead). - `ISurface` binary slot = SDK header slot + 4; see `src/ISURFACE_STATUS.md`. - GUI font handles are validated with `GetTextSize` and recreated when the local player entity or UI scale changes; if the scaled size fails, 16 px is used as a fallback. VGUI can silently invalidate font handles across level transitions. - Silent aim and ABH temporarily replace the usercmd view; silent aim preserves movement, ABH intentionally zeroes it for the jump tick. - Aimbot/silent/melee vischeck now traces everything bullets care about (`MASK_SHOT`) while ignoring the local player and target. Static props, crates and other NPCs block. Non-solid glass and breakable-glass materials are skipped; solid non-break glass (bulletproof) blocks. Triggerbot still uses the same `MASK_SHOT` trace and will not fire through glass. ## Valuable items - Visuals feature; Active is on by default and unbound (always-on). - Scans the client entity list every 100 ms and tags health, ammo, armor/suit, world weapon pickups, the two supply-crate models and generic items. - Each tag is a colored marker with a name and optional distance, filtered by category toggles and a max-distance slider (default 5000 units; 0 means unlimited and displays as "All"). Drawn through walls and independently of the hostile NPC ESP. - Ammo labels are specific where the model is known: pistol, SMG, AR2, .357, crossbow, shotgun, sniper, flares, AR2 grenade, RPG rocket, plus ammo-crate variants. Unknown ammo falls back to "Ammo". - Weapons are world pickups only: the active weapon and anything with a valid `m_hOwner`/`m_hOwnerEntity` handle is skipped. - Only `item_item_crate` and `item_beacon_crate` are tagged as supply crates. Other breakable crates are intentionally ignored; crate chunk debris is skipped. - Client `CPhysicsProp` entities often have `m_nModelIndex == 0`, including item crates. The item scan falls back to the server entity's model index (`server_model_index`) to recover the model name. ## Roadmap / TODO - **Server-side spread/recoil control**: not planned; recoil and accuracy penalties are intentionally left stock. - **Distance weapon spread thesis**: addressed by Spread control; stock HL2 hitscan damage is range-independent, so distance efficiency is the cone.