Old School Ops

Client Scripting

Writing Lua macros for the GoMMO client, and deciding whether your server accepts them.

Every game · updated 2026-11-06
On this page

The GoMMO client runs Lua scripts. A script drives your character through the same actions you drive it through: walk, fight, pick things up, wear things, cast. It works with a window open and it works with no window at all.

Two people read this page. If you play, the first half is how to write and run one. If you run a server, Refusing scripted clients is the setting you want.

Where scripts live

Beside the client executable, in scripts/:

GoMMO_Client.exe
scripts/
  default/        our scripts. Do not edit these; copy one instead
  farm.lua        yours. Any name, any subfolder

The folder ships with GoMMO Client. Move the executable and move the folder with it: without scripts/default/connect.lua the client cannot log in at all, because logging in is itself one of those scripts.

Your own scripts go anywhere under scripts/ except default/. That folder is replaced when you update the client.

Running one

GoMMO_Client.exe -script farm
GoMMO_Client.exe -list-scripts
GoMMO_Client.exe -verbs
GoMMO_Client.exe -verb inventory

-script takes a path under scripts/, with or without the .lua. -list-scripts shows what is on disk, ours and yours, with a one line summary of each. -verbs prints everything a script can call, generated from the client itself, so it matches the build you are holding. Neither listing needs a server.

With no -script, a windowed client plays normally and a -headless run executes scripts/default/main.lua.

In the window

While a script runs, it drives your character and your movement and combat keys do nothing. The camera, the zoom, the overlay and the character panels stay yours. A banner in the top right names the script and the key that stops it.

PAUSE stops a running script immediately, wherever it has got to, and the character stops walking with it. Ctrl+Break does the same. Closing the window also stops it.

Writing one

A script owns the whole run, including logging in:

run("default/connect")

local lib = run("default/lib")

lib.equip_best()

local target = lib.nearest_monster()
if target then
  local r = lib.fight(target, 15)
  log(string.format("%d hits for %d damage", r.hits, r.damage))
  lib.loot_nearby()
end

run("default/connect") is the first line of any script that is the whole run. Without it every verb below it finds an empty world.

In the windowed client, leave that line out. The client has already logged you in by the time your script starts, and connect() refuses with a message naming the line to delete.

The two layers

default/lib is the streamlined layer, written in Lua and readable. It hides the traps: re-reading a target that moves, the attack cadence, draining combat events, walking at your character's own speed. Use it unless you need something it does not do.

FunctionDoes
lib.walk_to(target, within, limit)Close the distance and stop
lib.fight(target, seconds)Fight for that long; returns swings, hits, damage, kills
lib.nearest_monster()The closest living thing you may attack
lib.equip_best()Wear the first thing in the bag
lib.loot_nearby()Walk to the nearest drop and take it
lib.alive(e), lib.current(e)Is it alive, and where is it now

Under that is the raw layer, one verb per action: me, entities, nearest, dist, move_to, step_toward, stop, attack, use_skill, vitals, respawn, skills, combat_events, inventory, equipment, equip, unequip, use_item, pick_up, sleep, log, run.

Two of those are easy to confuse. move_to sends one movement request and does not walk anywhere; calling it in a loop restarts the same move from a position the server already has, and the character shuffles. step_toward is the one that travels: call it in a loop with a sleep, and call stop when you arrive.

Walls

GoMMO Client 0.2.1 and later. RF Online.

The RF server does not stop a character walking through a wall; the original client steers round walls before it moves. GoMMO Client now does the same from the map's walls in your RF install:

  • step_toward stops short of a wall instead of passing through it;
  • path_to(x, y) returns the waypoints round the walls to a point, or nil and a reason when there is no way round;
  • lib.walk_to follows that path, and plans again when the target moves or the character stops making progress.

A path stays on the floor the character stands on: it does not plan up stairs or ramps to another level. Without an RF install the client has no walls, and walking goes in a straight line as before.

In RF Online, effects(), casting(), pets(), vehicles() and the pet_ and vehicle_ verbs (0.2.1 and later) are listed by -verbs; the Automation API describes what each returns. The same page covers the guild, election, chip war and module verbs.

Asking what the game can do

Ask what a game supports rather than which game it is, and your script survives meeting a new one:

if has_combat() then ... end
if can_set_stance() then set_stance(true) end

has_combat, has_inventory, has_guilds, has_elections, has_chip_war, has_race_admin, has_modules, can_select_target, can_set_stance and equip_slots answer for the game you are connected to. A verb the game cannot do refuses by name rather than doing nothing quietly. game_name() exists, and comparing against it is the thing that goes stale.

Copying a default

Copy the file, then say what you copied in the header:

-- @script  mycombat
-- @version 1
-- @basedon default/combat v4

The client then tells you, once per run, when the original moves past the version you copied. It says so and does nothing else: no merge, no rewrite, no blocked run. -list-scripts marks the same rows.

A script that CALLS a default with run("default/combat") never goes stale, so it needs no @basedon. That is the reason to extend one rather than fork it.

Checks

check(name, function() ... end) records a named result, and pass, fail and skip are the verdicts. A script that never calls check produces no report and no exit code, which is what a macro should do. A script that does can be run from a shell script and its exit code means something.

Refusing scripted clients

A client that a script is driving says so when it logs in. ALLOW_SCRIPTED_CLIENTS in your settings.ini decides what happens next:

ALLOW_SCRIPTED_CLIENTS = 1

It ships set to 1. Set it to 0 and a client that declared a script is refused with a message telling the player to start without one. GTH servers read it under [loginserver], RF servers under [gameserver].

This is a policy, not a protection. The server believes what a client tells it about itself. A modified client that simply says nothing is accepted whatever you set here, and the server has no way to tell the difference. Every client written before the declaration existed says nothing and is allowed either way. It lets you state a rule and hold it against clients that play fair. It is not anti-cheat, it will not stop anybody determined, and setting it to 0 does not mean nobody is scripting on your server.

Something here wrong?

Out of date, incomplete, or assuming something it should not. Documentation errors are bugs, and this one arrives tagged and pointed at this page.

Top xp sources

How xp works
Loading…

Open the full search page