Skip to content

title: "Opis CLI" description: "Sterowanie Mercy SF z innego programu: każde polecenie, jego wyjście JSON, kody wyjścia i rozpisane przykłady." ​

Opis CLI, sterowanie Mercy SF z innego programu ​

mercy-cli ma tryb nieinteraktywny: argumenty do środka, jeden obiekt JSON na zewnątrz. Jest przeznaczony dla paneli, nadzorców i skryptów.

Ta strona jest obietnicą. Polecenia i nazwy pól poniżej nie zmienią się bez uprzedzenia. Cała reszta CLI, menu, jego sformułowania, układ wyjścia, może się zmienić w każdej chwili, więc proszę, nie buduj na tym.

Uruchomione bez argumentów, startuje interaktywne menu dokładnie jak wcześniej.

Każda odpowiedź niesie "api". Dziś to 1. Dodanie pola tego nie zmienia, wywołujący, który czyta tylko znane sobie pola, pozostaje nietknięty. Zmienia się, gdy pole zostaje usunięte albo zmienia się jego znaczenie, więc możesz odmówić działania wobec kształtu, pod który nie byłeś budowany.


Logowanie ​

Konta założone przez pojedyncze logowanie S&F nie potrzebują niczego więcej. Konto należące do jednego serwera, które nigdy nie było częścią pojedynczego logowania, z jego punktu widzenia nie istnieje i wymaga --server:

bash
mercy-cli --status --user <account> --server https://f1.sfgame.net/

--server działa z każdym poleceniem poniżej.

Wyjście ​

Każde polecenie oprócz --start wypisuje JSON i nic więcej, więc --json i tak jest wartością domyślną, a podanie go nic nie zmienia. Istnieje dla wywołujących, którzy wolą to powiedzieć, niż polegać na domyślnym zachowaniu, oraz dla --start, które w przeciwnym razie pisze dziennik dla człowieka.


Każdy panel, który rysuje aplikacja ​

bash
mercy-cli --views
mercy-cli --view <name> --user <account> --character <name>

--views wypisuje katalog i nie potrzebuje ani konta, ani hasła. --view odpowiada na jeden z nich dla jednej postaci.

Każda nazwa prowadzi dokładnie do tej funkcji, którą rysuje aplikacja. O to tu chodzi: liczba policzona drugi raz prędzej czy później zostanie policzona inaczej, a wtedy dwa okna tej samej instalacji pokazują co innego.

nazwana co odpowiada
account_overviewwiersz tej postaci w tabeli kont
achievementsosiągnięcia i co wybrałby silnik
analyticszapisana historia tej postaci
battle_historyzachowane walki, najnowsze pierwsze
blacksmithmetal, arkana i ile rozbiórek zostało dziś
combat_decisionco ostatnio zdecydował moduł walki i dlaczego
dungeonskażdy loch z poziomem i postępem
fight_calibrationwyuczony model walki dla tej postaci
gearile wart jest każdy slot wobec zawartości torby
hellevatorhellevator: piętro, zdrowie, następny ruch
legendary_dungeonpanel legendarnego lochu, razem ze zdrowiem
live_eventsktóre eventy trwają i ich okna
scrapbook_cacheilu zwiadowanych przeciwników jest w pamięci
scrapbook_targetsktórych brakujących przedmiotów warto szukać
world_bossworld boss i jego sklep

Odpowiedź to zawsze {"ok": true, "view": "<nazwa>", "data": …}. Pięć z nich czyta z dysku i odpowiada, nie dotykając gry.

Odczyt ​

Lista postaci na koncie ​

bash
mercy-cli --characters --user <account>
json
{
  "ok": true,
  "characters": [
    { "character": "Hero", "server": "https://s1.sfgame.net/", "level": 345 },
    { "character": "Alt",  "server": "https://s42.sfgame.net/", "level": 88 }
  ]
}

Jedno logowanie obejmuje każdą postać na koncie, na wszystkich serwerach. level jest null, jeśli stanu tej postaci nie udało się pobrać; wpis i tak jest wypisany, bo świadomość, że postać istnieje, jest sensem tego wywołania.

