mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-06-26 10:04:58 +08:00
Compare commits
No commits in common. "a2d5b934df76c00513309c0cfac4b3b669450e9a" and "bd6bf0e3f94db0e420b5169e05ed9f79678f3fa8" have entirely different histories.
a2d5b934df
...
bd6bf0e3f9
2
.gitignore
vendored
2
.gitignore
vendored
@ -17,7 +17,7 @@
|
|||||||
*.nar
|
*.nar
|
||||||
*.ear
|
*.ear
|
||||||
*.zip
|
*.zip
|
||||||
*.gz
|
*.tar.gz
|
||||||
*.rar
|
*.rar
|
||||||
|
|
||||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||||
|
@ -1,162 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
|
||||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400&display=swap">
|
|
||||||
<link rel="stylesheet"
|
|
||||||
href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
background-color: #f0f0f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
font-weight: 300;
|
|
||||||
}
|
|
||||||
|
|
||||||
a, a:hover {
|
|
||||||
text-decoration: none !important;
|
|
||||||
color: #626976;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
padding: 3rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
color: #626976;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
width: 70%;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
table thead tr {
|
|
||||||
height: 60px;
|
|
||||||
background: #626976;
|
|
||||||
}
|
|
||||||
|
|
||||||
table thead tr th {
|
|
||||||
font-size: 18px;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
table tbody tr {
|
|
||||||
height: 50px;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
}
|
|
||||||
|
|
||||||
tbody tr:nth-child(even) {
|
|
||||||
background-color: #fdfdfd;
|
|
||||||
}
|
|
||||||
|
|
||||||
table th, table td {
|
|
||||||
text-align: left;
|
|
||||||
padding: 0 8px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<title>GM Handbook</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="content">
|
|
||||||
<div class="container">
|
|
||||||
<h2 class="mb-5">{{TITLE}}</h2>
|
|
||||||
|
|
||||||
<h3>{{TITLE_COMMANDS}}</h3>
|
|
||||||
<hr/>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>{{HEADER_COMMAND}}</th>
|
|
||||||
<th>{{HEADER_DESCRIPTION}}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
{{COMMANDS_TABLE}}
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<h3>{{TITLE_AVATARS}}</h3>
|
|
||||||
<hr/>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>{{HEADER_ID}}</th>
|
|
||||||
<th>{{HEADER_AVATAR}}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
{{AVATARS_TABLE}}
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<h3>{{TITLE_ITEMS}}</h3>
|
|
||||||
<hr/>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>{{HEADER_ID}}</th>
|
|
||||||
<th>{{HEADER_ITEM}}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
{{ITEMS_TABLE}}
|
|
||||||
</table>
|
|
||||||
|
|
||||||
|
|
||||||
<h3>{{TITLE_SCENES}}</h3>
|
|
||||||
<hr/>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>{{HEADER_ID}}</th>
|
|
||||||
<th>{{HEADER_SCENE}}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
{{SCENES_TABLE}}
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<h3>{{TITLE_MONSTERS}}</h3>
|
|
||||||
<hr/>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>{{HEADER_ID}}</th>
|
|
||||||
<th>{{HEADER_MONSTER}}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
{{MONSTERS_TABLE}}
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<footer>
|
|
||||||
<div class="copyright">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<span>Template by BecodReyes. All rights reserved.</span>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<ul style="float:right">
|
|
||||||
<li class="list-inline-item">
|
|
||||||
<a href="https://github.com/Grasscutters/Grasscutter">Github</a>
|
|
||||||
</li>
|
|
||||||
<li class="list-inline-item">·</li>
|
|
||||||
<li class="list-inline-item">
|
|
||||||
<a href="https://github.com/Grasscutters/Grasscutter/blob/stable/LICENSE">License</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,106 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
|
||||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400&display=swap">
|
|
||||||
<link rel="stylesheet"
|
|
||||||
href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
background-color: #f0f0f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
font-weight: 300;
|
|
||||||
}
|
|
||||||
|
|
||||||
a, a:hover {
|
|
||||||
text-decoration: none !important;
|
|
||||||
color: #626976;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
padding: 3rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
color: #626976;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
width: 70%;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
table thead tr {
|
|
||||||
height: 60px;
|
|
||||||
background: #626976;
|
|
||||||
}
|
|
||||||
|
|
||||||
table thead tr th {
|
|
||||||
font-size: 18px;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
table tbody tr {
|
|
||||||
height: 50px;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
}
|
|
||||||
|
|
||||||
tbody tr:nth-child(even) {
|
|
||||||
background-color: #fdfdfd;
|
|
||||||
}
|
|
||||||
|
|
||||||
table th, table td {
|
|
||||||
text-align: left;
|
|
||||||
padding: 0 8px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<title>Documentation</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="content">
|
|
||||||
<div class="container">
|
|
||||||
<h2 class="mb-5">{{TITLE}}</h2>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li><a href="/documentation/handbook">{{ITEM_HANDBOOK}}</a></li>
|
|
||||||
<li><a href="/documentation/gachamapping">{{ITEM_GACHA_MAPPING}}</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<footer>
|
|
||||||
<div class="copyright">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<span>Template by BecodReyes. All rights reserved.</span>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<ul style="float:right">
|
|
||||||
<li class="list-inline-item">
|
|
||||||
<a href="https://github.com/Grasscutters/Grasscutter">Github</a>
|
|
||||||
</li>
|
|
||||||
<li class="list-inline-item">·</li>
|
|
||||||
<li class="list-inline-item">
|
|
||||||
<a href="https://github.com/Grasscutters/Grasscutter/blob/stable/LICENSE">License</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -14,7 +14,6 @@ import emu.grasscutter.server.http.HttpServer;
|
|||||||
import emu.grasscutter.server.http.dispatch.DispatchHandler;
|
import emu.grasscutter.server.http.dispatch.DispatchHandler;
|
||||||
import emu.grasscutter.server.http.handlers.*;
|
import emu.grasscutter.server.http.handlers.*;
|
||||||
import emu.grasscutter.server.http.dispatch.RegionHandler;
|
import emu.grasscutter.server.http.dispatch.RegionHandler;
|
||||||
import emu.grasscutter.server.http.documentation.DocumentationServerHandler;
|
|
||||||
import emu.grasscutter.utils.ConfigContainer;
|
import emu.grasscutter.utils.ConfigContainer;
|
||||||
import emu.grasscutter.utils.Utils;
|
import emu.grasscutter.utils.Utils;
|
||||||
import org.jline.reader.EndOfFileException;
|
import org.jline.reader.EndOfFileException;
|
||||||
@ -130,7 +129,6 @@ public final class Grasscutter {
|
|||||||
httpServer.addRouter(AnnouncementsHandler.class);
|
httpServer.addRouter(AnnouncementsHandler.class);
|
||||||
httpServer.addRouter(DispatchHandler.class);
|
httpServer.addRouter(DispatchHandler.class);
|
||||||
httpServer.addRouter(GachaHandler.class);
|
httpServer.addRouter(GachaHandler.class);
|
||||||
httpServer.addRouter(DocumentationServerHandler.class);
|
|
||||||
|
|
||||||
// TODO: find a better place?
|
// TODO: find a better place?
|
||||||
StaminaManager.initialize();
|
StaminaManager.initialize();
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
package emu.grasscutter.server.http.documentation;
|
|
||||||
|
|
||||||
import express.http.Request;
|
|
||||||
import express.http.Response;
|
|
||||||
|
|
||||||
interface DocumentationHandler {
|
|
||||||
|
|
||||||
void handle(Request request, Response response);
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
package emu.grasscutter.server.http.documentation;
|
|
||||||
|
|
||||||
import emu.grasscutter.server.http.Router;
|
|
||||||
import express.Express;
|
|
||||||
import io.javalin.Javalin;
|
|
||||||
|
|
||||||
public final class DocumentationServerHandler implements Router {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void applyRoutes(Express express, Javalin handle) {
|
|
||||||
final RootRequestHandler root = new RootRequestHandler();
|
|
||||||
final HandbookRequestHandler handbook = new HandbookRequestHandler();
|
|
||||||
final GachaMappingRequestHandler gachaMapping = new GachaMappingRequestHandler();
|
|
||||||
|
|
||||||
express.get("/documentation/handbook", handbook::handle);
|
|
||||||
express.get("/documentation/gachamapping", gachaMapping::handle);
|
|
||||||
express.get("/documentation", root::handle);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,155 +0,0 @@
|
|||||||
package emu.grasscutter.server.http.documentation;
|
|
||||||
|
|
||||||
import static emu.grasscutter.Configuration.RESOURCE;
|
|
||||||
|
|
||||||
import com.google.gson.reflect.TypeToken;
|
|
||||||
import emu.grasscutter.Grasscutter;
|
|
||||||
import emu.grasscutter.data.GameData;
|
|
||||||
import emu.grasscutter.data.ResourceLoader;
|
|
||||||
import emu.grasscutter.data.def.AvatarData;
|
|
||||||
import emu.grasscutter.data.def.ItemData;
|
|
||||||
import emu.grasscutter.tools.Tools;
|
|
||||||
import emu.grasscutter.utils.Utils;
|
|
||||||
import express.http.Request;
|
|
||||||
import express.http.Response;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
final class GachaMappingRequestHandler implements DocumentationHandler {
|
|
||||||
|
|
||||||
private Map<Long, String> map;
|
|
||||||
|
|
||||||
GachaMappingRequestHandler() {
|
|
||||||
ResourceLoader.loadResources();
|
|
||||||
final String textMapFile = "TextMap/TextMap" + Tools.getLanguageOption() + ".json";
|
|
||||||
try (InputStreamReader fileReader = new InputStreamReader(new FileInputStream(
|
|
||||||
Utils.toFilePath(RESOURCE(textMapFile))), StandardCharsets.UTF_8)) {
|
|
||||||
map = Grasscutter.getGsonFactory().fromJson(fileReader,
|
|
||||||
new TypeToken<Map<Long, String>>() {
|
|
||||||
}.getType());
|
|
||||||
} catch (IOException e) {
|
|
||||||
Grasscutter.getLogger().warn("Resource does not exist: " + textMapFile);
|
|
||||||
map = new HashMap<>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handle(Request request, Response response) {
|
|
||||||
if (map.isEmpty()) {
|
|
||||||
response.status(500);
|
|
||||||
} else {
|
|
||||||
response.set("Content-Type", "application/json")
|
|
||||||
.ctx()
|
|
||||||
.result(createGachaMappingJson());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String createGachaMappingJson() {
|
|
||||||
List<Integer> list;
|
|
||||||
|
|
||||||
final StringBuilder sb = new StringBuilder();
|
|
||||||
list = new ArrayList<>(GameData.getAvatarDataMap().keySet());
|
|
||||||
Collections.sort(list);
|
|
||||||
|
|
||||||
final String newLine = System.lineSeparator();
|
|
||||||
|
|
||||||
// if the user made choices for language, I assume it's okay to assign his/her selected language to "en-us"
|
|
||||||
// since it's the fallback language and there will be no difference in the gacha record page.
|
|
||||||
// The enduser can still modify the `gacha_mappings.js` directly to enable multilingual for the gacha record system.
|
|
||||||
sb.append("{").append(newLine);
|
|
||||||
|
|
||||||
// Avatars
|
|
||||||
boolean first = true;
|
|
||||||
for (Integer id : list) {
|
|
||||||
AvatarData data = GameData.getAvatarDataMap().get(id);
|
|
||||||
int avatarID = data.getId();
|
|
||||||
if (avatarID >= 11000000) { // skip test avatar
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (first) { // skip adding comma for the first element
|
|
||||||
first = false;
|
|
||||||
} else {
|
|
||||||
sb.append(",");
|
|
||||||
}
|
|
||||||
String color;
|
|
||||||
switch (data.getQualityType()) {
|
|
||||||
case "QUALITY_PURPLE":
|
|
||||||
color = "purple";
|
|
||||||
break;
|
|
||||||
case "QUALITY_ORANGE":
|
|
||||||
color = "yellow";
|
|
||||||
break;
|
|
||||||
case "QUALITY_BLUE":
|
|
||||||
default:
|
|
||||||
color = "blue";
|
|
||||||
}
|
|
||||||
// Got the magic number 4233146695 from manually search in the json file
|
|
||||||
sb.append("\"")
|
|
||||||
.append(avatarID % 1000 + 1000)
|
|
||||||
.append("\" : [\"")
|
|
||||||
.append(map.get(data.getNameTextMapHash()))
|
|
||||||
.append("(")
|
|
||||||
.append(map.get(4233146695L))
|
|
||||||
.append(")\", \"")
|
|
||||||
.append(color)
|
|
||||||
.append("\"]")
|
|
||||||
.append(newLine);
|
|
||||||
}
|
|
||||||
|
|
||||||
list = new ArrayList<>(GameData.getItemDataMap().keySet());
|
|
||||||
Collections.sort(list);
|
|
||||||
|
|
||||||
// Weapons
|
|
||||||
for (Integer id : list) {
|
|
||||||
ItemData data = GameData.getItemDataMap().get(id);
|
|
||||||
if (data.getId() <= 11101 || data.getId() >= 20000) {
|
|
||||||
continue; //skip non weapon items
|
|
||||||
}
|
|
||||||
String color;
|
|
||||||
|
|
||||||
switch (data.getRankLevel()) {
|
|
||||||
case 3:
|
|
||||||
color = "blue";
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
color = "purple";
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
color = "yellow";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
continue; // skip unnecessary entries
|
|
||||||
}
|
|
||||||
|
|
||||||
// Got the magic number 4231343903 from manually search in the json file
|
|
||||||
|
|
||||||
sb.append(",\"")
|
|
||||||
.append(data.getId())
|
|
||||||
.append("\" : [\"")
|
|
||||||
.append(map.get(data.getNameTextMapHash()).replaceAll("\"", ""))
|
|
||||||
.append("(")
|
|
||||||
.append(map.get(4231343903L))
|
|
||||||
.append(")\",\"")
|
|
||||||
.append(color)
|
|
||||||
.append("\"]")
|
|
||||||
.append(newLine);
|
|
||||||
}
|
|
||||||
sb.append(",\"200\": \"")
|
|
||||||
.append(map.get(332935371L))
|
|
||||||
.append("\", \"301\": \"")
|
|
||||||
.append(map.get(2272170627L))
|
|
||||||
.append("\", \"302\": \"")
|
|
||||||
.append(map.get(2864268523L))
|
|
||||||
.append("\"")
|
|
||||||
.append("}\n}")
|
|
||||||
.append(newLine);
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,127 +0,0 @@
|
|||||||
package emu.grasscutter.server.http.documentation;
|
|
||||||
|
|
||||||
import static emu.grasscutter.Configuration.DATA;
|
|
||||||
import static emu.grasscutter.Configuration.RESOURCE;
|
|
||||||
import static emu.grasscutter.utils.Language.translate;
|
|
||||||
|
|
||||||
import com.google.gson.reflect.TypeToken;
|
|
||||||
import emu.grasscutter.Grasscutter;
|
|
||||||
import emu.grasscutter.command.CommandMap;
|
|
||||||
import emu.grasscutter.data.GameData;
|
|
||||||
import emu.grasscutter.data.ResourceLoader;
|
|
||||||
import emu.grasscutter.data.def.AvatarData;
|
|
||||||
import emu.grasscutter.data.def.ItemData;
|
|
||||||
import emu.grasscutter.data.def.MonsterData;
|
|
||||||
import emu.grasscutter.data.def.SceneData;
|
|
||||||
import emu.grasscutter.tools.Tools;
|
|
||||||
import emu.grasscutter.utils.FileUtils;
|
|
||||||
import emu.grasscutter.utils.Utils;
|
|
||||||
import express.http.Request;
|
|
||||||
import express.http.Response;
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
final class HandbookRequestHandler implements DocumentationHandler {
|
|
||||||
|
|
||||||
private final String template;
|
|
||||||
private Map<Long, String> map;
|
|
||||||
|
|
||||||
|
|
||||||
public HandbookRequestHandler() {
|
|
||||||
ResourceLoader.loadResources();
|
|
||||||
final File templateFile = new File(Utils.toFilePath(DATA("documentation/handbook.html")));
|
|
||||||
if (templateFile.exists()) {
|
|
||||||
template = new String(FileUtils.read(templateFile), StandardCharsets.UTF_8);
|
|
||||||
} else {
|
|
||||||
Grasscutter.getLogger().warn("File does not exist: " + templateFile);
|
|
||||||
template = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
final String textMapFile = "TextMap/TextMap" + Tools.getLanguageOption() + ".json";
|
|
||||||
try (InputStreamReader fileReader = new InputStreamReader(new FileInputStream(
|
|
||||||
Utils.toFilePath(RESOURCE(textMapFile))), StandardCharsets.UTF_8)) {
|
|
||||||
map = Grasscutter.getGsonFactory()
|
|
||||||
.fromJson(fileReader, new TypeToken<Map<Long, String>>() {
|
|
||||||
}.getType());
|
|
||||||
} catch (IOException e) {
|
|
||||||
Grasscutter.getLogger().warn("Resource does not exist: " + textMapFile);
|
|
||||||
map = new HashMap<>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handle(Request request, Response response) {
|
|
||||||
if (template == null) {
|
|
||||||
response.status(500);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final CommandMap cmdMap = new CommandMap(true);
|
|
||||||
final Int2ObjectMap<AvatarData> avatarMap = GameData.getAvatarDataMap();
|
|
||||||
final Int2ObjectMap<ItemData> itemMap = GameData.getItemDataMap();
|
|
||||||
final Int2ObjectMap<SceneData> sceneMap = GameData.getSceneDataMap();
|
|
||||||
final Int2ObjectMap<MonsterData> monsterMap = GameData.getMonsterDataMap();
|
|
||||||
|
|
||||||
// Add translated title etc. to the page.
|
|
||||||
String content = template.replace("{{TITLE}}", translate("documentation.handbook.title"))
|
|
||||||
.replace("{{TITLE_COMMANDS}}", translate("documentation.handbook.title_commands"))
|
|
||||||
.replace("{{TITLE_AVATARS}}", translate("documentation.handbook.title_avatars"))
|
|
||||||
.replace("{{TITLE_ITEMS}}", translate("documentation.handbook.title_items"))
|
|
||||||
.replace("{{TITLE_SCENES}}", translate("documentation.handbook.title_scenes"))
|
|
||||||
.replace("{{TITLE_MONSTERS}}", translate("documentation.handbook.title_monsters"))
|
|
||||||
.replace("{{HEADER_ID}}", translate("documentation.handbook.header_id"))
|
|
||||||
.replace("{{HEADER_COMMAND}}", translate("documentation.handbook.header_command"))
|
|
||||||
.replace("{{HEADER_DESCRIPTION}}",
|
|
||||||
translate("documentation.handbook.header_description"))
|
|
||||||
.replace("{{HEADER_AVATAR}}", translate("documentation.handbook.header_avatar"))
|
|
||||||
.replace("{{HEADER_ITEM}}", translate("documentation.handbook.header_item"))
|
|
||||||
.replace("{{HEADER_SCENE}}", translate("documentation.handbook.header_scene"))
|
|
||||||
.replace("{{HEADER_MONSTER}}", translate("documentation.handbook.header_monster"))
|
|
||||||
// Commands table
|
|
||||||
.replace("{{COMMANDS_TABLE}}", cmdMap.getAnnotationsAsList()
|
|
||||||
.stream()
|
|
||||||
.map(cmd -> "<tr><td><code>" + cmd.label() + "</code></td><td>" +
|
|
||||||
cmd.description() + "</td></tr>")
|
|
||||||
.collect(Collectors.joining("\n")))
|
|
||||||
// Avatars table
|
|
||||||
.replace("{{AVATARS_TABLE}}", GameData.getAvatarDataMap().keySet()
|
|
||||||
.intStream()
|
|
||||||
.sorted()
|
|
||||||
.mapToObj(avatarMap::get)
|
|
||||||
.map(data -> "<tr><td><code>" + data.getId() + "</code></td><td>" +
|
|
||||||
map.get(data.getNameTextMapHash()) + "</td></tr>")
|
|
||||||
.collect(Collectors.joining("\n")))
|
|
||||||
// Items table
|
|
||||||
.replace("{{ITEMS_TABLE}}", GameData.getItemDataMap().keySet()
|
|
||||||
.intStream()
|
|
||||||
.sorted()
|
|
||||||
.mapToObj(itemMap::get)
|
|
||||||
.map(data -> "<tr><td><code>" + data.getId() + "</code></td><td>" +
|
|
||||||
map.get(data.getNameTextMapHash()) + "</td></tr>")
|
|
||||||
.collect(Collectors.joining("\n")))
|
|
||||||
// Scenes table
|
|
||||||
.replace("{{SCENES_TABLE}}", GameData.getSceneDataMap().keySet()
|
|
||||||
.intStream()
|
|
||||||
.sorted()
|
|
||||||
.mapToObj(sceneMap::get)
|
|
||||||
.map(data -> "<tr><td><code>" + data.getId() + "</code></td><td>" +
|
|
||||||
data.getScriptData() + "</td></tr>")
|
|
||||||
.collect(Collectors.joining("\n")))
|
|
||||||
.replace("{{MONSTERS_TABLE}}", GameData.getMonsterDataMap().keySet()
|
|
||||||
.intStream()
|
|
||||||
.sorted()
|
|
||||||
.mapToObj(monsterMap::get)
|
|
||||||
.map(data -> "<tr><td><code>" + data.getId() + "</code></td><td>" +
|
|
||||||
map.get(data.getNameTextMapHash()) + "</td></tr>")
|
|
||||||
.collect(Collectors.joining("\n")));
|
|
||||||
|
|
||||||
response.send(content);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
package emu.grasscutter.server.http.documentation;
|
|
||||||
|
|
||||||
import static emu.grasscutter.Configuration.DATA;
|
|
||||||
import static emu.grasscutter.utils.Language.translate;
|
|
||||||
|
|
||||||
import emu.grasscutter.Grasscutter;
|
|
||||||
import emu.grasscutter.data.ResourceLoader;
|
|
||||||
import emu.grasscutter.utils.FileUtils;
|
|
||||||
import emu.grasscutter.utils.Utils;
|
|
||||||
import express.http.Request;
|
|
||||||
import express.http.Response;
|
|
||||||
import java.io.File;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
|
|
||||||
final class RootRequestHandler implements DocumentationHandler {
|
|
||||||
|
|
||||||
private final String template;
|
|
||||||
|
|
||||||
public RootRequestHandler() {
|
|
||||||
ResourceLoader.loadResources();
|
|
||||||
final File templateFile = new File(Utils.toFilePath(DATA("documentation/index.html")));
|
|
||||||
if (templateFile.exists()) {
|
|
||||||
template = new String(FileUtils.read(templateFile), StandardCharsets.UTF_8);
|
|
||||||
} else {
|
|
||||||
Grasscutter.getLogger().warn("File does not exist: " + templateFile);
|
|
||||||
template = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handle(Request request, Response response) {
|
|
||||||
if (template == null) {
|
|
||||||
response.status(500);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String content = template.replace("{{TITLE}}", translate("documentation.index.title"))
|
|
||||||
.replace("{{ITEM_HANDBOOK}}", translate("documentation.index.handbook"))
|
|
||||||
.replace("{{ITEM_GACHA_MAPPING}}", translate("documentation.index.gacha_mapping"));
|
|
||||||
response.send(content);
|
|
||||||
}
|
|
||||||
}
|
|
@ -386,27 +386,5 @@
|
|||||||
"available_three_stars": "Available 3-star Items",
|
"available_three_stars": "Available 3-star Items",
|
||||||
"template_missing": "data/gacha_details.html is missing."
|
"template_missing": "data/gacha_details.html is missing."
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"documentation": {
|
|
||||||
"handbook": {
|
|
||||||
"title": "GM Handbook",
|
|
||||||
"title_commands": "Commands",
|
|
||||||
"title_avatars": "Avatars",
|
|
||||||
"title_items": "Items",
|
|
||||||
"title_scenes": "Scenes",
|
|
||||||
"title_monsters": "Monsters",
|
|
||||||
"header_id": "Id",
|
|
||||||
"header_command": "Command",
|
|
||||||
"header_description": "Description",
|
|
||||||
"header_avatar": "Avatar",
|
|
||||||
"header_item": "Item",
|
|
||||||
"header_scene": "Scene",
|
|
||||||
"header_monster": "Monster"
|
|
||||||
},
|
|
||||||
"index": {
|
|
||||||
"title": "Documentation",
|
|
||||||
"handbook": "GM Handbook",
|
|
||||||
"gacha_mapping": "Gacha mapping JSON"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -221,12 +221,12 @@
|
|||||||
"description": "获取所在位置"
|
"description": "获取所在位置"
|
||||||
},
|
},
|
||||||
"quest": {
|
"quest": {
|
||||||
|
"description": "添加或完成任务",
|
||||||
"usage": "quest <add|finish> [任务ID]",
|
"usage": "quest <add|finish> [任务ID]",
|
||||||
"added": "已添加任务 %s。",
|
"added": "已添加任务 %s。",
|
||||||
"finished": "已完成任务 %s。",
|
"finished": "已完成任务 %s。",
|
||||||
"not_found": "此任务不存在。",
|
"not_found": "此任务不存在。",
|
||||||
"invalid_id": "无效的任务ID。",
|
"invalid_id": "无效的任务ID。"
|
||||||
"description": "添加或完成任务"
|
|
||||||
},
|
},
|
||||||
"reload": {
|
"reload": {
|
||||||
"reload_start": "正在重载配置文件和数据。",
|
"reload_start": "正在重载配置文件和数据。",
|
||||||
@ -323,17 +323,17 @@
|
|||||||
"description": "设置当前角色的天赋等级"
|
"description": "设置当前角色的天赋等级"
|
||||||
},
|
},
|
||||||
"team": {
|
"team": {
|
||||||
"usage": "用法:team <add|remove|set> [角色ID,...] [索引|first|last|索引-索引,...]",
|
"usage": "用法: team <add|remove|set> [角色ID,...] [索引|first|last|索引-索引,...]",
|
||||||
"invalid_usage": "无效用法。",
|
"invalid_usage": "无效用法。",
|
||||||
"add_usage": "用法 (add):team add <角色ID,...> [索引]",
|
"add_usage": "用法 (add): team add <角色ID,...> [索引]",
|
||||||
"invalid_index": "无效索引。",
|
"invalid_index": "无效索引。",
|
||||||
"add_too_much": "服务端仅允许你队伍里至多有 %d 名角色。",
|
"add_too_much": "服务端仅允许你队伍里至多有 %d 名角色。",
|
||||||
"failed_to_add_avatar": "无法根据ID %s 添加角色。",
|
"failed_to_add_avatar": "无法根据ID %s 添加角色。",
|
||||||
"remove_usage": "用法 (remove):team remove <索引|first|last|索引-索引,...>",
|
"remove_usage": "用法 (remove): team remove <索引|first|last|索引-索引,...>",
|
||||||
"failed_to_parse_index": "无法解析索引:%s",
|
"failed_parse_index": "无法解析索引:%s",
|
||||||
"remove_too_much": "你不能删除那么多角色,你的队伍列表将会变空。",
|
"remove_too_much": "你不能删除那么多角色,你的队伍列表将会变空。",
|
||||||
"ignore_index": "忽略的索引列表:%s",
|
"ignore_index": "忽略的索引列表:%s",
|
||||||
"set_usage": "用法 (set):team set <索引> <角色ID>",
|
"set_usage": "用法 (set): team set <索引> <角色ID>",
|
||||||
"index_out_of_range": "你指定的索引超出了范围。",
|
"index_out_of_range": "你指定的索引超出了范围。",
|
||||||
"failed_parse_avatar_id": "无法解析的角色ID:%s",
|
"failed_parse_avatar_id": "无法解析的角色ID:%s",
|
||||||
"avatar_already_in_team": "角色已经在你的队伍中了。",
|
"avatar_already_in_team": "角色已经在你的队伍中了。",
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
<file>logs/latest.log</file>
|
<file>logs/latest.log</file>
|
||||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||||
<fileNamePattern>logs/log.%d{yyyy-MM-dd}_%d{HH}.log.gz</fileNamePattern>
|
<fileNamePattern>logs/log.%d{yyyy-MM-dd}_%d{HH}.log.tar.gz</fileNamePattern>
|
||||||
<maxHistory>24</maxHistory>
|
<maxHistory>24</maxHistory>
|
||||||
</rollingPolicy>
|
</rollingPolicy>
|
||||||
<encoder>
|
<encoder>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user