Skip to content

Managing the Library

The four emote types

emote_type is the playback router. The server accepts only these four; anything else is coerced to anim on save.

TypeWhat it doesdictname
animA normal animation, played with TaskPlayAnimThe animation dictionaryThe clip name
faceSame, but with upper-body and keep-control flags, so the player can keep movingThe dictionaryThe clip
walkApplies an MP_Style_* walk style, persisted per playerUnused — the pack stores the literal walkstyleThe MP_Style_* string
scenarioStarts a WORLD_HUMAN_* world scenario in placeUnused — stores ''The scenario name, hashed client-side

Two more kinds exist at runtime — prop and shared — but they never exist in the database. They come from shared/extras.lua. See Props & shared emotes.

face versus the upper_body column

They do the same thing by two routes. emote_type = 'face' and upper_body = 1 both OR the animation flags with 48 — bit 16 (upper body) plus bit 32 (keep player control).

Use face for things that are conceptually expressions and belong in the Moods tab. Use upper_body on an anim row when you want a normal animation the player can walk during.

walk rows behave differently on purpose

  • A walk style is not cleared by X, the Stop chip, /e stop or exports.rm_emotes:Stop().
  • It is re-applied a few seconds after resource start and after character load.
  • Id 1000, name = 'default', is the only way back to a normal walk. The client special-cases that value: rather than applying a style it removes the active one and deletes the stored preference. Do not delete that row.
  • Walk styles are the one emote kind that works while mounted.
  • Random never picks one.

Categories and tabs

category decides which tab a card lands in. It is independent of emote_type.

CategoryTab
actionActions
danceDances
positionPoses
sitSeated
walkWalks
faceMoods
other, or anything unrecognisedMisc

The column is varchar(55) and any string is accepted — the menu simply folds what it does not recognise into Misc. The editor's New emote form defaults to action; other is only the server-side fallback for a save that carries no category at all, and the column's own SQL default is NULL.

The shipped seeds use action 89, other 46, dance 34, position 34, sit 3 and photo 2. Note photo exists nowhere else in the product, so those two cards land in Misc.

Three ways to add an emote

1. The in-game editor

The fastest route, and the only one that takes effect immediately. Admin tab → New emote. Full field reference in the Admin guide.

The row gets the next AUTO_INCREMENT id, the server re-reads the table, re-derives every /e slug, and pushes to everyone.

2. SQL

Useful for bulk work — importing a themed pack, or adding fifty rows at once.

sql
INSERT INTO `rm_emotes`
  (dict, name, custom_name, duration, category, is_loop, emote_type, upper_body)
VALUES
  ('amb_wander@code_human_smoking_wander@male_a@base', 'base',
   'Smoke While Walking', -1, 'action', 1, 'anim', 1);

Then rebuild the cache

The server serves every client from an in-memory copy. Your insert is invisible until you restart the resource or call exports.rm_emotes:Rebuild().

Omit id and let AUTO_INCREMENT assign it. Omit command unless you want a specific slug.

3. shared/extras.lua

Only for prop emotes and two-player emotes, which carry nested data the flat schema cannot hold.

Finding dictionaries and clip names

The shipped sql/amount.sql pack was generated from the community rdr3_discoveries in-game animation listing, and the scenario names in sql/scenarios.sql were checked against the same project's scenario data.

That listing is the practical reference for finding a dict and name pair. Those are name lists, not game data — no Rockstar asset is included in or redistributed by this resource, and nothing is streamed, so installing it does not increase your server's download size.

Keeping a large library usable

With every pack imported the library is 4,359 rows. That is a lot to put in front of a player.

The menu renders at most 400 rows per tab and shows +N more — use search to narrow past that. So the practical question is not whether players can reach an emote, but whether they can find one.

Hide rather than delete. Hiding is reversible and does not touch favourites; deleting cascades and destroys them.

sql
-- tame the bulk pack's noisiest category, keep the rest
UPDATE `rm_emotes` SET hidden = 1 WHERE id >= 5000 AND category = 'other';

-- shelve the entire bulk pack; re-enable with one statement
UPDATE `rm_emotes` SET hidden = 1 WHERE id BETWEEN 5000 AND 9132;

Let usage tell you what to keep. The most useful number available is what players actually save:

sql
SELECT e.id, e.custom_name, e.category, COUNT(*) AS saves
FROM `rm_emote_favs` f
JOIN `rm_emotes` e ON e.id = f.emote_id
GROUP BY e.id, e.custom_name, e.category
ORDER BY saves DESC
LIMIT 25;

Give good emotes good slugs. A memorable /e name matters more than tab position for anything players use often. Set command explicitly rather than accepting the derived one.

Duplicates

The shipped seeds contain deliberate duplicates: six dict+name pairs are duplicated, twelve rows in total, and Look into the Distance appears three times — ids 217 and 218 are the same clip filed under different categories, and id 219 is a different clip from the same dictionary. These are not faults.

They are legal because no unique constraint applies, and the slug allocator quietly appends 2 and 3 to the later ones. Find them with:

sql
SELECT custom_name, COUNT(*) AS copies, GROUP_CONCAT(id ORDER BY id) AS ids
FROM `rm_emotes`
GROUP BY custom_name
HAVING copies > 1
ORDER BY copies DESC;

Slug collisions

There is no unique constraint on command, so two rows can carry the same explicit slug. The allocator resolves it at runtime in id order — meaning the lower id silently keeps the slug you wanted and the higher one becomes <slug>2.

sql
SELECT command, COUNT(*) AS copies, GROUP_CONCAT(id ORDER BY id) AS ids
FROM `rm_emotes`
WHERE command IS NOT NULL AND command <> ''
GROUP BY command
HAVING copies > 1;

Editing ids

Don't. Favourites, recents, key binds and the hover preview all key on id, and rm_emote_favs has a foreign key onto it. Changing an id orphans everything pointing at it.

If you need an emote to sit somewhere else in the list, change its category — not its id.

Documentation for RedMorrow. Scripts are licensed per server — redistribution is not permitted.