Odczyt stanu jednej postaci ​

bash
mercy-cli --status --user <account> --character <name>

--character można pominąć, gdy konto ma dokładnie jedną.

json
{
  "ok": true,
  "character": "Hero",
  "server": "https://s1.sfgame.net/",
  "level": 345,
  "class": "DemonHunter",
  "race": "DarkElf",
  "experience": { "current": 740300000, "next_level": 769800000 },
  "silver": 725000000,
  "mushrooms": 8,
  "honor": 12043,
  "rank": 1800,
  "action": "CityGuard",
  "attributes": {
    "strength":     { "base": 4077, "bonus": 1200 },
    "dexterity":    { "base": 1000, "bonus": 300 },
    "intelligence": { "base": 900,  "bonus": 250 },
    "constitution": { "base": 3000, "bonus": 800 },
    "luck":         { "base": 700,  "bonus": 150 }
  },
  "equipment": [
    {
      "slot": "Weapon",
      "model_id": 55,
      "color": 1,
      "class": "DemonHunter",
      "epic": true,
      "legendary": false,
      "armor": 0,
      "min_damage": 900,
      "max_damage": 1400,
      "upgrades": 3,
      "attributes": { "strength": 420, "constitution": 210 },
      "rune": { "type": "FireDamage", "value": 60 },
      "enchantment": "SwordOfVengeance",
      "gem": { "type": "Strength", "value": 300 }
    }
  ],
  "arena": {
    "fights_for_xp": 7,
    "next_free_fight": "2026-08-06T12:04:11+02:00",
    "opponents": [50099, 34316, 13668]
  },
  "tavern": {
    "thirst_for_adventure_sec": 3600,
    "beer_drunk": 2,
    "mushroom_skip_allowed": true,
    "quests": [
      { "length_sec": 600, "silver": 120000, "experience": 45000,
        "location": "Wolves", "item": true }
    ]
  },
  "guild": {
    "name": "Blvck Clover",
    "rank": 12,
    "honor": 90000,
    "members": [
      { "name": "Hero", "level": 345, "last_online": "2026-08-06T09:12:00+02:00" }
    ]
  },
  "scrapbook_items": 1102
}

Wszystko pochodzi z jednego pobrania stanu, więc nie ma sekcji do zamawiania ani niczego do zaoszczędzenia przez proszenie o mniej.

guild jest null, gdy postać do żadnej nie należy. scrapbook_items jest null, dopóki album nie zostanie odczytany choć raz. Puste miejsce na ekwipunek jest pomijane, a nie wypisywane jako null.

O nazwach przedmiotów: protokół niesie identyfikator modelu i slot, a nie nazwę wyświetlaną, więc to właśnie dostajesz. To także sposób, w jaki adresowana jest grafika, więc panel może narysować dokładnie ten obrazek, który rysuje aplikacja na pulpicie. Wymyślanie tu nazwy byłoby zgadywanką, której nie dałoby się sprawdzić.

Powtarzanie odczytu w odstępach ​

bash
mercy-cli --status --user <account> --character <name> --watch 60

Jedno logowanie, a potem jeden obiekt co 60 sekund, dopóki proces nie zostanie zatrzymany. Używaj tego zamiast uruchamiać polecenie w pętli: każde uruchomienie to świeże logowanie, a to najdroższy sposób obserwowania liczby i najbardziej rzucający się w oczy od strony serwera. Dolna granica to 30 sekund. Nieudana runda jest wypisywana i pętla leci dalej; obserwator, który poddaje się przy pierwszej czkawce, wygląda dokładnie tak samo jak taki, u którego nic się nie dzieje.

Z kim będzie walczył następnie i dlaczego ​

