mirror of
https://github.com/SirHumza/orbisRPC.git
synced 2026-10-06 11:00:57 +02:00
Gateway precision, SFO OOB fix, build lists, artwork runbook
- discord: HELLO requires text frame; READY/HELLO timeout diagnostics; invalid-session fast retry path (-3); clamp warning; malformed close - daemon: invalid session resets backoff instead of doubling it - sfo: bounded key scan (no OOB on malformed files) + regression test - build.sh + plugin Makefile compile sfo.c (link verified) - deploy/ARTWORK.md: shared-app asset pipeline, no-URL rule
This commit is contained in:
1 parent
cdb431cfa6
commit
28f6275a05
8 files changed
+59
-7
No files matched your search
@@ -0,0 +1,30 @@
|
||||
# Cover art pipeline
|
||||
|
||||
Discord serves Rich Presence images only from assets uploaded to a
|
||||
Discord application. There is no URL mode and no automatic official art
|
||||
for custom presences. One shared app covers every install.
|
||||
|
||||
## One-time maintainer setup
|
||||
|
||||
1. Create an application at https://discord.com/developers/applications.
|
||||
2. Rich Presence, Art Assets, upload one PNG per game. Asset name must be
|
||||
the lowercase title ID, e.g. `cusa00740` for Terraria. 512x512 PNG.
|
||||
3. Copy the Application ID into the repo default (`config/config.json`
|
||||
`application_id`) so every install inherits it.
|
||||
|
||||
## Where icons live on the PS4
|
||||
|
||||
`/user/appmeta/<TITLEID>/icon0.png` (around 50KB). Pull over FTP:
|
||||
|
||||
```
|
||||
get /user/appmeta/CUSA00740/icon0.png -> cusa00740.png
|
||||
```
|
||||
|
||||
Upload as asset `cusa00740`.
|
||||
|
||||
## Runtime behavior
|
||||
|
||||
The daemon sends `assets: { large_image: "<lower titleId>",
|
||||
large_text: "<game name>" }` only when `application_id` is set. Without
|
||||
it, presence posts with no artwork and logs one notice. Missing assets
|
||||
for a title also degrade to no image. Nothing crashes on absent art.
|
||||
@@ -174,6 +174,7 @@ int daemon_run(const char *fixed_game_name){
|
||||
log_close();
|
||||
return 2;
|
||||
}
|
||||
if(tr == -3){ log_msg("invalid session; fresh identify"); backoff = base_poll; ws_close(&dc.ws); break; }
|
||||
if(tr != 0){ log_msg("gateway dropped; reconnecting"); break; }
|
||||
|
||||
for(int i = 0; i < 10 && !s_stop; i++) usleep(100000);
|
||||
|
||||
+13
-4
@@ -146,7 +146,8 @@ int discord_connect(discord_t *d, const char *token){
|
||||
/* HELLO (text frame carrying {"op":10,...}) */
|
||||
char buf[2048]; int op=0,fin=0;
|
||||
int nr=rx_frame(d,buf,sizeof buf,&op,&fin,now+15);
|
||||
if(nr<=0 || (op!=1 && op!=0)){
|
||||
if(nr==-4){ log_msg("no HELLO (timeout)"); ws_close(&d->ws); d->connected=0; return -1; }
|
||||
if(nr<=0 || op!=1){
|
||||
log_msg("no HELLO (nr=%d op=%d)",nr,op);
|
||||
ws_close(&d->ws); d->connected=0;
|
||||
return -1;
|
||||
@@ -161,6 +162,11 @@ int discord_connect(discord_t *d, const char *token){
|
||||
jl_free(h);
|
||||
}
|
||||
if(!d->hb_interval_ms) d->hb_interval_ms=45000;
|
||||
if(d->hb_interval_ms < 5000){
|
||||
log_msg("discord: hb_interval %lldms suspiciously small; clamping to 5000",
|
||||
(long long)d->hb_interval_ms);
|
||||
d->hb_interval_ms = 5000;
|
||||
}
|
||||
if(send_identify(d, token) < 0){
|
||||
log_msg("discord: identify send failed");
|
||||
ws_close(&d->ws); d->connected=0;
|
||||
@@ -172,6 +178,7 @@ int discord_connect(discord_t *d, const char *token){
|
||||
int64_t dl=time(NULL)+20;
|
||||
for(;;){
|
||||
nr=rx_frame(d,buf,sizeof buf,&op,&fin,dl);
|
||||
if(nr==-4){ log_msg("no READY after identify (timeout)"); break; }
|
||||
if(nr<=0){ log_msg("no READY after identify (nr=%d)",nr); break; }
|
||||
if(op==8){
|
||||
if(!fin){ log_msg("fragmented close; reconnecting"); ws_close(&d->ws); d->connected=0; return -1; }
|
||||
@@ -182,7 +189,7 @@ int discord_connect(discord_t *d, const char *token){
|
||||
return code==4004 ? -2 : -1;
|
||||
}
|
||||
if(op==9){ ws_pong(&d->ws); continue; }
|
||||
if(op!=1 && op!=0) continue;
|
||||
if(op!=1) continue;
|
||||
int go = gw_op(buf, (size_t)nr);
|
||||
if(go==11){ d->last_ack=time(NULL); continue; }
|
||||
if(go==0 && is_ready(buf, (size_t)nr)){
|
||||
@@ -323,10 +330,12 @@ int discord_tick(discord_t *d){
|
||||
log_msg("gateway: reconnect requested");
|
||||
d->connected=0;
|
||||
return -1;
|
||||
case 9: /* INVALID_SESSION */
|
||||
case 9: /* INVALID_SESSION: session dead, fresh IDENTIFY needed.
|
||||
* Distinct code so the caller retries promptly instead of
|
||||
* doubling into a long backoff. */
|
||||
log_msg("gateway: invalid session");
|
||||
d->connected=0;
|
||||
return -1;
|
||||
return -3;
|
||||
case 11:
|
||||
d->last_ack=now;
|
||||
break;
|
||||
|
||||
+1
-1
@@ -24,5 +24,5 @@ int discord_set_presence_ex(discord_t *d, const char *state, const char *name,
|
||||
const char *title_id, const char *application_id,
|
||||
int64_t started_epoch); /* op 3 */
|
||||
int discord_clear_presence(discord_t *d); /* clear activity, stay online */
|
||||
int discord_tick(discord_t *d); /* 0 ok; -1 drop/reconnect; -2 auth-fatal */
|
||||
int discord_tick(discord_t *d); /* 0 ok; -1 drop/reconnect; -2 auth-fatal; -3 invalid session (retry promptly) */
|
||||
#endif
|
||||
+6
-1
@@ -36,10 +36,15 @@ int sfo_title(const unsigned char *buf, size_t n, char *out, size_t cap){
|
||||
for(uint32_t i = 0; i < h->count; i++){
|
||||
size_t ko = (size_t)h->key_off + e[i].key_off;
|
||||
if(ko >= n) continue;
|
||||
/* keys must be null-terminated INSIDE the buffer: bound the scan */
|
||||
size_t kmax = n - ko;
|
||||
size_t klen = 0;
|
||||
while(klen < kmax && buf[ko + klen]) klen++;
|
||||
if(klen >= kmax) continue; /* unterminated key: malformed */
|
||||
const char *key = (const char *)(buf + ko);
|
||||
/* keys of interest: TITLE first, then language variants TITLE_XX */
|
||||
int is_title = (!strcmp(key, "TITLE") ||
|
||||
(!strncmp(key, "TITLE_", 6) && strlen(key) == 8));
|
||||
(klen == 8 && !memcmp(key, "TITLE_", 6)));
|
||||
if(!is_title) continue;
|
||||
if(e[i].fmt != 0x0004 && e[i].fmt != 0x0204) continue;
|
||||
size_t dlen = e[i].len;
|
||||
|
||||
@@ -20,6 +20,7 @@ INTDIR := $(BUILD_DIR)
|
||||
|
||||
# Our daemon sources (payload main.c is NOT part of the plugin; plugin.c is the entry)
|
||||
DAEMON_SRC := $(ORBISRPC)/log.c $(ORBISRPC)/cfg.c $(ORBISRPC)/jsonlite.c $(ORBISRPC)/b64.c \
|
||||
$(ORBISRPC)/sfo.c \
|
||||
$(ORBISRPC)/tls.c $(ORBISRPC)/ws.c $(ORBISRPC)/discord.c $(ORBISRPC)/detect.c \
|
||||
$(ORBISRPC)/daemon.c
|
||||
PLUGIN_SRC := $(SRCDIR)/plugin.c $(SRCDIR)/compat.c
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ LDFLAGS="-m elf_x86_64 -pie --eh-frame-hdr -L$SDK/lib $LIBS $SDK/lib/crt1.o --sc
|
||||
export OO_PS4_TOOLCHAIN="$SDK"
|
||||
OUT="$ROOT/build"; mkdir -p "$OUT"
|
||||
echo "=== compiling (CC=$CC LD=$LD SDK=$SDK) ==="
|
||||
for f in log cfg jsonlite b64 tls ws detect discord daemon compat main; do
|
||||
for f in log cfg jsonlite b64 sfo tls ws detect discord daemon compat main; do
|
||||
"$CC" $CFLAGS -c -o "$OUT/$f.o" "orbisrpc/$f.c" || fail "compile $f"
|
||||
done
|
||||
echo "=== mbedtls (skip net_sockets/timing: POSIX-only) ==="
|
||||
|
||||
@@ -77,6 +77,12 @@ static void test_sfo(void) {
|
||||
/* tiny output buffer still safe */
|
||||
assert(sfo_title(sfo, 64, out, 4) == 0);
|
||||
assert(out[3] == 0);
|
||||
/* unterminated key region: must fail, not read OOB */
|
||||
memset(sfo, 0x41, sizeof sfo);
|
||||
sfo[0]=0x00; sfo[1]='P'; sfo[2]='S'; sfo[3]='F';
|
||||
sfo[8]=36; sfo[12]=48; sfo[16]=1;
|
||||
sfo[20]=0; sfo[22]=0x04; sfo[24]=9; sfo[28]=16; sfo[32]=0;
|
||||
assert(sfo_title(sfo, 64, out, sizeof out) != 0);
|
||||
}
|
||||
static void test_nametable(void) {
|
||||
char out[64];
|
||||
|
||||
Reference in new issue
Block a user