Discord Presence configuration

From Wiki G1R-MP G1 Remake Multiplayer
Revision as of 13:26, 20 October 2026 by QCherry (talk | contribs) (Release 0.1.9 / BUILD264 / Protocol42: availability and download setup; preserve experimental limits)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Discord Presence configuration

Available from 0.1.9. Use matching updated clients and server.

Complete optional server-only starter for Discord Rich Presence. It adds no public commands, remote event endpoints, client scripts or polling timers. Disabled by default; the Application ID is intentionally empty.

Create your application

Use the Discord Developer Portal to create an application. Its name is the main Playing title in this integration. Copy the public Application ID from General Information into the quoted configuration field. You do not need a bot, token, Client Secret, OAuth setup or access to players' Discord accounts.

Optional images: application settings, Rich Presence, Art Assets. Upload artwork and copy its saved lowercase asset key. Leave image keys empty for the first test. G1R:MP accepts keys only, not image URLs or local files.

Install on Windows or Linux

  1. Back up your existing server configuration and resources.
  2. Create resources/discord_presence under your configured resources directory and put the three files below there.
  3. Set enabled=true, replace the empty Application ID with your real public ID string, and edit public texts/images/links.
  4. Append discord_presence to the existing autostart list in the existing [resources] section of serverconf.cfg. Keep all current resources and settings; do not add a duplicate section or overwrite the entire configuration.
  5. Restart this resource/server normally. Neither Windows nor Linux dedicated servers need Discord installed or an extra runtime dependency.

For example, if your current list is autostart=chat,my_gamemode, change it to autostart=chat,my_gamemode,discord_presence. Keep your real names; my_gamemode is just an example.

On the player's machine: start desktop Discord, allow activity sharing, enable Discord Rich Presence in launcher Settings, Save and rejoin. Local opt-in is off by default; server Lua cannot override it. A browser-only Discord session is insufficient. The game-side transport is Windows; Linux dedicated servers are supported.

Files

File names and load order are significant. Save as UTF-8.

scripts.xml

<?xml version="1.0" encoding="utf-8"?>
<scripts>
    <script src="config.lua" type="server" />
    <script src="server.lua" type="server" />
</scripts>

config.lua

-- Server-owner starter resource. Requires the Discord API available from 0.1.9.
-- No bot, token, Client Secret, Discord login or player account ID is needed.
-- Public Application ID must be a quoted string copied from Discord.
DiscordPresenceConfig = {
    enabled = false,
    applicationId = "",
    showSessionTime = true, -- starts on join / resource start; not saved
    logStatusChanges = false, -- diagnostics only, never authorization
    fields = {
        details = "My Gothic RP server",
        state = "Exploring the Colony",
        largeImage = "", -- optional uploaded asset key, e.g. "server_logo"
        largeText = "",
        smallImage = "",
        smallText = "",
        -- Optional website / Discord invite links (HTTPS only):
        -- button1Label = "Website",
        -- button1Url = "https://example.com/",
        -- button2Label = "Community",
        -- button2Url = "https://discord.gg/YOUR_INVITE",
        -- Optional count display; these are static unless your Lua updates them:
        -- partyId = "my-rp-server", -- public label, NOT a secret or IP
        -- partySize = 1,
        -- partyMax = 100,
        -- Optional countdown; turn showSessionTime off for explicit timestamps:
        -- startTimestamp = 1792454400,
        -- endTimestamp = 1792458000,
    },
}

server.lua

-- No client scripts, remote events, public commands or polling timers.
-- Put server-authoritative location/job updates in THIS owning resource,
-- or integrate both this config and this loader into your gamemode instead.
local config = DiscordPresenceConfig
local function log(message)
    outputDebugString("[Discord Presence] " .. message)
end
if type(config) ~= "table" then
    log("Missing config.lua; resource is inactive.")
    return
end
if config.enabled ~= true then
    log("Disabled in config.lua; no presence is published.")
    return
end
for _, name in ipairs({
    "setDiscordApplicationId", "getDiscordTimestamp", "setPlayerDiscordPresence"
}) do
    if type(_G[name]) ~= "function" then
        log("This server does not provide the required 0.1.9 Discord API; inactive.")
        return
    end
