mirror of
https://github.com/Melledy/Grasscutter.git
synced 2024-11-26 15:39:37 +00:00
Add quest data dumping for the handbook
This commit is contained in:
parent
a377fe2107
commit
919f533ed7
@ -2,10 +2,12 @@
|
|||||||
Use Grasscutter's dumpers to generate the data to put here.
|
Use Grasscutter's dumpers to generate the data to put here.
|
||||||
|
|
||||||
## Files Required
|
## Files Required
|
||||||
|
- `mainquests.csv'
|
||||||
- `commands.json`
|
- `commands.json`
|
||||||
- `entities.csv`
|
- `entities.csv`
|
||||||
- `avatars.csv`
|
- `avatars.csv`
|
||||||
- `scenes.csv`
|
- `scenes.csv`
|
||||||
|
- `quests.csv`
|
||||||
- `items.csv`
|
- `items.csv`
|
||||||
|
|
||||||
# Item Icon Notes
|
# Item Icon Notes
|
||||||
|
@ -1,17 +1,21 @@
|
|||||||
|
import mainQuests from "@data/mainquests.csv";
|
||||||
import commands from "@data/commands.json";
|
import commands from "@data/commands.json";
|
||||||
import entities from "@data/entities.csv";
|
import entities from "@data/entities.csv";
|
||||||
import avatars from "@data/avatars.csv";
|
import avatars from "@data/avatars.csv";
|
||||||
import scenes from "@data/scenes.csv";
|
import scenes from "@data/scenes.csv";
|
||||||
|
import quests from "@data/quests.csv";
|
||||||
import items from "@data/items.csv";
|
import items from "@data/items.csv";
|
||||||
|
|
||||||
import { Quality, ItemType, ItemCategory, SceneType } from "@backend/types";
|
import { Quality, ItemType, ItemCategory, SceneType } from "@backend/types";
|
||||||
import type { Command, Avatar, Item, Scene, Entity } from "@backend/types";
|
import type { MainQuest, Command, Avatar, Item, Scene, Entity, Quest } from "@backend/types";
|
||||||
|
|
||||||
import { inRange } from "@app/utils";
|
import { inRange } from "@app/utils";
|
||||||
|
|
||||||
type AvatarDump = { [key: number]: Avatar };
|
type AvatarDump = { [key: number]: Avatar };
|
||||||
type CommandDump = { [key: string]: Command };
|
type CommandDump = { [key: string]: Command };
|
||||||
type TaggedItems = { [key: number]: Item[] };
|
type TaggedItems = { [key: number]: Item[] };
|
||||||
|
type QuestDump = { [key: number]: Quest };
|
||||||
|
type MainQuestDump = { [key: number]: MainQuest };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see {@file src/handbook/data/README.md}
|
* @see {@file src/handbook/data/README.md}
|
||||||
@ -27,6 +31,8 @@ export const sortedItems: TaggedItems = {
|
|||||||
[ItemCategory.Miscellaneous]: []
|
[ItemCategory.Miscellaneous]: []
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export let allMainQuests: MainQuestDump = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setup function for this file.
|
* Setup function for this file.
|
||||||
* Sorts all items into their respective categories.
|
* Sorts all items into their respective categories.
|
||||||
@ -57,6 +63,8 @@ export function setup(): void {
|
|||||||
sortedItems[ItemCategory.Avatar].push(item);
|
sortedItems[ItemCategory.Avatar].push(item);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
allMainQuests = getMainQuests();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -148,3 +156,56 @@ export function getItems(): Item[] {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches and casts all quests in the file.
|
||||||
|
*/
|
||||||
|
export function getQuests(): QuestDump {
|
||||||
|
const map: QuestDump = {};
|
||||||
|
quests.forEach((quest: Quest) => {
|
||||||
|
quest.description = quest.description
|
||||||
|
.replaceAll("\\", ",");
|
||||||
|
map[quest.id] = quest;
|
||||||
|
});
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches and lists all the quests in the file.
|
||||||
|
*/
|
||||||
|
export function listQuests(): Quest[] {
|
||||||
|
return Object.values(getQuests())
|
||||||
|
.sort((a, b) => a.id - b.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches and casts all quests in the file.
|
||||||
|
*/
|
||||||
|
export function getMainQuests(): MainQuestDump {
|
||||||
|
const map: MainQuestDump = {};
|
||||||
|
mainQuests.forEach((quest: MainQuest) => {
|
||||||
|
quest.title = quest.title
|
||||||
|
.replaceAll("\\", ",");
|
||||||
|
map[quest.id] = quest;
|
||||||
|
});
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches and lists all the quests in the file.
|
||||||
|
*/
|
||||||
|
export function listMainQuests(): MainQuestDump[] {
|
||||||
|
return Object.values(allMainQuests)
|
||||||
|
.sort((a, b) => a.id - b.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches a quest by its ID.
|
||||||
|
*
|
||||||
|
* @param quest The quest ID.
|
||||||
|
*/
|
||||||
|
export function getMainQuestFor(quest: Quest): MainQuest {
|
||||||
|
return allMainQuests[quest.mainId];
|
||||||
|
}
|
||||||
|
@ -2,6 +2,11 @@ export type Page = "Home" | "Commands" | "Avatars" | "Items" | "Entities" | "Sce
|
|||||||
export type Overlays = "None" | "ServerSettings";
|
export type Overlays = "None" | "ServerSettings";
|
||||||
export type Days = "Sunday" | "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday" | "Saturday";
|
export type Days = "Sunday" | "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday" | "Saturday";
|
||||||
|
|
||||||
|
export type MainQuest = {
|
||||||
|
id: number;
|
||||||
|
title: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type Command = {
|
export type Command = {
|
||||||
name: string[];
|
name: string[];
|
||||||
description: string;
|
description: string;
|
||||||
@ -36,6 +41,12 @@ export type Entity = {
|
|||||||
internal: string;
|
internal: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type Quest = {
|
||||||
|
id: number;
|
||||||
|
description: string;
|
||||||
|
mainId: number;
|
||||||
|
};
|
||||||
|
|
||||||
// Exported from Project Amber.
|
// Exported from Project Amber.
|
||||||
export type ItemInfo = {
|
export type ItemInfo = {
|
||||||
response: number | 200 | 404;
|
response: number | 200 | 404;
|
||||||
|
@ -4,6 +4,6 @@
|
|||||||
overflow-x: scroll;
|
overflow-x: scroll;
|
||||||
|
|
||||||
p {
|
p {
|
||||||
color: black;
|
color: white;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,14 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { listCommands, listAvatars, getItems, getEntities, getScenes } from "@backend/data";
|
import {
|
||||||
|
listCommands,
|
||||||
|
listAvatars,
|
||||||
|
getItems,
|
||||||
|
getEntities,
|
||||||
|
getScenes,
|
||||||
|
listQuests,
|
||||||
|
getMainQuestFor
|
||||||
|
} from "@backend/data";
|
||||||
|
|
||||||
import "@css/views/PlainText.scss";
|
import "@css/views/PlainText.scss";
|
||||||
|
|
||||||
@ -13,7 +21,7 @@ class PlainText extends React.PureComponent {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{listCommands().map((command) => (
|
{listCommands().map((command) => (
|
||||||
<p>{`${command.name[0]} : ${command.description}`}</p>
|
<p key={command.name[0]}>{`${command.name[0]} : ${command.description}`}</p>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@ -29,7 +37,7 @@ class PlainText extends React.PureComponent {
|
|||||||
{listAvatars()
|
{listAvatars()
|
||||||
.sort((a, b) => a.id - b.id)
|
.sort((a, b) => a.id - b.id)
|
||||||
.map((avatar) => (
|
.map((avatar) => (
|
||||||
<p>{`${avatar.id} : ${avatar.name}`}</p>
|
<p key={avatar.id}>{`${avatar.id} : ${avatar.name}`}</p>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@ -45,7 +53,7 @@ class PlainText extends React.PureComponent {
|
|||||||
{getItems()
|
{getItems()
|
||||||
.sort((a, b) => a.id - b.id)
|
.sort((a, b) => a.id - b.id)
|
||||||
.map((item) => (
|
.map((item) => (
|
||||||
<p>{`${item.id} : ${item.name}`}</p>
|
<p key={item.id}>{`${item.id} : ${item.name}`}</p>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@ -61,7 +69,7 @@ class PlainText extends React.PureComponent {
|
|||||||
{getEntities()
|
{getEntities()
|
||||||
.sort((a, b) => a.id - b.id)
|
.sort((a, b) => a.id - b.id)
|
||||||
.map((entity) => (
|
.map((entity) => (
|
||||||
<p>{`${entity.id} : ${entity.name}`}</p>
|
<p key={entity.id}>{`${entity.id} : ${entity.name}`}</p>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@ -77,7 +85,23 @@ class PlainText extends React.PureComponent {
|
|||||||
{getScenes()
|
{getScenes()
|
||||||
.sort((a, b) => a.id - b.id)
|
.sort((a, b) => a.id - b.id)
|
||||||
.map((scene) => (
|
.map((scene) => (
|
||||||
<p>{`${scene.id} : ${scene.identifier} [${scene.type}]`}</p>
|
<p key={scene.id}>{`${scene.id} : ${scene.identifier} [${scene.type}]`}</p>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a paragraph of quests.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private getQuests(): React.ReactNode {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{listQuests()
|
||||||
|
.sort((a, b) => a.id - b.id)
|
||||||
|
.map((quest) => (
|
||||||
|
<p key={quest.id}>{`${quest.id} : ${getMainQuestFor(quest)?.title ?? "Unknown"} - ${quest.description}`}</p>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@ -129,6 +153,14 @@ class PlainText extends React.PureComponent {
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
{this.getScenes()}
|
{this.getScenes()}
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
// Quests
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{this.getQuests()}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,8 @@ import emu.grasscutter.game.inventory.ItemType;
|
|||||||
import emu.grasscutter.game.props.SceneType;
|
import emu.grasscutter.game.props.SceneType;
|
||||||
import emu.grasscutter.utils.JsonUtils;
|
import emu.grasscutter.utils.JsonUtils;
|
||||||
import emu.grasscutter.utils.Language;
|
import emu.grasscutter.utils.Language;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
@ -17,7 +19,6 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
|
|
||||||
public interface Dumpers {
|
public interface Dumpers {
|
||||||
// See `src/handbook/data/README.md` for attributions.
|
// See `src/handbook/data/README.md` for attributions.
|
||||||
@ -53,6 +54,16 @@ public interface Dumpers {
|
|||||||
.collect(Collectors.joining("\n"));
|
.collect(Collectors.joining("\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes the dump into comma separated values.
|
||||||
|
*
|
||||||
|
* @param dump The dump to encode.
|
||||||
|
* @return The encoded dump.
|
||||||
|
*/
|
||||||
|
private static String miniEncode(Map<Integer, ?> dump, String... headers) {
|
||||||
|
return String.join(",", headers) + "\n" + Dumpers.miniEncode(dump);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dumps all commands to a JSON file.
|
* Dumps all commands to a JSON file.
|
||||||
*
|
*
|
||||||
@ -104,7 +115,7 @@ public interface Dumpers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dumps all avatars to a JSON file.
|
* Dumps all avatars to a CSV file.
|
||||||
*
|
*
|
||||||
* @param locale The language to dump the avatars in.
|
* @param locale The language to dump the avatars in.
|
||||||
*/
|
*/
|
||||||
@ -145,7 +156,7 @@ public interface Dumpers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dumps all items to a JSON file.
|
* Dumps all items to a CSVv file.
|
||||||
*
|
*
|
||||||
* @param locale The language to dump the items in.
|
* @param locale The language to dump the items in.
|
||||||
*/
|
*/
|
||||||
@ -195,7 +206,7 @@ public interface Dumpers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Dumps all scenes to a JSON file. */
|
/** Dumps all scenes to a CSV file. */
|
||||||
static void dumpScenes() {
|
static void dumpScenes() {
|
||||||
// Reload resources.
|
// Reload resources.
|
||||||
ResourceLoader.loadAll();
|
ResourceLoader.loadAll();
|
||||||
@ -223,7 +234,7 @@ public interface Dumpers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dumps all entities to a JSON file.
|
* Dumps all entities to a CSV file.
|
||||||
*
|
*
|
||||||
* @param locale The language to dump the entities in.
|
* @param locale The language to dump the entities in.
|
||||||
*/
|
*/
|
||||||
@ -261,6 +272,68 @@ public interface Dumpers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dumps all quests to a JSON file.
|
||||||
|
*
|
||||||
|
* @param locale The language to dump the quests in.
|
||||||
|
*/
|
||||||
|
static void dumpQuests(String locale) {
|
||||||
|
// Reload resources.
|
||||||
|
ResourceLoader.loadAll();
|
||||||
|
Language.loadTextMaps();
|
||||||
|
|
||||||
|
// Convert all known quests to a quest map.
|
||||||
|
var dump = new HashMap<Integer, QuestInfo>();
|
||||||
|
GameData.getQuestDataMap().forEach((id, quest) -> {
|
||||||
|
var langHash = quest.getDescTextMapHash();
|
||||||
|
dump.put(id, new QuestInfo(
|
||||||
|
langHash == 0 ? "Unknown" :
|
||||||
|
Language.getTextMapKey(langHash).get(locale)
|
||||||
|
.replaceAll(",", "\\\\"),
|
||||||
|
quest.getMainId()
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Convert all known main quests into a quest map.
|
||||||
|
var mainDump = new HashMap<Integer, MainQuestInfo>();
|
||||||
|
GameData.getMainQuestDataMap().forEach((id, mainQuest) -> {
|
||||||
|
var langHash = mainQuest.getTitleTextMapHash();
|
||||||
|
mainDump.put(id, new MainQuestInfo(
|
||||||
|
langHash == 0 ? "Unknown" :
|
||||||
|
Language.getTextMapKey(langHash).get(locale)
|
||||||
|
.replaceAll(",", "\\\\")
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Create a file for the dump.
|
||||||
|
var file = new File("quests.csv");
|
||||||
|
if (file.exists() && !file.delete()) throw new RuntimeException("Failed to delete file.");
|
||||||
|
if (!file.exists() && !file.createNewFile())
|
||||||
|
throw new RuntimeException("Failed to create file.");
|
||||||
|
|
||||||
|
// Write the dump to the file.
|
||||||
|
Files.writeString(file.toPath(), Dumpers.miniEncode(dump,
|
||||||
|
"id", "description", "mainId"));
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
throw new RuntimeException("Failed to write to file.");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Create a file for the dump.
|
||||||
|
var file = new File("mainquests.csv");
|
||||||
|
if (file.exists() && !file.delete()) throw new RuntimeException("Failed to delete file.");
|
||||||
|
if (!file.exists() && !file.createNewFile())
|
||||||
|
throw new RuntimeException("Failed to create file.");
|
||||||
|
|
||||||
|
// Write the dump to the file.
|
||||||
|
Files.writeString(file.toPath(), Dumpers.miniEncode(mainDump,
|
||||||
|
"id", "title"));
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
throw new RuntimeException("Failed to write to file.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
class CommandInfo {
|
class CommandInfo {
|
||||||
public List<String> name;
|
public List<String> name;
|
||||||
@ -317,6 +390,27 @@ public interface Dumpers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
class MainQuestInfo {
|
||||||
|
public String title;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.title;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
class QuestInfo {
|
||||||
|
public String description;
|
||||||
|
public int mainQuest;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.description + "," + this.mainQuest;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum Quality {
|
enum Quality {
|
||||||
LEGENDARY,
|
LEGENDARY,
|
||||||
EPIC,
|
EPIC,
|
||||||
|
@ -1,17 +1,18 @@
|
|||||||
package emu.grasscutter.utils;
|
package emu.grasscutter.utils;
|
||||||
|
|
||||||
import static emu.grasscutter.config.Configuration.*;
|
|
||||||
|
|
||||||
import ch.qos.logback.classic.Level;
|
import ch.qos.logback.classic.Level;
|
||||||
import ch.qos.logback.classic.Logger;
|
import ch.qos.logback.classic.Logger;
|
||||||
import emu.grasscutter.BuildConfig;
|
import emu.grasscutter.BuildConfig;
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
import emu.grasscutter.net.packet.PacketOpcodesUtils;
|
import emu.grasscutter.net.packet.PacketOpcodesUtils;
|
||||||
import emu.grasscutter.tools.Dumpers;
|
import emu.grasscutter.tools.Dumpers;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
import static emu.grasscutter.config.Configuration.*;
|
||||||
|
|
||||||
/** A parser for start-up arguments. */
|
/** A parser for start-up arguments. */
|
||||||
public final class StartupArguments {
|
public final class StartupArguments {
|
||||||
@ -167,6 +168,7 @@ public final class StartupArguments {
|
|||||||
case "items" -> Dumpers.dumpItems(language);
|
case "items" -> Dumpers.dumpItems(language);
|
||||||
case "scenes" -> Dumpers.dumpScenes();
|
case "scenes" -> Dumpers.dumpScenes();
|
||||||
case "entities" -> Dumpers.dumpEntities(language);
|
case "entities" -> Dumpers.dumpEntities(language);
|
||||||
|
case "quests" -> Dumpers.dumpQuests(language);
|
||||||
}
|
}
|
||||||
|
|
||||||
Grasscutter.getLogger().info("Finished dumping.");
|
Grasscutter.getLogger().info("Finished dumping.");
|
||||||
|
Loading…
Reference in New Issue
Block a user