bash
mercy-cli --plan --user <account> --character <name>
json
{
  "ok": true,
  "outcome": "planned",
  "reason": "Next: Someone — 3 scrapbook items, 72% odds",
  "candidates": [
    {
      "name": "Someone",
      "level": 340,
      "source": "pool",
      "missing_items": 3,
      "rank_gain": 12,
      "favourite": false,
      "win_chance": 0.72,
      "item_score": 300.0,
      "xp_score": 60.0,
      "rank_score": 25.0,
      "base": 385.0,
      "score": 277.2
    }
  ]
}

Ta sama decyzja, którą rysuje aplikacja: kandydaci oceniani po przedmiotach, dziennym PD i miejscu w rankingu, a potem mnożeni przez szanse. outcome to planned, waiting, blocked albo empty, a reason mówi jednym zdaniem który.

To nie wydaje żadnej walki ani grzyba. Przeczesuje jednak w poszukiwaniu kandydatów, więc nie jest darmowe w zapytaniach; nie wsadzaj tego w ciasną pętlę.

Próba walki ​

bash
mercy-cli --simulate --user <account> --character <name> --against <player>
json
{ "ok": true, "against": "Someone", "against_level": 340, "win_chance": 0.63, "iterations": 1000 }

Arytmetyka. Jedno zapytanie idzie do serwera, żeby ustalić, kto to jest; sama walka nigdy nie jest wysyłana. win_chance to wygrane wobec walk rozstrzygniętych, więc symulacja, która nie potrafi rozstrzygnąć, raportuje z mniejszej liczby, niż przeprowadziła.

Co jest noszone ​

bash
mercy-cli --inventory --user <account> --character <name>
json
{ "ok": true, "carried": 12, "backpack": [ { "slot_index": 0, "model_id": 55 } ] }

Jedna lista, bo gra trzyma jedną. Przedmioty mają ten sam kształt co w --status, plus slot_index.

Album ​

bash
mercy-cli --scrapbook --user <account> --character <name>
json
{ "ok": true, "unlocked": true, "owned": 1102, "monsters": 214 }

unlocked jest false poniżej poziomu, który daje album, a owned jest tam null. To nie jest błąd.

Zapisane walki ​

bash
mercy-cli --history --user <account> --character <name> --limit 50
json
{ "ok": true, "total": 4210, "returned": 50, "battles": [] }

Najnowsze na początku. Zapisuje je ta instalacja, która prowadziła bota, nie są pobierane z serwera. Proces, który dopiero wystartował, nie ma nic do pokazania i nie jest to usterka. total to to, co istnieje, returned to to, co zostawił --limit.


Działanie ​

Uruchomienie bota ​

bash
mercy-cli --start --user <account> --character <name>

Chodzi, dopóki proces nie zostanie zatrzymany, zapisując przy tym dziennik. --character jest tu wymagane: wypisywanie listy i czytanie stanu są nieszkodliwe, ale uruchomienie bota gra w grę, a wybranie złej postaci nie jest czymś, co da się cofnąć.

--all uruchamia zamiast tego każdą postać konta w tym jednym procesie. To jedno logowanie na konto zamiast jednego na postać, co jest tańsze i powstrzymuje postaci przed wzajemnym unieważnianiem sesji.

Przy Ctrl-C (albo SIGTERM) bieżący ruch może się dokończyć, zanim proces wyjdzie, zamiast być ucięty w środku zapytania.

Zdarzenia zamiast prozy ​

bash
mercy-cli --start --user <account> --character <name> --events

Jeden obiekt JSON na linię, więc nadzorca czyta linia po linii:

json
{"at":"2026-08-06T17:22:04+02:00","api":1,"event":"arena_win","character":"Hero"}
{"at":"2026-08-06T17:22:31+02:00","api":1,"event":"quest_done","character":"Hero"}

event to jedno z started, stopping, stopped, arena_win, arena_loss, dungeon_win, dungeon_loss, quest_done, scrapbook_win, scrapbook_loss, scrapbook_items (które niesie count).

Ta lista będzie rosła. Traktuj nierozpoznane zdarzenie jako takie do zignorowania, a nie jako błąd; to cały powód, dla którego nazwy są wypisane, a nie są tym, co akurat wypluł Rust.

