DISCORD RICH PRESENCE - SERVER OWNER SETUP
Available from 0.1.9 (upcoming). Not present in the public 0.1.8 release.
The private Discord-capable test client/server can be used before that release.

1. CREATE YOUR DISCORD APPLICATION
Visit https://discord.com/developers/applications and create an application.
Choose its public name: G1R:MP uses that name for Discord's main "Playing" title.
Copy the public Application ID from General Information. Keep it in quotes.
This is NOT your Discord server/guild ID, user ID, bot token or Client Secret.
Do not create a bot or give anyone credentials for this feature.

Optional artwork: application settings > Rich Presence > Art Assets. Upload
your art and copy the asset KEY, e.g. server_logo (Discord lowercases keys).
This G1R:MP API accepts uploaded asset keys only, not image URLs or disk paths.
Leave image fields empty for the first test. Artwork is not a client game PAK.

2. INSTALL (WINDOWS OR LINUX DEDICATED SERVER)
Back up your current resource files and serverconf.cfg first.
Copy the whole discord_presence directory into your existing resources folder.
Edit config.lua:
  enabled = true,
  applicationId = "YOUR_PUBLIC_APPLICATION_ID",
  details / state = your public server name and activity text.
YOUR_PUBLIC_APPLICATION_ID is a placeholder, NOT a usable ID.
Configure optional images, tooltips and the two HTTPS link pairs as needed.
Application ID remains empty and enabled=false in the distributed template.

In the EXISTING [resources] section of serverconf.cfg, append discord_presence
to the EXISTING autostart list. Example ONLY:
  autostart=chat,player_list,your_gamemode,discord_presence
Keep your real resource names and other entries; do not add a second section
or replace your working server configuration. The placeholder your_gamemode
must not be copied literally. Then restart the resource/server normally.
Neither Windows nor Linux dedicated servers need Discord, Python or an SDK.
Do not start discord_presence_test on a public server; it is unrestricted QA.

3. PLAYER OPT-IN
Run the Discord DESKTOP app and sign in normally, allow activity sharing in
Discord, enable Discord Rich Presence in G1R:MP launcher Settings, Save, then
rejoin the game. Launcher opt-in is OFF by default and takes effect next join.
A server cannot override it. Discord in a browser alone is insufficient.
The game-side transport supports Windows; Linux/Wine game clients are not
qualified. Linux dedicated servers ARE supported.

4. WHAT THIS STARTER DOES
Applies the configured profile on player join and to already-connected players
at resource start. The main title comes from the Discord application; details
is the server/activity line and state is the location/job/status line.
showSessionTime=true adds an elapsed clock, restarting on join/resource start,
and overrides any startTimestamp/endTimestamp in fields. Set it false to use
explicit timestamps, or omit timestamps for no clock.
partyId/partySize/partyMax, if uncommented, are STATIC display values. They do
not automatically count players or reserve slots. Update all three together
from your server logic if needed. Buttons are website links, not Join/Spectate.
No player names, IPs, coordinates, login data or character details are collected
automatically. Only configured text is published. Avoid sensitive text.
No commands, client event endpoints or polling timers are installed.
On disconnect/resource stop, the core releases this resource's presence.
To disable later, set enabled=false and restart this resource. Editing the file
without restarting does not change the already loaded Lua configuration.

5. DYNAMIC GAMEMODE INTEGRATION - CHOOSE ONE OWNER
Only ONE resource owns a player's active presence. Do not run this starter
and separate gamemode code that both set the same player's presence.
Option A: keep this resource, add your trusted server-side integration script
after server.lua in its scripts.xml.
Option B: copy config.lua and server.lua into your gamemode (use distinct file
names if needed), list them in its manifest in that order, and do NOT autostart
the standalone discord_presence resource. Keep existing gamemode scripts.
Do not overwrite an existing gamemode config.lua/server.lua.
Then, from the SAME resource's authorized gameplay logic:

  local ok, reason = updatePlayerDiscordPresence(playerId, {
      state = "Working at the forge",
      smallImage = "smith", smallText = "Blacksmith"
  })
  if not ok then outputDebugString("Presence update rejected: " .. reason) end

Use a real connected playerId; smith must be your uploaded asset key.
To remove optional fields:
  updatePlayerDiscordPresence(playerId, {smallImage=false, smallText=false})
To hide the profile:
  clearPlayerDiscordPresence(playerId)
After clear, use setPlayerDiscordPresence again, not update.
Do not pass the entire getter result into set/update: owner/applicationId are
read-only metadata. A changed application ID takes effect on subsequent
set/update calls; restart the resource to reapply this template to everyone.
Never expose a client-supplied arbitrary profile setter as a remote event.

6. DIAGNOSTICS AND ACCEPTANCE
Temporarily set logStatusChanges=true and restart. Use
getPlayerDiscordPresenceStatus(playerId) from the same server Lua workflow.
Status callback: onPlayerDiscordPresenceStatus(playerId, status).
set/update returning true means queued, NOT publicly visible.
applied means Discord acknowledged the local RPC request. Verify actual public
display with another Discord user; do not use this status for roles/rewards.
disabled: enable the launcher option and rejoin.
unsupported: incompatible client, no capability report yet, or expired report.
unavailable: start/restart desktop Discord and allow reconnection.
connecting: wait for connection/ACK and coalesced update.
error: check Application ID and field/asset configuration, then try again.
presence-owned-by-another-resource: remove the second owner, do not force-clear
other resources. getPlayerDiscordPresence(playerId).owner identifies the owner.
application-id-required / application-id-must-be-a-decimal-string: configure
the public ID as a decimal string, never a Lua number.
presence-not-set: set an initial profile before update.

Allow about 15 seconds for coalescing/reconnect after changes, plus Discord's
own display delay. Rapid intermediate values can intentionally be skipped.
Test two accounts' views, artwork, state updates, clear, quit/rejoin, resource
stop/start, Discord restart and launcher opt-out. A local ACK cannot certify
what friends see. End-to-end real Discord qualification still requires a
valid owner Application ID; automated IPC tests do not replace it.

Full field limits, all eight functions and callback:
https://g1r-mp.com/wiki/index.php?title=Discord_Rich_Presence
Official Discord setup reference:
https://docs.discord.com/developers/discord-social-sdk/development-guides/setting-rich-presence
