Skip to content

Commit 347637d

Browse files
gaoyu06claude
andcommitted
feat(edge): add auth service and API client, enhance OOBE flow
Add AuthService and FPSMasterApiClient for launcher token authentication, update ConfigManager/Configure annotations, and significantly expand the OOBE screen with new animations and flow steps. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 06a0abd commit 347637d

15 files changed

Lines changed: 1887 additions & 66 deletions

File tree

src/main/java/top/fpsmaster/FPSMaster.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import top.fpsmaster.features.impl.interfaces.ClientSettings;
88
import top.fpsmaster.features.manager.ModuleManager;
99
import top.fpsmaster.font.FontManager;
10+
import top.fpsmaster.modules.client.api.AuthService;
1011
import top.fpsmaster.modules.client.thread.ClientThreadPool;
1112
import top.fpsmaster.modules.client.telemetry.EdgeTelemetryReporter;
1213
import top.fpsmaster.modules.config.ConfigManager;
@@ -64,6 +65,11 @@ public static String getClientTitle() {
6465
return CLIENT_NAME + " " + EDITION + " (" + GitInfo.getBranch() + " - " + GitInfo.getCommitIdAbbrev() + ")" + (development ? " - dev" : "");
6566
}
6667

68+
private void initializeAuth() {
69+
ClientLogger.info("Initializing Auth Service...");
70+
AuthService.getInstance().initialize();
71+
}
72+
6773
private void initializeFonts() {
6874
ClientLogger.info("Initializing Fonts...");
6975

@@ -117,6 +123,7 @@ private void checkOptifine() {
117123
public void initialize() {
118124
try {
119125
FileUtils.init(mc.mcDataDir);
126+
initializeAuth(); // Initialize auth service early to check for launcher tokens
120127
initializeFonts();
121128
initializeModules();
122129
initializeComponents();
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
package top.fpsmaster.modules.client.api;
2+
3+
import com.google.gson.Gson;
4+
import com.google.gson.GsonBuilder;
5+
import com.google.gson.JsonObject;
6+
import top.fpsmaster.modules.logger.ClientLogger;
7+
8+
import java.io.BufferedReader;
9+
import java.io.BufferedWriter;
10+
import java.io.File;
11+
import java.io.IOException;
12+
import java.nio.file.DirectoryStream;
13+
import java.nio.file.Files;
14+
import java.nio.file.Path;
15+
import java.nio.file.Paths;
16+
17+
/**
18+
* Service for managing authentication tokens at system user level.
19+
* Tokens are stored in AppData/Roaming/FPSMaster/auth.json
20+
* Launcher can pass tokens via system properties: fpsmaster.auth.token
21+
*/
22+
public class AuthService {
23+
private static final String AUTH_FILE_NAME = "auth.json";
24+
private static final String SYSTEM_PROPERTY_TOKEN = "fpsmaster.auth.token";
25+
private static final String SYSTEM_PROPERTY_REFRESH = "fpsmaster.auth.refreshToken";
26+
private static final String SYSTEM_PROPERTY_EXPIRES = "fpsmaster.auth.tokenExpiresAt";
27+
28+
private static AuthService instance;
29+
private final Gson gson;
30+
private final File authFile;
31+
32+
private String accessToken;
33+
private String refreshToken;
34+
private long tokenExpiresAt;
35+
36+
private AuthService() {
37+
this.gson = new GsonBuilder().setPrettyPrinting().create();
38+
this.authFile = resolveAuthFile();
39+
loadFromFile();
40+
}
41+
42+
private File resolveAuthFile() {
43+
String appData = System.getenv("APPDATA");
44+
if (appData != null && !appData.isEmpty()) {
45+
File fpsmasterDir = new File(appData, "FPSMaster");
46+
if (!fpsmasterDir.exists()) {
47+
fpsmasterDir.mkdirs();
48+
}
49+
return new File(fpsmasterDir, AUTH_FILE_NAME);
50+
}
51+
// Fallback to user home
52+
String userHome = System.getProperty("user.home");
53+
File fpsmasterDir = new File(userHome, ".fpsmaster");
54+
if (!fpsmasterDir.exists()) {
55+
fpsmasterDir.mkdirs();
56+
}
57+
return new File(fpsmasterDir, AUTH_FILE_NAME);
58+
}
59+
60+
public static AuthService getInstance() {
61+
if (instance == null) {
62+
instance = new AuthService();
63+
}
64+
return instance;
65+
}
66+
67+
/**
68+
* Initialize the service and check for launcher-provided tokens
69+
*/
70+
public void initialize() {
71+
// Check if launcher provided tokens via system properties
72+
String launcherToken = System.getProperty(SYSTEM_PROPERTY_TOKEN);
73+
if (launcherToken != null && !launcherToken.isEmpty()) {
74+
ClientLogger.info("Found auth token from launcher");
75+
this.accessToken = launcherToken;
76+
this.refreshToken = System.getProperty(SYSTEM_PROPERTY_REFRESH);
77+
String expiresStr = System.getProperty(SYSTEM_PROPERTY_EXPIRES);
78+
if (expiresStr != null && !expiresStr.isEmpty()) {
79+
try {
80+
this.tokenExpiresAt = Long.parseLong(expiresStr);
81+
} catch (NumberFormatException e) {
82+
this.tokenExpiresAt = System.currentTimeMillis() + (7L * 24 * 60 * 60 * 1000);
83+
}
84+
} else {
85+
this.tokenExpiresAt = System.currentTimeMillis() + (7L * 24 * 60 * 60 * 1000);
86+
}
87+
saveToFile();
88+
} else {
89+
loadFromFile();
90+
}
91+
}
92+
93+
public boolean isLoggedIn() {
94+
return accessToken != null && !accessToken.isEmpty() && !isTokenExpired();
95+
}
96+
97+
public boolean isTokenExpired() {
98+
return tokenExpiresAt > 0 && System.currentTimeMillis() >= tokenExpiresAt;
99+
}
100+
101+
public String getAccessToken() {
102+
return accessToken;
103+
}
104+
105+
public String getRefreshToken() {
106+
return refreshToken;
107+
}
108+
109+
public long getTokenExpiresAt() {
110+
return tokenExpiresAt;
111+
}
112+
113+
/**
114+
* Save tokens (called after successful login)
115+
*/
116+
public void saveTokens(String access, String refresh, long expiresAt) {
117+
this.accessToken = access;
118+
this.refreshToken = refresh;
119+
this.tokenExpiresAt = expiresAt;
120+
saveToFile();
121+
}
122+
123+
/**
124+
* Save tokens with default expiration (7 days)
125+
*/
126+
public void saveTokens(String access, String refresh) {
127+
saveTokens(access, refresh, System.currentTimeMillis() + (7L * 24 * 60 * 60 * 1000));
128+
}
129+
130+
/**
131+
* Clear all tokens (called after logout)
132+
*/
133+
public void clearTokens() {
134+
this.accessToken = null;
135+
this.refreshToken = null;
136+
this.tokenExpiresAt = 0;
137+
saveToFile();
138+
}
139+
140+
private void saveToFile() {
141+
try {
142+
JsonObject json = new JsonObject();
143+
if (accessToken != null) {
144+
json.addProperty("accessToken", accessToken);
145+
}
146+
if (refreshToken != null) {
147+
json.addProperty("refreshToken", refreshToken);
148+
}
149+
json.addProperty("tokenExpiresAt", tokenExpiresAt);
150+
json.addProperty("lastUpdated", System.currentTimeMillis());
151+
152+
String content = gson.toJson(json);
153+
try (BufferedWriter writer = Files.newBufferedWriter(authFile.toPath())) {
154+
writer.write(content);
155+
}
156+
ClientLogger.debug("Auth tokens saved to: " + authFile.getAbsolutePath());
157+
} catch (IOException e) {
158+
ClientLogger.error("Failed to save auth tokens: " + e.getMessage());
159+
}
160+
}
161+
162+
private void loadFromFile() {
163+
if (!authFile.exists()) {
164+
ClientLogger.debug("No auth file found at: " + authFile.getAbsolutePath());
165+
return;
166+
}
167+
168+
try {
169+
StringBuilder content = new StringBuilder();
170+
try (BufferedReader reader = Files.newBufferedReader(authFile.toPath())) {
171+
String line;
172+
while ((line = reader.readLine()) != null) {
173+
content.append(line);
174+
}
175+
}
176+
JsonObject json = gson.fromJson(content.toString(), JsonObject.class);
177+
if (json != null) {
178+
accessToken = json.has("accessToken") && !json.get("accessToken").isJsonNull()
179+
? json.get("accessToken").getAsString()
180+
: null;
181+
refreshToken = json.has("refreshToken") && !json.get("refreshToken").isJsonNull()
182+
? json.get("refreshToken").getAsString()
183+
: null;
184+
tokenExpiresAt = json.has("tokenExpiresAt") && !json.get("tokenExpiresAt").isJsonNull()
185+
? json.get("tokenExpiresAt").getAsLong()
186+
: 0L;
187+
ClientLogger.debug("Auth tokens loaded from: " + authFile.getAbsolutePath());
188+
}
189+
} catch (IOException e) {
190+
ClientLogger.error("Failed to load auth tokens: " + e.getMessage());
191+
}
192+
}
193+
194+
public File getAuthFile() {
195+
return authFile;
196+
}
197+
}

0 commit comments

Comments
 (0)