end
if type(config.fields) ~= "table" then
    log("fields must be a table; resource is inactive.")
    return
end
local configured, reason = setDiscordApplicationId(config.applicationId)
if not configured then
    log("Set applicationId to your PUBLIC decimal Application ID string: " .. tostring(reason))
    return
end

local startedAt = {}
local function apply(playerId)
    local fields = {}
    for key, value in pairs(config.fields) do fields[key] = value end
    if config.showSessionTime == true then
        startedAt[playerId] = startedAt[playerId] or getDiscordTimestamp()
        fields.startTimestamp = startedAt[playerId]
        fields.endTimestamp = nil -- elapsed time takes precedence over a countdown
    end
    local ok, failure = setPlayerDiscordPresence(playerId, fields)
    if not ok then
        -- In particular, never clear/steal a profile owned by another resource.
        log("Player " .. tostring(playerId) .. " profile rejected: " .. tostring(failure))
    end
end

addEventHandler("onResourceStart", resourceRoot, function()
    -- Also handles starting this resource while players are already connected.
    for _, playerId in ipairs(getPlayers()) do apply(playerId) end
end)
addEventHandler("onPlayerJoin", root, apply)
addEventHandler("onPlayerQuit", root, function(playerId)
    startedAt[playerId] = nil
end)
addEventHandler("onPlayerDiscordPresenceStatus", root, function(playerId, status)
    if config.logStatusChanges == true then
        log("Player " .. tostring(playerId) .. " client report: " .. tostring(status))
    end
end)
-- Core resource cleanup releases owned profiles automatically on stop/fault.

Configuring public content

  • details: server/activity description; state: location, job or current activity. The main Playing title comes from the Discord application name.
  • largeImage/smallImage: optional uploaded asset keys; largeText/smallText: tooltips.
  • showSessionTime=true: elapsed time from join/resource start, overriding explicit timestamps. Set false for explicit startTimestamp/endTimestamp or omit timestamps for no clock. Restarting the resource resets the starter's elapsed clock for connected players.
  • Optional partyId/partySize/partyMax are static values in this configuration, not automatically counted players. Your trusted Lua can update them together; they do not reserve server slots.
  • Link labels and HTTPS URLs must be configured in pairs. Replace the example domains/invite with your own. Buttons are not automatic Join/Spectate.
  • logStatusChanges=true enables diagnostic logging; turn it off after testing if unnecessary. Status does not authorize rewards or permissions.
  • To disable, set enabled=false and restart this resource. Editing the file alone does not reload it.

Every field and limit is documented in the API guide. Do not publish account information, passwords, tokens, player IPs or private character data. This starter does not automatically read player names, positions, jobs or inventory.

Dynamic RP integration: choose one owner

Only one resource may own a player's active presence.

Option A: standalone resource. Add your trusted server integration script after server.lua in this resource's manifest. Call updatePlayerDiscordPresence there after your gameplay logic changes a location/job.

Option B: own the presence in your gamemode. Add renamed copies of the configuration and loader to the gamemode's manifest in the same order, keeping existing files/scripts intact. Do not overwrite an existing config.lua/server.lua. Do not start the standalone discord_presence resource as a second owner. Now the gamemode can call the same Lua functions for its profiles.

Do not register an arbitrary client-controlled setter event. Stop/remove competing owners, including development test resources. Another resource's profile cannot be silently taken over. The getter returns an owner field to help diagnose conflicts.

Test and troubleshoot

Start with basic text and no artwork/links. Allow roughly 15 seconds for update coalescing/reconnect, plus Discord's own display delay. Use getPlayerDiscordPresenceStatus or the optional callback log.

Disabled means the player must opt in and rejoin; unsupported means no compatible report yet/old client/expired capability; unavailable means desktop Discord is absent or disconnected. Error requires checking your application/fields. A presence-owned-by-another-resource error means two scripts compete for one profile.

Applied means Discord acknowledged the local request, not that a friend can see it. Verify with another account, then test artwork, dynamic updates, clear, quit/rejoin, resource stop/start, Discord restart and launcher opt-out. Real Discord visibility still needs an owner-provided application and acceptance testing.

For the complete eight functions, callback, ownership, cleanup, timing and privacy contract, see Discord Rich Presence.