Używaj tego zamiast czytać dziennik dla ludzi. Tamten dziennik pisany jest dla człowieka i może zostać przeredagowany w każdej chwili; parsowanie go znaczy, że twoje narzędzie psuje się, gdy ktoś poprawi zdanie.

Odbiór tego, co czeka ​

bash
mercy-cli --claim --user <account> --character <name>

Kalendarz, zadania dzienne, oczekujące odblokowania. --character jest wymagane, z tego samego powodu co przy --start.

Odczyt lub zmiana konfiguracji ​

bash
mercy-cli --config --user <account> --character <name>
mercy-cli --config --user <account> --character <name> --set auto_arena=false
json
{ "ok": true, "settable": ["auto_arena"], "changed": [ { "key": "auto_arena", "from": true, "to": false } ] }

Odczyt zwraca całą konfigurację pod config. Nazwy pól wewnątrz tego bloku nie są częścią tej obietnicy, są nią wyłącznie klucze wypisane w settable. Konfiguracja ma znacznie ponad sto pól i rośnie z każdym wydaniem; obiecanie ich wszystkich znaczyłoby, że nigdy więcej nie da się przebudować ustawień.

--set przyjmuje true/false, on/off, yes/no, 1/0, zawsze wymaga --character (przełącznik należy do jednej postaci, a „ta jedyna” to zgadywanka, po której czytać można, a pisać nie), i można je powtarzać. Wartość jest potem odczytywana z dysku, a wywołanie kończy się niepowodzeniem, jeśli nie wylądowała, bo zgłoszenie zmiany, która się nie wydarzyła, to jedyny wynik, po którym nie da się pozbierać.


Eksploatacja ​

Co to za kompilacja ​

bash
mercy-cli --version
json
{ "ok": true, "version": "2.12.0", "api": 1 }

Jedyne polecenie, które nie potrzebuje ani hasła, ani sieci. Nadzorca musi móc zapytać, z czym rozmawia, zanim będzie miał dane do tej rozmowy.

Czy jest osiągalne ​

bash
mercy-cli --health --user <account> --character <name>
json
{ "ok": true, "character": "Hero", "level": 345, "round_trip_ms": 412 }

Zaloguj, pobierz, zgłoś, wyjdź. Celowo maleńkie: kontrola zdrowia zwracająca pełną kartę postaci zachęca do jej parsowania, a wtedy sama kontrola staje się kolejnym interfejsem.


Hasło ​

Nie ma flagi --password i jest to celowe. Argumenty wiersza poleceń są czytelne dla każdego innego procesu na maszynie, ps, /proc, Menedżer zadań, więc panel uruchamiający to dla kilku osób oddawałby wszystkie ich hasła do gry każdemu, kto ma powłokę na tej maszynie.

Zamiast tego dwie drogi:

bash
MERCY_SF_PASSWORD='…' mercy-cli --status --user someone
bash
printf '%s' "$password" | mercy-cli --status --user someone --password-stdin

--password-stdin czyta pierwszą linię standardowego wejścia, co trzyma sekret także poza środowiskiem.


Kody wyjścia ​

KodZnaczenie
0Sukces, a także dla --help
1Ruszyło i zawiodło, powód jest w error, a ok to false
2Wywołanie było błędne: nieznana opcja, brak wartości, brak akcji. Na stderr

Różnica między 1 a 2 ma znaczenie dla nadzorcy: 2 zawiedzie za każdym razem tak samo i potrzebuje człowieka, 1 może całkiem dobrze zadziałać przy kolejnej próbie.


Błędy ​

Niepowodzenia to też JSON, więc jeden parser obsługuje jedno i drugie:

json
{ "ok": false, "error": "no character called 'Typo' on this account" }

ok obecne jest w każdej odpowiedzi. Sprawdź je przed wszystkim innym.

Częste przyczyny:

