v3.0.1: SpotPass-Test-Aufgabe aufräumen (Start/Ende, 24-h-Abstand), Werkzeuge nur für vom Server bestätigte Admins (MBMNET-Platz, Antwort an die PID gebunden)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Nico Meister
2026-10-26 20:07:57 +01:00
co-authored by Claude Opus 5.5
parent f5fc0861c7
commit 29f2c556c2
5 changed files with 45 additions and 12 deletions
+15 -6
View File
@@ -1,6 +1,6 @@
// Entwickler-Werkzeuge nur für Admins: Die App fragt bei MBMNET, ob die verknüpfte NNID einem Admin
// gehört (GET /mbmnet/app-info?pid=...). Die Antwort wird auf der SD gemerkt, damit die Werkzeuge
// auch im Nintendo-Modus und ohne Verbindung da sind.
// Entwickler-Werkzeuge nur für Admins: Die App fragt bei MBMNET, ob die im MBMNET-Platz verknüpfte ID einem
// Admin gehört (GET /mbmnet/app-info?pid=...). Ohne verknüpfte MBMNET-ID gibt es keine Werkzeuge.
// Die Antwort wird mit der Kennung (PID) auf der SD gemerkt: Ohne Verbindung gilt sie nur für dasselbe Konto.
#include <3ds.h>
#include <stdio.h>
#include <string.h>
@@ -12,11 +12,12 @@
static volatile bool isAdmin;
static Thread thread;
static char url[128];
static u32 currentPid;
static void remember(bool on)
{
FILE *f = fopen(CACHE_FILE, "w");
if (f) { fputs(on ? "1" : "0", f); fclose(f); }
if (f) { fprintf(f, "%lu %d\n", (unsigned long)currentPid, on ? 1 : 0); fclose(f); }
}
static void worker(void *arg)
@@ -38,9 +39,17 @@ static void worker(void *arg)
void admin_start(const char *server, u32 pid)
{
currentPid = pid;
isAdmin = false;
if (!pid) { remove(CACHE_FILE); return; } // keine MBMNET-ID verknüpft: keine Werkzeuge
// Gemerkte Antwort nur, wenn sie zu genau diesem Konto gehört (alte Dateien ohne PID zählen nicht)
FILE *f = fopen(CACHE_FILE, "r");
if (f) { isAdmin = fgetc(f) == '1'; fclose(f); }
if (!pid) return;
if (f) {
unsigned long cachedPid = 0;
int on = 0;
if (fscanf(f, "%lu %d", &cachedPid, &on) == 2 && cachedPid == pid) isAdmin = on == 1;
fclose(f);
}
snprintf(url, sizeof(url), "http://%s/mbmnet/app-info?pid=%lu", server, (unsigned long)pid);
s32 prio = 0x30;
svcGetThreadPriority(&prio, CUR_THREAD_HANDLE);
+15 -3
View File
@@ -155,7 +155,7 @@ static void draw_top(PatchState st, const char *msg, bool admin, bool isAdmin)
{
C2D_DrawRectangle(0, 0, 0, TOP_W, SCR_H, COL_BG2, COL_BG2, COL_BG, COL_BG);
draw_update_line(admin);
const char *hint = admin ? "START: Beenden SELECT: Werkzeuge aus" : "START: Beenden SELECT: Werkzeuge";
const char *hint = admin ? "START: Beenden SELECT: Werkzeuge aus" : isAdmin ? "START: Beenden SELECT: Werkzeuge" : "START: Beenden";
text(hint, 10, 4, 0.4f, COL_MUTED, 0);
if (ftpOn) {
// FTP-Adresse direkt daneben
@@ -587,7 +587,17 @@ int main(int argc, char **argv)
snprintf(msg, sizeof(msg), "Neuer Speicherort für die Patches!\nBitte MBMNET einmal neu antippen.");
PatchState st = patch_state();
// Nur im MBMNET-Modus ist die verknüpfte NNID ein MBMNET-Konto
admin_start(server, st == STATE_MBMNET ? nnid_pid() : 0);
// Werkzeuge nur für Admins: gefragt wird mit der ID im MBMNET-Platz (auch wenn gerade Nintendo aktiv ist)
{
u8 mslot = frdswitch_slot_of(FRIEND_ACCOUNT_MBMNET);
char mid[20];
u32 mpid = 0;
if (mslot && frdswitch_slot_linked(FRIEND_ACCOUNT_MBMNET, mid, sizeof(mid)))
ACT_GetAccountInfo(&mpid, sizeof(mpid), mslot, INFO_TYPE_PRINCIPAL_ID);
admin_start(server, mpid);
}
// Ein SpotPass-Test von früher (App mitten im Test verlassen) darf nicht weiterlaufen
spotpass_test_cancel();
// MBMNET aktiv, aber das Freundekonto hat eine fremde Kennung: darauf hinweisen
if (st == STATE_MBMNET && frdswitch_current() == FRIEND_ACCOUNT_MBMNET) {
@@ -621,7 +631,8 @@ int main(int argc, char **argv)
u32 down = hidKeysDown();
if (--ftpCheckIn <= 0) { ftpCheckIn = 120; ftpOn = ftpctl_running(); }
if (down & KEY_START && rebootIn == 0) break; // während des Umschaltens nicht abbrechen
if (down & KEY_SELECT) toolsToggled = !toolsToggled;
if (down & KEY_SELECT && admin_is()) toolsToggled = !toolsToggled;
if (!admin_is()) toolsToggled = false; // Werkzeuge nur für Admins (Server hat es bestätigt)
// Werkzeuge nur mit SELECT, beim Öffnen immer aus
bool admin = toolsToggled;
if (!visible(selected, admin)) selected = admin || selected <= BTN_REBOOT ? BTN_ENABLE : BTN_SHOP;
@@ -728,6 +739,7 @@ int main(int argc, char **argv)
// schreiben sie nach dem Freigeben hinein und Luma zeigt einen Absturz (z. B. beim Neustart nach "Nintendo").
ftpctl_stop(NULL, 0);
shopctl_exit();
if (spotpassTestFrames >= 0) spotpass_test_cancel(); // App mitten im Test beendet
// Update- und Admin-Abfrage nur kurz abwarten: Läuft noch etwas, beendet sich die App hart (schneller
// Neustart nach dem Umschalten). Das Auffrischen schreibt Patch-Dateien - darauf warten (beim Umschalten
// ist es ohnehin schon fertig, Aktionen warten darauf).
+10 -1
View File
@@ -205,7 +205,9 @@ bool spotpass_test_start(const char *server, char *msg, size_t msglen)
return false;
}
const char *step = "";
rc = setup_task(server, TASK_TEST, testUrl, 60, &step);
// Abstand 24 h: Selbst wenn die Aufgabe stehen bleibt (App mitten im Test verlassen), kommt der Test
// nicht jede Minute wieder. Normalerweise löscht spotpass_test_poll/spotpass_test_cancel sie.
rc = setup_task(server, TASK_TEST, testUrl, 24 * 60 * 60, &step);
if (R_SUCCEEDED(rc)) { step = "Aufgabe starten"; rc = bossStartTaskImmediate(TASK_TEST); }
bossExit();
if (R_FAILED(rc)) {
@@ -217,6 +219,13 @@ bool spotpass_test_start(const char *server, char *msg, size_t msglen)
return true;
}
void spotpass_test_cancel(void)
{
if (R_FAILED(bossInit(0, false))) return;
bossDeleteTask(TASK_TEST, 0);
bossExit();
}
bool spotpass_test_poll(int seconds, char *msg, size_t msglen)
{
if (R_FAILED(bossInit(0, false))) return true;
+4 -1
View File
@@ -3,7 +3,7 @@
#include <stddef.h>
// SpotPass (BOSS): Der 3DS holt MBMNET-Nachrichten und Ankündigungen selbst im Hintergrund ab (auch im
// Standby, ohne App) und zeigt sie als Mitteilung im HOME-Menü. Die Aufgabe ruft alle 2 Minuten
// Standby, ohne App) und zeigt sie als Mitteilung im HOME-Menü. Die Aufgabe ruft alle 15 Minuten
// http://<server>/spotpass/<Geräte-ID>/news.boss ab (MBMNET Accounting, spotpass.js).
// Hintergrund-Aufgabe anmelden (bei aktivem MBMNET). false + msg bei Fehler.
@@ -15,3 +15,6 @@ void spotpass_unregister(void);
bool spotpass_test_start(const char *server, char *msg, size_t msglen);
// true = Test noch nicht fertig. msg bekommt den Stand bzw. das Ergebnis.
bool spotpass_test_poll(int seconds, char *msg, size_t msglen);
// Test-Aufgabe löschen (beim Start, beim Verlassen der App): Bleibt sie stehen, ruft der 3DS den Test
// immer wieder ab und zeigt jedes Mal eine neue Mitteilung.
void spotpass_test_cancel(void);