feat: customize login and lobby spaces from the panel
This commit is contained in:
54 files changed
+1146
-158
No files matched your search
@@ -5,6 +5,7 @@ import best.lolicon.felis.link.FelisApiClient;
|
||||
import best.lolicon.felis.link.LinkClient;
|
||||
import best.lolicon.felis.link.LinkCode;
|
||||
import best.lolicon.felis.link.LinkConfig;
|
||||
import best.lolicon.felis.link.ExperienceConfig;
|
||||
import best.lolicon.felis.link.LinkConfigLoader;
|
||||
|
||||
import com.loohp.limbo.events.EventHandler;
|
||||
@@ -92,6 +93,7 @@ public final class FelisLimboPlugin extends LimboPlugin implements Listener {
|
||||
|
||||
private volatile Readiness readiness;
|
||||
private volatile LoginFlow flow;
|
||||
private ExperienceConfig experience;
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
@@ -146,6 +148,7 @@ public final class FelisLimboPlugin extends LimboPlugin implements Listener {
|
||||
try {
|
||||
File props = new File(getDataFolder(), "felis-link.properties");
|
||||
cfg = LinkConfigLoader.load(props.toPath());
|
||||
experience = ExperienceConfig.load(ExperienceConfig.PATH);
|
||||
} catch (IOException e) {
|
||||
// Missing/half config: like the other Felis plugins, load un-crippled —
|
||||
// readiness stays up, the login flow just never turns on.
|
||||
@@ -161,7 +164,9 @@ public final class FelisLimboPlugin extends LimboPlugin implements Listener {
|
||||
}
|
||||
String consoleUrl = "https://" + panelHost;
|
||||
String lobby = GateConfig.lobby(System.getenv("FELIS_LOBBY_SERVER"));
|
||||
long timeoutMillis = GateConfig.loginTimeoutSeconds(System.getenv("FELIS_LOGIN_TIMEOUT_SECONDS")) * 1000L;
|
||||
long timeoutMillis = GateConfig.loginTimeoutSeconds(System.getenv("FELIS_LOGIN_TIMEOUT_SECONDS") != null
|
||||
? System.getenv("FELIS_LOGIN_TIMEOUT_SECONDS")
|
||||
: Long.toString(experience.number("loginTimeoutSeconds", GateConfig.DEFAULT_TIMEOUT_SECONDS))) * 1000L;
|
||||
this.flow = new LoginFlow(new LimboGate(), new FelisApiClient(cfg), new LinkClient(cfg),
|
||||
consoleUrl, timeoutMillis, System::currentTimeMillis, LOG);
|
||||
LOG.info("FelisLimbo: login flow ON — console=" + consoleUrl
|
||||
@@ -187,7 +192,7 @@ public final class FelisLimboPlugin extends LimboPlugin implements Listener {
|
||||
@Override
|
||||
public LoginFlow.Seat player(UUID id) {
|
||||
Player player = getServer().getPlayer(id);
|
||||
return player != null && player.isValid() ? new LimboSeat(player) : null;
|
||||
return player != null && player.isValid() ? new LimboSeat(player, experience) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -222,7 +227,7 @@ public final class FelisLimboPlugin extends LimboPlugin implements Listener {
|
||||
}
|
||||
|
||||
/** LimboSeat is one Limbo player as the flow sees them. */
|
||||
private record LimboSeat(Player player) implements LoginFlow.Seat {
|
||||
private record LimboSeat(Player player, ExperienceConfig experience) implements LoginFlow.Seat {
|
||||
@Override
|
||||
public String name() {
|
||||
return player.getName();
|
||||
@@ -235,7 +240,9 @@ public final class FelisLimboPlugin extends LimboPlugin implements Listener {
|
||||
|
||||
@Override
|
||||
public void showCode(LinkCode code, String url) {
|
||||
player.openBook(LoginBook.book(code.code(), url));
|
||||
if (experience.flag("openBook", true)) {
|
||||
player.openBook(LoginBook.book(code.code(), url, experience));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package best.lolicon.felis.limbo;
|
||||
|
||||
import best.lolicon.felis.link.ExperienceConfig;
|
||||
import java.util.Map;
|
||||
import net.kyori.adventure.inventory.Book;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
@@ -30,15 +32,20 @@ final class LoginBook {
|
||||
}
|
||||
|
||||
static Book book(String code, String url) {
|
||||
Component page = Component.text("Felis 登录 / Login\n\n")
|
||||
return book(code, url, new ExperienceConfig(Map.of()));
|
||||
}
|
||||
|
||||
static Book book(String code, String url, ExperienceConfig settings) {
|
||||
Component page = Component.text(settings.text("bookHeading", "Felis 登录 / Login") + "\n\n")
|
||||
.append(Component.text("绑定码 / Code:\n"))
|
||||
.append(Component.text(code + "\n\n").color(NamedTextColor.GOLD))
|
||||
.append(Component.text("▶ 点此打开登录页\n▶ Open login page\n")
|
||||
.append(Component.text(settings.text("bookAction", "▶ 点此打开登录页\n▶ Open login page") + "\n")
|
||||
.color(NamedTextColor.AQUA)
|
||||
.clickEvent(ClickEvent.openUrl(url)))
|
||||
.append(Component.text("\n在系统浏览器中完成。\nUse your SYSTEM browser —\nnot WeChat / QQ (passkey\nwon't work there).")
|
||||
.append(Component.text("\n" + settings.text("bookHelp", "在系统浏览器中完成。\nUse your SYSTEM browser —\nnot WeChat / QQ (passkey\nwon't work there)."))
|
||||
.color(NamedTextColor.GRAY));
|
||||
return Book.book(Component.text("Felis Login"), Component.text("Felis"), page);
|
||||
return Book.book(Component.text(settings.text("bookTitle", "Felis Login")),
|
||||
Component.text(settings.text("bookAuthor", "Felis")), page);
|
||||
}
|
||||
|
||||
/** chatLine renders one chat line with § colour codes, its web addresses clickable. */
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package best.lolicon.felis.limbo;
|
||||
|
||||
import best.lolicon.felis.link.ExperienceConfig;
|
||||
import best.lolicon.felis.link.Control;
|
||||
import best.lolicon.felis.link.ControlFrame;
|
||||
import best.lolicon.felis.link.FelisApiClient;
|
||||
@@ -24,6 +25,8 @@ import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -356,7 +359,22 @@ public final class LoginFlowTest {
|
||||
assertEq("lobby set", "hub", GateConfig.lobby(" hub "));
|
||||
}
|
||||
|
||||
private static void loginBook() {
|
||||
private static void loginBook() throws IOException {
|
||||
Path file = Files.createTempFile("felis-experience", ".json");
|
||||
ExperienceConfig settings;
|
||||
try {
|
||||
Files.writeString(file, "{\"bookTitle\":\"My Network\",\"bookHeading\":\"欢迎 / Welcome\"}");
|
||||
settings = ExperienceConfig.load(file);
|
||||
} finally {
|
||||
Files.deleteIfExists(file);
|
||||
}
|
||||
Book customized = LoginBook.book(CODE, CONSOLE + "/link", settings);
|
||||
assertEq("custom book title", "My Network", plain(customized.title()));
|
||||
assertTrue("custom book reads UTF-8 from the world volume", plain(customized.pages().get(0)).contains("欢迎 / Welcome"));
|
||||
assertEq("custom book keeps the code", true, plain(customized.pages().get(0)).contains(CODE));
|
||||
List<Component> customLinks = new ArrayList<>();
|
||||
collectLinks(customized.pages().get(0), customLinks);
|
||||
assertEq("custom book keeps the generated login URL", CONSOLE + "/link", customLinks.get(0).clickEvent().value());
|
||||
Book book = LoginBook.book(CODE, CONSOLE + "/link");
|
||||
assertEq("book title", "Felis Login", plain(book.title()));
|
||||
Component page = book.pages().get(0);
|
||||
|
||||
Reference in new issue
Block a user