KomunikatCo zrobić
--user is requiredBrakuje --user (każda akcja poza --version)
--start needs --character (or --all)Nazwij cel; nie zostanie zgadnięty
this account has N characters — name one with --characterJest ich kilka, wskaż jedną
no character called '…' on this accountLiterówka; --characters wypisuje poprawne nazwy
no password: set MERCY_SF_PASSWORD or pass --password-stdinWybierz jedną z dwóch dróg podania hasła
login failed: …Sprawdź dane logowania albo serwer; konta spoza pojedynczego logowania wymagają --server
'…' is not settable from hereNie ma tego na liście dozwolonych; --config bez --set wypisuje settable
'…' did not save — it still reads back unchangedZmiana nie wylądowała; sprawdź uprawnienia do zapisu

Raspberry Pi i inne małe płytki ​

Aplikacja na komputer nie jest budowana pod ARM. WebKitGTK na ARM to dokładnie to miejsce, w którym posiadacz Pi zgłosił awarię zaraz po zalogowaniu, a wydanie builda, którego nikt tutaj nie potrafi przetestować, byłoby obietnicą, której ten projekt nie utrzyma. CLI nie ma interfejsu, więc nic z tego go nie dotyczy, i jest budowane pod ARM64 przy każdym wydaniu.

Najpierw sprawdź, czy płytka pracuje na systemie 64-bitowym:

bash
uname -m   # aarch64 to ten właściwy, armv7l oznacza system 32-bitowy

Dla 32-bitowego Raspberry Pi OS nie ma builda. Jedyne przejście to reinstalacja obrazem 64-bitowym: to inne cele i nic się nie konwertuje.

Potem weź mercy-cli-linux-arm64 ze strony pobierania:

bash
chmod +x mercy-cli-linux-arm64
sudo mv mercy-cli-linux-arm64 /usr/local/bin/mercy-cli
mercy-cli --version

Cała reszta tej strony działa stamtąd tak samo. Pi 4 spokojnie uciągnie kilka postaci, a Pi Zero 2 W poradzi sobie z jedną. mercy-node-linux-arm64 leży na tej samej stronie i jest robotnikiem floty, a nie tym, od czego się zaczyna.

Ustaw to jako usługę według przepisu systemd poniżej i włącz przez systemctl enable, wtedy wróci samo po restarcie płytki.

Przepisy ​

Odbieranie na każdym koncie raz dziennie

bash
for account in alpha beta gamma; do
  MERCY_SF_PASSWORD="$(pass mercy/$account)" \
    mercy-cli --claim --user "$account" --character Hero || echo "failed: $account"
done

Utrzymywanie kafelka panelu na bieżąco

bash
MERCY_SF_PASSWORD='…' mercy-cli --status --user someone --character Hero --watch 60

Jedno logowanie, jeden obiekt na minutę. Nie uruchamiaj tego zamiast tego z crona co minutę; to za każdym razem świeże logowanie.

Zapis zdarzeń do pliku

bash
MERCY_SF_PASSWORD='…' mercy-cli --start --user someone --character Hero --events \
  >> events.ndjson

Sprawdzenie, z czym rozmawiasz, przed zalogowaniem

bash
api=$(mercy-cli --version | jq -r .api)
[ "$api" = "1" ] || { echo "unknown contract version: $api"; exit 1; }

Jako usługa systemd

ini
[Service]
Environment=MERCY_SF_PASSWORD=…
ExecStart=/usr/local/bin/mercy-cli --start --user someone --all --events
Restart=on-failure
KillSignal=SIGTERM

SIGTERM pozwala dokończyć bieżący ruch. Proszę, nie rób mu SIGKILL.


Czego to jeszcze nie robi ​

Uruchamiania kilku postaci i zatrzymywania ich pojedynczo (--all uruchamia wszystkie i zatrzymuje wszystkie naraz), kanału sterującego do działającego procesu oraz zmieniania czegokolwiek poza przełącznikami modułów.

Jeśli potrzebujesz którejś z tych rzeczy, powiedz której; łatwiej zbudować właściwą rzecz niż zgadywać i patrzeć, jak leży nieużywana.

Mercy SF nie jest powiązany z Shakes & Fidget ani Playa Games. Korzystasz na własne ryzyko.