Merge development into plugin-auth

This commit is contained in:
KingRainbow44 2022-05-14 12:08:33 -04:00
commit 5c6fe7bb04
No known key found for this signature in database
GPG Key ID: FC2CB64B00D257BE
190 changed files with 5290 additions and 1219 deletions

2
.gitignore vendored
View File

@ -69,6 +69,8 @@ language/
languages/
gacha-mapping.js
data/gacha_mappings.js
BuildConfig.java
# macOS
.DS_Store
data/hk4e/announcement/

View File

@ -139,6 +139,7 @@ There is a dummy user named "Server" in every player's friends list that you can
| talent | talent \<talentID> \<value> | player.settalent | Client only | Sets talent level for your currently selected character | |
| teleport | teleport [@playerUid] \<x> \<y> \<z> [sceneId] | player.teleport | Both side | Change the player's position. | tp |
| tpall | | player.tpall | Client only | Teleports all players in your world to your position | |
| unlocktower | | player.tower | Client only | Unlock the all floors of abyss | ut |
| weather | weather \<weatherID> \<climateID> | player.weather | Client only | Changes the weather | w |
### Bonus

View File

@ -140,6 +140,7 @@ chmod +x gradlew
| talent | talent <天赋ID> <等级> | player.settalent | 仅客户端 | 设置当前角色的天赋等级 | |
| teleport | teleport [@playerUid] \<x> \<y> \<z> [sceneId] | player.teleport | 均可使用 | 传送玩家到指定坐标 | tp |
| tpall | | player.tpall | 仅客户端 | 传送多人世界中所有的玩家到自身地点 | |
| unlocktower | | player.tower | 仅客户端 | 解锁深渊全部层 | ut |
| weather | weather <天气ID> <气候ID> | player.weather | 仅客户端 | 改变天气 | w |
### 额外功能

View File

@ -45,6 +45,7 @@ targetCompatibility = JavaVersion.VERSION_17
group = 'xyz.grasscutters'
version = '1.1.2-dev'
sourceCompatibility = 17
targetCompatibility = 17
@ -100,12 +101,14 @@ application {
mainClassName = 'emu.grasscutter.Grasscutter'
}
jar {
manifest {
attributes 'Main-Class': 'emu.grasscutter.Grasscutter'
}
jar.baseName = 'grasscutter'
jar.archiveName = project.hasProperty('jarFilename') ? "${jarFilename}.${extension}" : archiveName
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
@ -229,6 +232,23 @@ javadoc {
}
}
task injectGitHash {
def gitCommitHash = {
try {
return 'git rev-parse --verify --short HEAD'.execute().text.trim()
} catch (e) {
return "GIT_NOT_FOUND"
}
}
new File(projectDir, "src/main/java/emu/grasscutter/BuildConfig.java").text = """
package emu.grasscutter;
public class BuildConfig {
public static final String VERSION = \"${version}\";
public static final String GIT_HASH = \"${gitCommitHash()}\";
}
"""
}
processResources {
dependsOn "generateProto"
}

View File

@ -6,12 +6,15 @@
"prefabPath": "GachaShowPanel_A022",
"previewPrefabPath": "UI_Tab_GachaShowPanel_A022",
"titlePath": "UI_GACHA_SHOW_PANEL_A022_TITLE",
"costItem": 224,
"costItemId": 224,
"costItemAmount": 1,
"costItemAmount10": 10,
"beginTime": 0,
"endTime": 1924992000,
"sortId": 1000,
"rateUpItems1": [],
"rateUpItems2": []
"fallbackItems4Pool1": [1006, 1014, 1015, 1020, 1021, 1023, 1024, 1025, 1027, 1031, 1032, 1034, 1036, 1039, 1043, 1044, 1045, 1048, 1053, 1055, 1056, 1064],
"weights4": [[1,510], [8,510], [10,10000]],
"weights5": [[1,75], [73,150], [90,10000]]
},
{
"gachaType": 301,
@ -20,13 +23,14 @@
"prefabPath": "GachaShowPanel_A079",
"previewPrefabPath": "UI_Tab_GachaShowPanel_A079",
"titlePath": "UI_GACHA_SHOW_PANEL_A048_TITLE",
"costItem": 223,
"costItemId": 223,
"beginTime": 0,
"endTime": 1924992000,
"sortId": 9998,
"maxItemType": 1,
"rateUpItems1": [1002],
"rateUpItems2": [1053, 1020, 1045]
"rateUpItems4": [1053, 1020, 1045],
"rateUpItems5": [1002],
"fallbackItems5Pool2": [],
"weights5": [[1,80], [73,80], [90,10000]]
},
{
"gachaType": 302,
@ -35,15 +39,17 @@
"prefabPath": "GachaShowPanel_A080",
"previewPrefabPath": "UI_Tab_GachaShowPanel_A080",
"titlePath": "UI_GACHA_SHOW_PANEL_A021_TITLE",
"costItem": 223,
"costItemId": 223,
"beginTime": 0,
"endTime": 1924992000,
"sortId": 9997,
"minItemType": 2,
"eventChance": 75,
"softPity": 80,
"hardPity": 80,
"rateUpItems1": [11509, 12504],
"rateUpItems2": [11401, 12402, 13407, 14401, 15401]
"rateUpItems4": [11401, 12402, 13407, 14401, 15401],
"rateUpItems5": [11509, 12504],
"fallbackItems5Pool1": [],
"weights4": [[1,600], [7,600], [8, 6600], [10,12600]],
"weights5": [[1,100], [62,100], [73, 7800], [80,10000]]
}
]

View File

@ -1,29 +1,22 @@
{
"list": [
"list": [
{
"ann_id": 1,
"title": "<b>Welcome to Grasscutter!</b>",
"subtitle": "<b>Welcome</b>",
"title": "<strong>Welcome to Grasscutter!</strong>",
"subtitle": "Welcome!",
"banner": "https://uploadstatic-sea.mihoyo.com/announcement/2020/09/17/f4aa42d505822805eebf4a55d72a78d8_2755691727027973637.jpg",
"content": "Hi there!<br>First of all, welcome to Grasscutter. If you have any issues, please let us know so that Lawnmower can help you! Check out our:<br><div><p style=\"white-space: pre-wrap;\"><strong>¡þDiscord¡þ</strong></p><p style=\"white-space: pre-wrap;\"><a href=\"https://discord.gg/T5vZU6UyeG\">https://discord.gg/T5vZU6UyeG</a></p><p style=\"white-space: pre-wrap;\"><strong>¡þGitHub¡þ</strong></p><p style=\"white-space: pre-wrap;\"><a href=\"https://github.com/Grasscutters/Grasscutter\">https://github.com/Grasscutters/Grasscutter</a></p></div>",
"lang": "es-es"
"content": "<p>Hi there!</p><p>First of all, welcome to Grasscutter. If you have any issues, please let us know so that Lawnmower can help you!</p><br><p><strong>〓Discord〓</strong></p><a href=\"https://discord.gg/T5vZU6UyeG\">https://discord.gg/T5vZU6UyeG</a><br><br><p><strong>〓GitHub〓</strong><a href=\"https://github.com/Grasscutters/Grasscutter\">https://github.com/Grasscutters/Grasscutter</a>",
"lang": "en-US"
},
{
"ann_id": 2,
"title": "<b>How to use announcements</b>",
"subtitle": "<b>How to use</b>",
"title": "<strong>How to use announcements</strong>",
"subtitle": "How to use announcements",
"banner": "https://uploadstatic-sea.mihoyo.com/announcement/2020/09/17/f4aa42d505822805eebf4a55d72a78d8_2755691727027973637.jpg",
"content": "<strong>Tips<br></strong>>How to use announcements<br><br>>Announcement content can use HTML<br><br>>The specific content of the announcement is stored in the program directory<code>data/GameAnnouncement.json</code>, while<code>GameAnnouncementList.json</code> stores the announcement list data<br><br><strong>How to use</strong><br>>In <code>GameAnnouncement</code><table><thead><thead><tr><th>Parameters</th><th>Description</th></thead></thead><thbody><thead><tr><th>ann_Id</th><th>Announcement unique id</th></thead><thead><tr><th>title</th><th>Show at the top of the content</th></thead><thead><tr><th>subtitle</th><th>title shown on the left</th></thead><thead><tr><th>banner</th><th>Display between content and title</th></thead><thead><tr><th>content</th><th>as u see</th></thead><thead><tr><th>lang</th><th>display language</th></thead><thead><tr><th>total</th><th>Announcement quantity</th></thead></thbody></table><br><br>>In <code>GameAnnouncementList</code><br>If you want to add an annouement, please add the list data in the announcement type corresponding to GameAnnouncementList, and finally add the announcement content in GameAnnouncement",
"lang": "es-es"
},
{
"ann_id": 3,
"title": "<b>ÕâÊǻ¹«¸æ--This is the event announcement</b>",
"subtitle": "<b>Welcome</b>",
"banner":"https://uploadstatic-sea.mihoyo.com/announcement/2020/09/22/7d85f19b152d218e73224d7c138a0fd0_5818585260283672899.jpg",
"content": "Welcome",
"lang": "es-es"
"content": "<p>Announcement content uses HTML. The specific content of the announcement is stored in the program directory <code>GameAnnouncement.json</code>, while <code>GameAnnouncementList.json</code> stores the announcement list data.</p><h2><code>GameAnnouncement</code></h2><table><tr><th>Parameter</th><th>Description</th></tr><tr><td>ann_id</td><td>Unique ID</td></tr><tr><td>title</td><td>Title shown at the top of the content</td></tr><tr><td>subtitle</td><td>Short title shown on the left</td></tr><tr><td>banner</td><td>Image to display between content and title</td></tr><tr><td>content</td><td>Content body in HTML</td></tr><tr><td>lang</td><td>Language code for this entry</td></tr></table><h2><code>GameAnnouncementList</code></h2><p>If you want to add an announcement, please add the list data in the announcement type corresponding to <code>GameAnnouncementList</code>, and finally add the announcement content in <code>GameAnnouncement</code>.</p>",
"lang": "en-US"
}
],
"total": 3
],
"total": 2
}

View File

@ -5,114 +5,64 @@
"list": [
{
"ann_id": 1,
"title": "<b>Welcome to Grasscutter!</b>",
"subtitle": "<b>Welcome</b>",
"title": "<strong>Welcome to Grasscutter!</strong>",
"subtitle": "Welcome!",
"banner": "https://uploadstatic-sea.mihoyo.com/announcement/2020/09/22/7d85f19b152d218e73224d7c138a0fd0_5818585260283672899.jpg",
"content": "",
"type_label": "Juego",
"tag_label": "1",
"tag_icon": "https://uploadstatic-sea.mihoyo.com/announcement/2020/03/05/a2588f1a51faee9fa8dfe9aead649dd6_7237021399135895303.png",
"login_alert": 1,
"lang": "es-es",
"start_time": "2020-09-25 04:05:30",
"end_time": "2023-10-30 11:00:00",
"type": 2,
"remind": 0,
"alert": 0,
"tag_start_time": "2000-01-02 15:04:05",
"tag_end_time": "2030-01-02 15:04:05",
"remind_ver": 1,
"has_content": true,
"extra_remind": 0
"type_label": "System",
"lang": "en-US",
"start_time": "2020-09-25 04:05:30",
"end_time": "2030-10-30 11:00:00",
"content": "",
"has_content": true
},
{
"ann_id": 2,
"title": "<b>这是游戏公告 -- This is the game announcement</b>",
"subtitle": "<b>This is the game announcement</b>",
"banner": "https://uploadstatic-sea.mihoyo.com/announcement/2020/09/17/85b7163c95745a76d49b3d163d893592_6487108933004985049.jpg",
"content": "",
"type_label": "Juego",
"tag_label": "1",
"title": "<strong>How to use announcements</strong>",
"subtitle": "How to use announcements",
"banner": "https://uploadstatic-sea.mihoyo.com/announcement/2020/09/22/7d85f19b152d218e73224d7c138a0fd0_5818585260283672899.jpg",
"tag_icon": "https://uploadstatic-sea.mihoyo.com/announcement/2020/03/05/a2588f1a51faee9fa8dfe9aead649dd6_7237021399135895303.png",
"login_alert": 1,
"lang": "es-es",
"start_time": "2020-09-25 15:12:09",
"end_time": "2030-10-30 11:00:00",
"type": 2,
"remind": 0,
"alert": 0,
"tag_start_time": "2000-01-02 08:04:05",
"tag_end_time": "2030-01-02 08:04:05",
"remind_ver": 1,
"has_content": true,
"extra_remind": 0
"type_label": "System",
"lang": "en-US",
"start_time": "2020-09-25 04:05:30",
"end_time": "2030-10-30 11:00:00",
"content": "",
"has_content": true
}
],
"type_id": 2,
"type_label": "Juego"
},
{
"list": [
{
"ann_id": 3,
"title": "<b>这是活动公告--This is the event announcement</b>",
"subtitle": "<b>Welcome</b>",
"banner": "https://uploadstatic-sea.mihoyo.com/announcement/2020/09/22/7d85f19b152d218e73224d7c138a0fd0_5818585260283672899.jpg",
"content": "",
"type_label": "Eventos",
"tag_label": "1",
"tag_icon": "https://uploadstatic-sea.mihoyo.com/announcement/2020/03/05/a2588f1a51faee9fa8dfe9aead649dd6_7237021399135895303.png",
"login_alert": 1,
"lang": "es-es",
"start_time": "2020-09-25 04:05:30",
"end_time": "2022-05-02 00:51:00",
"type": 2,
"remind": 0,
"alert": 0,
"tag_start_time": "2000-01-02 15:04:05",
"tag_end_time": "2022-05-02 00:51:00",
"remind_ver": 1,
"has_content": true,
"extra_remind": 0
}
],
"type_id": 1,
"type_label": "Eventos"
"type_label": "System"
},
{
"list": [
{}
],
"type_id": 3,
"type_label": "Others"
"type_label": "Events"
}
],
"total": 3,
"total": 2,
"type_list": [
{
"id": 2,
"name": "游戏系统公告",
"mi18n_name": "Juego"
"name": "游戏系统公告",
"mi18n_name": "System"
},
{
"id": 1,
"name": "活动公告",
"mi18n_name": "Eventos"
},
{
"id": 3,
"name": "其他",
"mi18n_name": "Others"
"name": "活动公告",
"mi18n_name": "Activity"
}
],
"alert": true,
"alert_id": 2,
"timezone": -5,
"pic_list": [
],
"alert": false,
"alert_id": 0,
"pic_list": [],
"pic_total": 0,
"pic_type_list": [
],
"pic_type_list": [],
"pic_alert": false,
"pic_alert_id": 0,
"static_sign": ""

121
data/gacha_details.html Normal file
View File

@ -0,0 +1,121 @@
<!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;
}
</style>
<title>Banner Details</title>
<script type="text/javascript" src="/gacha/mappings"></script>
</head>
<body>
<div class="content">
<div class="container">
<h2 class="mb-5">{{TITLE}}</h2>
<h3 class="">{{AVAILABLE_FIVE_STARS}}</h3>
<hr />
<ul id="5-star-list">
</ul>
<h3 class="">{{AVAILABLE_FOUR_STARS}}</h3>
<hr />
<ul id="4-star-list">
</ul>
<h3 class="">{{AVAILABLE_THREE_STARS}}</h3>
<hr />
<ul id="3-star-list">
</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>
<script>
var fiveStarItems = {{FIVE_STARS}};
var fourStarItems = {{FOUR_STARS}};
var threeStarItems = {{THREE_STARS}};
var lang = "{{LANGUAGE}}";
function getNameForId(itemId) {
if (mappings[lang] != null && mappings[lang][itemId] != null) {
return mappings[lang][itemId][0];
}
else if (mappings["en-us"] != null && mappings["en-us"][itemId] != null) {
return mappings["en-us"][itemId][0];
}
return itemId.toString();
}
fiveStarList = document.getElementById("5-star-list");
fourStarList = document.getElementById("4-star-list");
threeStarList = document.getElementById("3-star-list");
fiveStarItems.forEach(element => {
var entry = document.createElement("li");
entry.innerHTML = getNameForId(element);
fiveStarList.appendChild(entry);
});
fourStarItems.forEach(element => {
var entry = document.createElement("li");
entry.innerHTML = getNameForId(element);
fourStarList.appendChild(entry);
});
threeStarItems.forEach(element => {
var entry = document.createElement("li");
entry.innerHTML = getNameForId(element);
threeStarList.appendChild(entry);
});
</script>
</body>
</html>

View File

@ -53,47 +53,14 @@
}
</style>
<title>Gacha Records</title>
<script>
// Debug entry
// record = [
// {"time": 10000341, "item": 1041},
// {"time": 10000342, "item": 1032},
// {"time": 10000343, "item": 1035},
// ];
// maxPage = 5;
// in production environment
record = {{REPLACE_RECORD}};
maxPage = {{REPLACE_MAXPAGE}};
// TODO: implement this mapper by yourself
// I don't want to put real items' name here to avoid being DMCA'd
mappings = {
'en-us': {
200: "Standard",
301: "Event Avatar",
302: "Event Weapon",
1041 : ["M0n4", "blue"],
1032 : ["B4nn477", "purple"],
1035 : ["77", "yellow"]
},
'zh-cn': {
// encoding issues here, maybe we should consider load mappings remotely
// will display as "锟斤铐锟斤铐锟斤铐", lmao
// 200: "常驻",
// 301: "角色UP-1",
// 302: "武器UP"
200: "Standard",
301: "Event Avatar",
302: "Event Weapon",
}
};
</script>
<!-- This file could be generated automatically using `java -jar grasscutter.jar -gachamap` -->
<!-- You can also modify the file manually to customize it -->
<!-- Otherwise you may onle see number IDs in the gacha record -->
<script type="text/javascript" src="/gacha/mappings"></script>
<script>
record = {{REPLACE_RECORD}};
maxPage = {{REPLACE_MAXPAGE}};
mappings['default'] = mappings['en-us']; // make en-us as default/fallback option
</script>
</head>
@ -161,32 +128,12 @@
}
return "<span class='blue'>" + itemID + "</span>";
}
function dateFormatter(timeStamp) {
var date = new Date(timeStamp);
if (lang == "en-us" || lang == null) { // MM/DD/YYYY hh:mm:ss.SSS
return String(date.getMonth()+1).padStart(2, "0") +
"/"+String(date.getDate()).padStart(2, "0")+
"/"+date.getFullYear()+
" "+String(date.getHours()).padStart(2, "0")+
":"+String(date.getMinutes()).padStart(2, "0")+
":"+String(date.getSeconds()).padStart(2, "0")+
"."+String(date.getMilliseconds()).padStart(3, "0");
} else if (lang == "zh-cn") { // YYYY/MM/DD hh:mm:ss.SSS
return date.getFullYear()+
"/" + String(date.getMonth()+1).padStart(2, "0") +
"/"+String(date.getDate()).padStart(2, "0")+
" "+String(date.getHours()).padStart(2, "0")+
":"+String(date.getMinutes()).padStart(2, "0")+
":"+String(date.getSeconds()).padStart(2, "0")+
"."+String(date.getMilliseconds()).padStart(3, "0");
}
}
(function (){
var container = document.getElementById("container");
record.forEach(element => {
var e = document.createElement("tr");
e.innerHTML= "<td>" + dateFormatter(element.time) + "</td><td>" + itemMapper(element.item) + "</td>";
e.innerHTML= "<td>" + (new Date(element.time).toLocaleString(lang)) + "</td><td>" + itemMapper(element.item) + "</td>";
container.appendChild(e);
});
// setup pagenation buttons

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
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

View File

@ -0,0 +1,8 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message AbilityMixinWidgetMpSupport {
uint32 target_entity_id = 1;
}

View File

@ -0,0 +1,29 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "LunchBoxData.proto";
import "AnchorPointData.proto";
import "OneoffGatherPointDetectorData.proto";
import "ClientCollectorData.proto";
import "WidgetCoolDownData.proto";
import "WidgetSlotData.proto";
message AllWidgetDataNotify {
enum CmdId {
option allow_alias = true;
NONE = 0;
ENET_CHANNEL_ID = 0;
ENET_IS_RELIABLE = 1;
CMD_ID = 4260;
}
repeated AnchorPointData anchor_point_list = 2;
uint32 next_anchor_point_usable_time = 3;
LunchBoxData lunch_box_data = 5;
repeated OneoffGatherPointDetectorData oneoff_gather_point_detector_data_list = 6;
repeated ClientCollectorData client_collector_data_list = 7;
repeated WidgetCoolDownData cool_down_group_data_list = 8;
repeated WidgetCoolDownData normal_cool_down_data_list = 9;
repeated WidgetSlotData slot_list = 11;
}

View File

@ -0,0 +1,12 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "Vector.proto";
message AnchorPointData {
uint32 anchor_point_id = 1;
Vector pos = 2;
Vector rot = 3;
uint32 end_time = 4;
}

View File

@ -0,0 +1,18 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "AnchorPointData.proto";
message AnchorPointDataNotify {
enum CmdId {
option allow_alias = true;
NONE = 0;
ENET_CHANNEL_ID = 0;
ENET_IS_RELIABLE = 1;
CMD_ID = 4253;
}
repeated AnchorPointData anchor_point_list = 1;
uint32 next_usable_time = 2;
}

View File

@ -0,0 +1,24 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message AnchorPointOpReq {
enum CmdId {
option allow_alias = true;
ENET_CHANNEL_ID = 0;
NONE = 0;
ENET_IS_RELIABLE = 1;
IS_ALLOW_CLIENT = 1;
CMD_ID = 4255;
}
enum AnchorPointOpType {
ANCHOR_POINT_OP_NONE = 0;
ANCHOR_POINT_OP_TELEPORT = 1;
ANCHOR_POINT_OP_REMOVE = 2;
}
uint32 anchor_point_op_type = 1;
uint32 anchor_point_id = 2;
}

View File

@ -0,0 +1,18 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message AnchorPointOpRsp {
enum CmdId {
option allow_alias = true;
NONE = 0;
ENET_CHANNEL_ID = 0;
ENET_IS_RELIABLE = 1;
CMD_ID = 4274;
}
int32 retcode = 1;
uint32 anchor_point_op_type = 2;
uint32 anchor_point_id = 3;
}

9
proto/ChildQuest.proto Normal file
View File

@ -0,0 +1,9 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message ChildQuest {
uint32 quest_id = 1;
uint32 state = 2;
uint32 quest_config_id = 3;
}

View File

@ -0,0 +1,10 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message ClientCollectorData {
uint32 material_id = 1;
uint32 max_points = 2;
uint32 curr_points = 3;
}

View File

@ -0,0 +1,17 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "ClientCollectorData.proto";
message ClientCollectorDataNotify {
enum CmdId {
option allow_alias = true;
NONE = 0;
ENET_CHANNEL_ID = 0;
ENET_IS_RELIABLE = 1;
CMD_ID = 4280;
}
repeated ClientCollectorData client_collector_data_list = 1;
}

View File

@ -0,0 +1,16 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "CodexTypeData.proto";
message CodexDataFullNotify {
enum CmdId {
option allow_alias = true;
NONE = 0;
ENET_CHANNEL_ID = 0;
ENET_IS_RELIABLE = 1;
CMD_ID = 4208;
}
repeated CodexTypeData type_data_list = 1;
}

View File

@ -0,0 +1,18 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "CodexType.proto";
message CodexDataUpdateNotify {
enum CmdId {
option allow_alias = true;
NONE = 0;
ENET_CHANNEL_ID = 0;
ENET_IS_RELIABLE = 1;
CMD_ID = 4205;
}
CodexType type = 1;
uint32 id = 2;
uint32 weapon_max_promote_level = 3;
}

15
proto/CodexType.proto Normal file
View File

@ -0,0 +1,15 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
enum CodexType {
CODEX_NONE = 0;
CODEX_QUEST = 1;
CODEX_WEAPON = 2;
CODEX_ANIMAL = 3;
CODEX_MATERIAL = 4;
CODEX_BOOKS = 5;
CODEX_PUSHTIPS = 6;
CODEX_VIEW = 7;
CODEX_RELIQUARY = 8;
}

View File

@ -0,0 +1,6 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message CodexTypeComparer {
}

11
proto/CodexTypeData.proto Normal file
View File

@ -0,0 +1,11 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "CodexType.proto";
message CodexTypeData {
CodexType type = 1;
repeated uint32 codex_id_list = 2;
repeated bool have_viewed_list = 3;
map<uint32, uint32> weapon_max_promote_level_map = 4;
}

View File

@ -0,0 +1,8 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message CutSceneBeginNotify {
uint32 cutscene_id = 1;
bool is_wait_others = 2;
}

View File

@ -0,0 +1,8 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message CutSceneEndNotify {
int32 retcode = 1;
uint32 cutscene_id = 2;
}

View File

@ -0,0 +1,7 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message CutSceneFinishNotify {
uint32 cutscene_id = 1;
}

View File

@ -0,0 +1,9 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "ParentQuest.proto";
message FinishedParentQuestNotify {
repeated ParentQuest parent_quest_list = 1;
}

View File

@ -0,0 +1,9 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "ParentQuest.proto";
message FinishedParentQuestUpdateNotify {
repeated ParentQuest parent_quest_list = 1;
}

View File

@ -0,0 +1,16 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message GetWidgetSlotReq {
enum CmdId {
option allow_alias = true;
ENET_CHANNEL_ID = 0;
NONE = 0;
ENET_IS_RELIABLE = 1;
IS_ALLOW_CLIENT = 1;
CMD_ID = 4285;
}
}

View File

@ -0,0 +1,18 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "WidgetSlotData.proto";
message GetWidgetSlotRsp {
enum CmdId {
option allow_alias = true;
NONE = 0;
ENET_CHANNEL_ID = 0;
ENET_IS_RELIABLE = 1;
CMD_ID = 4291;
}
int32 retcode = 1;
repeated WidgetSlotData slot_list = 2;
}

View File

@ -0,0 +1,17 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message HomeChooseModuleReq {
enum CmdId {
option allow_alias = true;
ENET_CHANNEL_ID = 0;
NONE = 0;
ENET_IS_RELIABLE = 1;
IS_ALLOW_CLIENT = 1;
CMD_ID = 4530;
}
uint32 module_id = 1;
}

View File

@ -0,0 +1,17 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message HomeChooseModuleRsp {
enum CmdId {
option allow_alias = true;
NONE = 0;
ENET_CHANNEL_ID = 0;
ENET_IS_RELIABLE = 1;
CMD_ID = 4653;
}
int32 retcode = 1;
uint32 module_id = 2;
}

View File

@ -0,0 +1,17 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "HomeModuleComfortInfo.proto";
message HomeComfortInfoNotify {
enum CmdId {
option allow_alias = true;
NONE = 0;
ENET_CHANNEL_ID = 0;
ENET_IS_RELIABLE = 1;
CMD_ID = 4557;
}
repeated HomeModuleComfortInfo module_info_list = 1;
}

View File

@ -0,0 +1,10 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message HomeModuleComfortInfo {
uint32 module_id = 1;
repeated uint32 world_scene_block_comfort_value_list = 2;
uint32 room_scene_comfort_value = 3;
}

8
proto/LunchBoxData.proto Normal file
View File

@ -0,0 +1,8 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message LunchBoxData {
map<uint32, uint32> slot_material_map = 1;
}

View File

@ -0,0 +1,9 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
enum LunchBoxSlotType {
LUNCH_BOX_SLOT_NONE = 0;
LUNCH_BOX_SLOT_REVIVE = 1;
LUNCH_BOX_SLOT_HEAL = 2;
}

View File

@ -0,0 +1,15 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "Vector.proto";
message OneoffGatherPointDetectorData {
uint32 material_id = 1;
bool is_all_collected = 2;
bool is_hint_valid = 3;
Vector hint_center_pos = 4;
uint32 hint_radius = 5;
uint32 group_id = 6;
uint32 config_id = 7;
}

View File

@ -0,0 +1,17 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "OneoffGatherPointDetectorData.proto";
message OneoffGatherPointDetectorDataNotify {
enum CmdId {
option allow_alias = true;
NONE = 0;
ENET_CHANNEL_ID = 0;
ENET_IS_RELIABLE = 1;
CMD_ID = 4288;
}
repeated OneoffGatherPointDetectorData oneoff_gather_point_detector_data_list = 1;
}

18
proto/ParentQuest.proto Normal file
View File

@ -0,0 +1,18 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "ParentQuestRandomInfo.proto";
import "ChildQuest.proto";
message ParentQuest {
uint32 parent_quest_id = 1;
repeated ChildQuest child_quest_list = 2;
bool is_finished = 3;
bool is_random = 4;
ParentQuestRandomInfo random_info = 5;
repeated int32 quest_var = 6;
uint32 parent_quest_state = 7;
uint32 quest_var_seq = 8;
map<uint32, uint32> time_var_map = 9;
}

View File

@ -0,0 +1,9 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message ParentQuestRandomInfo {
uint32 entrance_id = 1;
uint32 template_id = 2;
repeated uint32 factor_list = 3;
}

View File

@ -0,0 +1,12 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "FriendEnterHomeOption.proto";
message PlayerHomeCompInfo {
FriendEnterHomeOption friend_enter_home_option = 1;
repeated uint32 unlocked_module_id_list = 2;
repeated uint32 levelup_reward_got_level_list = 3;
repeated uint32 seen_module_id_list = 4;
}

View File

@ -0,0 +1,17 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "PlayerHomeCompInfo.proto";
message PlayerHomeCompInfoNotify {
enum CmdId {
option allow_alias = true;
NONE = 0;
ENET_CHANNEL_ID = 0;
ENET_IS_RELIABLE = 1;
CMD_ID = 4628;
}
PlayerHomeCompInfo comp_info = 1;
}

20
proto/Quest.proto Normal file
View File

@ -0,0 +1,20 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message Quest {
uint32 quest_id = 1;
uint32 state = 2;
uint32 start_time = 4;
bool is_random = 5;
uint32 parent_quest_id = 6;
uint32 quest_config_id = 7;
uint32 start_game_time = 8;
uint32 accept_time = 9;
repeated uint32 lacked_npc_list = 10;
repeated uint32 finish_progress_list = 11;
repeated uint32 fail_progress_list = 12;
map<uint32, uint32> lacked_npc_map = 13;
repeated uint32 lacked_place_list = 14;
map<uint32, uint32> lacked_place_map = 15;
}

View File

@ -0,0 +1,7 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message QuestDelNotify {
uint32 quest_id = 1;
}

View File

@ -0,0 +1,8 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message QuestGlobalVar {
uint32 key = 1;
int32 value = 2;
}

View File

@ -0,0 +1,9 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "QuestGlobalVar.proto";
message QuestGlobalVarNotify {
repeated QuestGlobalVar var_list = 1;
}

View File

@ -0,0 +1,9 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "Quest.proto";
message QuestListNotify {
repeated Quest quest_list = 1;
}

View File

@ -0,0 +1,9 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "Quest.proto";
message QuestListUpdateNotify {
repeated Quest quest_list = 1;
}

View File

@ -0,0 +1,9 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message QuestProgressUpdateNotify {
uint32 quest_id = 1;
repeated uint32 finish_progress_list = 2;
repeated uint32 fail_progress_list = 3;
}

View File

@ -0,0 +1,9 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message QuestUpdateQuestVarNotify {
uint32 parent_quest_id = 1;
repeated int32 quest_var = 2;
uint32 parent_quest_var_seq = 3;
}

View File

@ -0,0 +1,12 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "QuestVarOp.proto";
message QuestUpdateQuestVarReq {
uint32 quest_id = 1;
repeated QuestVarOp quest_var_op_list = 2;
uint32 parent_quest_id = 3;
uint32 parent_quest_var_seq = 4;
}

View File

@ -0,0 +1,10 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message QuestUpdateQuestVarRsp {
int32 retcode = 1;
uint32 quest_id = 2;
uint32 parent_quest_id = 3;
uint32 parent_quest_var_seq = 4;
}

9
proto/QuestVarOp.proto Normal file
View File

@ -0,0 +1,9 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message QuestVarOp {
uint32 index = 1;
int32 value = 2;
bool is_add = 3;
}

View File

@ -0,0 +1,26 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "WidgetCreateLocationInfo.proto";
import "WidgetCameraInfo.proto";
import "WidgetCreatorInfo.proto";
import "WidgetThunderBirdFeatherInfo.proto";
message QuickUseWidgetReq {
enum CmdId {
option allow_alias = true;
ENET_CHANNEL_ID = 0;
NONE = 0;
ENET_IS_RELIABLE = 1;
IS_ALLOW_CLIENT = 1;
CMD_ID = 4300;
}
oneof Param {
WidgetCreateLocationInfo location_info = 20;
WidgetCameraInfo camera_info = 21;
WidgetCreatorInfo creator_info = 22;
WidgetThunderBirdFeatherInfo thunder_bird_feather_info = 23;
}
}

View File

@ -0,0 +1,21 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "OneoffGatherPointDetectorData.proto";
import "ClientCollectorData.proto";
message QuickUseWidgetRsp {
enum CmdId {
option allow_alias = true;
NONE = 0;
ENET_CHANNEL_ID = 0;
ENET_IS_RELIABLE = 1;
CMD_ID = 4258;
}
int32 retcode = 1;
uint32 material_id = 2;
OneoffGatherPointDetectorData detector_data = 3;
ClientCollectorData client_collector_data = 4;
}

View File

@ -0,0 +1,8 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message ServerCondMeetQuestListUpdateNotify {
repeated uint32 add_quest_id_list = 1;
repeated uint32 del_quest_id_list = 2;
}

View File

@ -0,0 +1,18 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "LunchBoxData.proto";
message SetUpLunchBoxWidgetReq {
enum CmdId {
option allow_alias = true;
ENET_CHANNEL_ID = 0;
NONE = 0;
ENET_IS_RELIABLE = 1;
IS_ALLOW_CLIENT = 1;
CMD_ID = 4265;
}
LunchBoxData lunch_box_data = 1;
}

View File

@ -0,0 +1,18 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "LunchBoxData.proto";
message SetUpLunchBoxWidgetRsp {
enum CmdId {
option allow_alias = true;
NONE = 0;
ENET_CHANNEL_ID = 0;
ENET_IS_RELIABLE = 1;
CMD_ID = 4297;
}
int32 retcode = 1;
LunchBoxData lunch_box_data = 2;
}

View File

@ -0,0 +1,21 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "WidgetSlotOp.proto";
import "WidgetSlotTag.proto";
message SetWidgetSlotReq {
enum CmdId {
option allow_alias = true;
ENET_CHANNEL_ID = 0;
NONE = 0;
ENET_IS_RELIABLE = 1;
IS_ALLOW_CLIENT = 1;
CMD_ID = 4269;
}
WidgetSlotOp op = 2;
repeated WidgetSlotTag tag_list = 3;
uint32 material_id = 4;
}

View File

@ -0,0 +1,21 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "WidgetSlotOp.proto";
import "WidgetSlotTag.proto";
message SetWidgetSlotRsp {
enum CmdId {
option allow_alias = true;
NONE = 0;
ENET_CHANNEL_ID = 0;
ENET_IS_RELIABLE = 1;
CMD_ID = 4279;
}
int32 retcode = 1;
WidgetSlotOp op = 2;
repeated WidgetSlotTag tag_list = 3;
uint32 material_id = 4;
}

View File

@ -0,0 +1,18 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message TryEnterHomeReq {
enum CmdId {
option allow_alias = true;
ENET_CHANNEL_ID = 0;
NONE = 0;
ENET_IS_RELIABLE = 1;
IS_ALLOW_CLIENT = 1;
CMD_ID = 4792;
}
uint32 target_uid = 1;
uint32 target_point = 2;
}

View File

@ -0,0 +1,18 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message TryEnterHomeRsp {
enum CmdId {
option allow_alias = true;
NONE = 0;
ENET_CHANNEL_ID = 0;
ENET_IS_RELIABLE = 1;
CMD_ID = 4690;
}
int32 retcode = 1;
uint32 target_uid = 2;
repeated uint32 param_list = 3;
}

View File

@ -0,0 +1,20 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "Vector.proto";
message UseWidgetCreateGadgetReq {
enum CmdId {
option allow_alias = true;
ENET_CHANNEL_ID = 0;
NONE = 0;
ENET_IS_RELIABLE = 1;
IS_ALLOW_CLIENT = 1;
CMD_ID = 4276;
}
uint32 material_id = 1;
Vector pos = 2;
Vector rot = 3;
}

View File

@ -0,0 +1,17 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message UseWidgetCreateGadgetRsp {
enum CmdId {
option allow_alias = true;
NONE = 0;
ENET_CHANNEL_ID = 0;
ENET_IS_RELIABLE = 1;
CMD_ID = 4270;
}
int32 retcode = 1;
uint32 material_id = 2;
}

View File

@ -0,0 +1,17 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message UseWidgetRetractGadgetReq {
enum CmdId {
option allow_alias = true;
ENET_CHANNEL_ID = 0;
NONE = 0;
ENET_IS_RELIABLE = 1;
IS_ALLOW_CLIENT = 1;
CMD_ID = 4273;
}
uint32 entity_id = 1;
}

View File

@ -0,0 +1,18 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message UseWidgetRetractGadgetRsp {
enum CmdId {
option allow_alias = true;
ENET_CHANNEL_ID = 0;
NONE = 0;
ENET_IS_RELIABLE = 1;
IS_ALLOW_CLIENT = 1;
CMD_ID = 4266;
}
int32 retcode = 1;
uint32 entity_id = 2;
}

View File

@ -0,0 +1,17 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "WidgetSlotData.proto";
message WidgetActiveChangeNotify {
enum CmdId {
option allow_alias = true;
NONE = 0;
ENET_CHANNEL_ID = 0;
ENET_IS_RELIABLE = 1;
CMD_ID = 4264;
}
repeated WidgetSlotData widget_data_list = 1;
}

View File

@ -0,0 +1,8 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message WidgetCameraInfo {
uint32 target_entity_id = 1;
}

View File

@ -0,0 +1,10 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message WidgetCoolDownData {
uint32 id = 1;
uint64 cool_down_time = 2;
bool is_success = 3;
}

View File

@ -0,0 +1,18 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "WidgetCoolDownData.proto";
message WidgetCoolDownNotify {
enum CmdId {
option allow_alias = true;
NONE = 0;
ENET_CHANNEL_ID = 0;
ENET_IS_RELIABLE = 1;
CMD_ID = 4298;
}
repeated WidgetCoolDownData group_cool_down_data_list = 1;
repeated WidgetCoolDownData normal_cool_down_data_list = 2;
}

View File

@ -0,0 +1,10 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "Vector.proto";
message WidgetCreateLocationInfo {
Vector pos = 1;
Vector rot = 2;
}

View File

@ -0,0 +1,12 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "WidgetCreatorOpType.proto";
import "WidgetCreateLocationInfo.proto";
message WidgetCreatorInfo {
WidgetCreatorOpType op_type = 1;
uint32 entity_id = 2;
WidgetCreateLocationInfo location_info = 3;
}

View File

@ -0,0 +1,9 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
enum WidgetCreatorOpType {
WIDGET_CREATOR_TYPE_NONE = 0;
WIDGET_CREATOR_TYPE_RETRACT = 1;
WIDGET_CREATOR_TYPE_RETRACT_AND_CREATE = 2;
}

View File

@ -0,0 +1,23 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "WidgetCreateLocationInfo.proto";
import "WidgetCreatorInfo.proto";
message WidgetDoBagReq {
enum CmdId {
option allow_alias = true;
ENET_CHANNEL_ID = 0;
NONE = 0;
ENET_IS_RELIABLE = 1;
IS_ALLOW_CLIENT = 1;
CMD_ID = 4269;
}
oneof OpInfo {
WidgetCreateLocationInfo location_info = 20;
WidgetCreatorInfo widget_creator_info = 21;
}
uint32 material_id = 1;
}

View File

@ -0,0 +1,17 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message WidgetDoBagRsp {
enum CmdId {
option allow_alias = true;
NONE = 0;
ENET_CHANNEL_ID = 0;
ENET_IS_RELIABLE = 1;
CMD_ID = 4270;
}
int32 retcode = 1;
uint32 material_id = 2;
}

View File

@ -0,0 +1,17 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "WidgetGadgetData.proto";
message WidgetGadgetAllDataNotify {
enum CmdId {
option allow_alias = true;
NONE = 0;
ENET_CHANNEL_ID = 0;
ENET_IS_RELIABLE = 1;
CMD_ID = 4262;
}
repeated WidgetGadgetData widget_gadget_data = 1;
}

View File

@ -0,0 +1,9 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message WidgetGadgetData {
uint32 gadget_id = 1;
repeated uint32 gadget_entity_id_list = 3;
}

View File

@ -0,0 +1,17 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "WidgetGadgetData.proto";
message WidgetGadgetDataNotify {
enum CmdId {
option allow_alias = true;
NONE = 0;
ENET_CHANNEL_ID = 0;
ENET_IS_RELIABLE = 1;
CMD_ID = 4256;
}
WidgetGadgetData widget_gadget_data = 1;
}

View File

@ -0,0 +1,16 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message WidgetGadgetDestroyNotify {
enum CmdId {
option allow_alias = true;
NONE = 0;
ENET_CHANNEL_ID = 0;
ENET_IS_RELIABLE = 1;
CMD_ID = 4268;
}
uint32 entity_id = 1;
}

View File

@ -0,0 +1,19 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message WidgetReportReq {
enum CmdId {
option allow_alias = true;
ENET_CHANNEL_ID = 0;
NONE = 0;
ENET_IS_RELIABLE = 1;
IS_ALLOW_CLIENT = 1;
CMD_ID = 4294;
}
uint32 material_id = 1;
bool is_clear_hint = 2;
bool is_client_collect = 3;
}

View File

@ -0,0 +1,17 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message WidgetReportRsp {
enum CmdId {
option allow_alias = true;
NONE = 0;
ENET_CHANNEL_ID = 0;
ENET_IS_RELIABLE = 1;
CMD_ID = 4259;
}
int32 retcode = 1;
uint32 material_id = 2;
}

View File

@ -0,0 +1,19 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "WidgetSlotOp.proto";
import "WidgetSlotData.proto";
message WidgetSlotChangeNotify {
enum CmdId {
option allow_alias = true;
NONE = 0;
ENET_CHANNEL_ID = 0;
ENET_IS_RELIABLE = 1;
CMD_ID = 4289;
}
WidgetSlotOp op = 1;
WidgetSlotData slot = 2;
}

View File

@ -0,0 +1,12 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "WidgetSlotTag.proto";
message WidgetSlotData {
WidgetSlotTag tag = 1;
uint32 material_id = 2;
uint32 cd_over_time = 3;
bool is_active = 4;
}

8
proto/WidgetSlotOp.proto Normal file
View File

@ -0,0 +1,8 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
enum WidgetSlotOp {
ATTACH = 0;
DETACH = 1;
}

View File

@ -0,0 +1,8 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
enum WidgetSlotTag {
WIDGET_SLOT_QUICK_USE = 0;
WIDGET_SLOT_ATTACH_AVATAR = 1;
}

View File

@ -0,0 +1,7 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message WidgetSlotTagComparer {
}

View File

@ -0,0 +1,8 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message WidgetThunderBirdFeatherInfo {
repeated uint32 entity_id_list = 1;
}

View File

@ -0,0 +1,17 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message WidgetUseAttachAbilityGroupChangeNotify {
enum CmdId {
option allow_alias = true;
NONE = 0;
ENET_CHANNEL_ID = 0;
ENET_IS_RELIABLE = 1;
CMD_ID = 4292;
}
uint32 material_id = 1;
bool is_attach = 2;
}

View File

@ -34,6 +34,7 @@ import emu.grasscutter.utils.Language;
import emu.grasscutter.server.game.GameServer;
import emu.grasscutter.tools.Tools;
import emu.grasscutter.utils.Crypto;
import emu.grasscutter.BuildConfig;
import javax.annotation.Nullable;
@ -88,6 +89,9 @@ public final class Grasscutter {
case "-gachamap" -> {
Tools.createGachaMapping(DATA("gacha_mappings.js")); exitEarly = true;
}
case "-version" -> {
System.out.println("Grasscutter version: " + BuildConfig.VERSION + "-" + BuildConfig.GIT_HASH); exitEarly = true;
}
}
}
@ -127,6 +131,9 @@ public final class Grasscutter {
httpServer.addRouter(LegacyAuthHandler.class);
httpServer.addRouter(GachaHandler.class);
// TODO: find a better place?
StaminaManager.initialize();
// Start servers.
var runMode = SERVER.runMode;
if (runMode == ServerRunMode.HYBRID) {
@ -178,15 +185,20 @@ public final class Grasscutter {
* Attempts to load the configuration from a file.
*/
public static void loadConfig() {
// Check if config.json exists. If not, we generate a new config.
if (!configFile.exists()) {
getLogger().info("config.json could not be found. Generating a default configuration ...");
config = new ConfigContainer();
Grasscutter.saveConfig(config);
return;
}
// If the file already exists, we attempt to load it.
try (FileReader file = new FileReader(configFile)) {
config = gson.fromJson(file, ConfigContainer.class);
} catch (Exception exception) {
Grasscutter.saveConfig(null);
config = new ConfigContainer();
} catch (Error error) {
// Occurred probably from an outdated config file.
Grasscutter.saveConfig(null);
config = new ConfigContainer();
getLogger().error("There was an error while trying to load the configuration from config.json. Please make sure that there are no syntax errors. If you want to start with a default configuration, delete your existing config.json.");
System.exit(1);
}
}

View File

@ -27,6 +27,13 @@ public interface AuthenticationSystem {
*/
void resetPassword(String username);
/**
* Called by plugins to internally verify a user's identity.
* @param details A unique, one-time token to verify the user.
* @return True if the user is verified, False otherwise.
*/
boolean verifyUser(String details);
/**
* This is the authenticator used for password authentication.
* @return An authenticator.

View File

@ -1,9 +1,12 @@
package emu.grasscutter.auth;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.auth.DefaultAuthenticators.*;
import emu.grasscutter.server.http.objects.ComboTokenResJson;
import emu.grasscutter.server.http.objects.LoginResultJson;
import static emu.grasscutter.utils.Language.translate;
/**
* The default Grasscutter authentication implementation.
* Allows all users to access any account.
@ -23,6 +26,12 @@ public final class DefaultAuthentication implements AuthenticationSystem {
// Unhandled. The default authenticator doesn't store passwords.
}
@Override
public boolean verifyUser(String details) {
Grasscutter.getLogger().info(translate("dispatch.authentication.default_unable_to_verify"));
return false;
}
@Override
public Authenticator<LoginResultJson> getPasswordAuthenticator() {
return this.passwordAuthenticator;

View File

@ -0,0 +1,46 @@
package emu.grasscutter.command.commands;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.server.packet.send.PacketChangeMpTeamAvatarRsp;
import java.util.ArrayList;
import java.util.List;
import static emu.grasscutter.utils.Language.translate;
@Command(label = "join", usage = "join [AvatarIDs] such as\"join 10000038 10000039\"",
description = "commands.join.description", permission = "player.join")
public class JoinCommand implements CommandHandler {
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
List<Integer> avatarIds = new ArrayList<>();
for (String arg : args) {
try {
int avatarId = Integer.parseInt(arg);
avatarIds.add(avatarId);
} catch (Exception ignored) {
ignored.printStackTrace();
CommandHandler.sendMessage(sender, translate("commands.generic.invalid.avatarId"));
return;
}
}
for (int i = 0; i < args.size(); i++) {
Avatar avatar = sender.getAvatars().getAvatarById(avatarIds.get(i));
if (avatar == null || sender.getTeamManager().getCurrentTeamInfo().contains(avatar)) {
CommandHandler.sendMessage(sender, translate("commands.generic.invalid.avatarId"));
return;
}
sender.getTeamManager().getCurrentTeamInfo().addAvatar(avatar);
}
// Packet
sender.getTeamManager().updateTeamEntities(new PacketChangeMpTeamAvatarRsp(sender, sender.getTeamManager().getCurrentTeamInfo()));
}
}

View File

@ -0,0 +1,66 @@
package emu.grasscutter.command.commands;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.quest.GameQuest;
import java.util.List;
import static emu.grasscutter.utils.Language.translate;
@Command(label = "quest", usage = "quest <add|finish> [quest id]", permission = "player.quest", permissionTargeted = "player.quest.others", description = "commands.quest.description")
public final class QuestCommand implements CommandHandler {
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
if (targetPlayer == null) {
CommandHandler.sendMessage(sender, translate(sender, "commands.execution.need_target"));
return;
}
if (args.size() != 2) {
CommandHandler.sendMessage(sender, translate(sender, "commands.quest.usage"));
return;
}
String cmd = args.get(0).toLowerCase();
int questId;
try {
questId = Integer.parseInt(args.get(1));
} catch (Exception e) {
CommandHandler.sendMessage(sender, translate(sender, "commands.quest.invalid_id"));
return;
}
switch (cmd) {
case "add" -> {
GameQuest quest = targetPlayer.getQuestManager().addQuest(questId);
if (quest != null) {
CommandHandler.sendMessage(sender, translate(sender, "commands.quest.added", questId));
return;
}
CommandHandler.sendMessage(sender, translate(sender, "commands.quest.not_found"));
}
case "finish" -> {
GameQuest quest = targetPlayer.getQuestManager().getQuestById(questId);
if (quest == null) {
CommandHandler.sendMessage(sender, translate(sender, "commands.quest.not_found"));
return;
}
quest.finish();
CommandHandler.sendMessage(sender, translate(sender, "commands.quest.finished", questId));
}
default -> {
CommandHandler.sendMessage(sender, translate(sender, "commands.quest.usage"));
}
}
}
}

View File

@ -21,7 +21,6 @@ public final class ReloadCommand implements CommandHandler {
Grasscutter.getGameServer().getGachaManager().load();
Grasscutter.getGameServer().getDropManager().load();
Grasscutter.getGameServer().getShopManager().load();
// Grasscutter.getHttpServer().loadQueries(); // Is this practical?
CommandHandler.sendMessage(sender, translate(sender, "commands.reload.reload_done"));
}

View File

@ -0,0 +1,43 @@
package emu.grasscutter.command.commands;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.server.packet.send.PacketChangeMpTeamAvatarRsp;
import java.util.ArrayList;
import java.util.List;
import static emu.grasscutter.utils.Language.translate;
@Command(label = "remove", usage = "remove [indexOfYourTeams] index start from 1",
description = "commands.remove.description", permission = "player.remove")
public class RemoveCommand implements CommandHandler {
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
List<Integer> avatarIds = new ArrayList<>();
for (String arg : args) {
try {
int avatarId = Integer.parseInt(arg);
avatarIds.add(avatarId);
} catch (Exception ignored) {
ignored.printStackTrace();
CommandHandler.sendMessage(sender, translate("commands.remove.invalid_index"));
return;
}
}
for (int i = 0; i < avatarIds.size(); i++) {
if (avatarIds.get(i) > sender.getTeamManager().getCurrentTeamInfo().getAvatars().size() || avatarIds.get(i) <= 0) {
CommandHandler.sendMessage(sender, translate("commands.remove.invalid_index"));
return;
}
sender.getTeamManager().getCurrentTeamInfo().removeAvatar(avatarIds.get(i) - 1);
}
// Packet
sender.getTeamManager().updateTeamEntities(new PacketChangeMpTeamAvatarRsp(sender, sender.getTeamManager().getCurrentTeamInfo()));
}
}

View File

@ -9,9 +9,9 @@ import java.util.Map;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.utils.Utils;
import emu.grasscutter.data.custom.AbilityEmbryoEntry;
import emu.grasscutter.data.custom.AbilityModifier;
import emu.grasscutter.data.custom.AbilityModifierEntry;
import emu.grasscutter.data.custom.OpenConfigEntry;
import emu.grasscutter.data.custom.MainQuestData;
import emu.grasscutter.data.custom.ScenePointEntry;
import emu.grasscutter.data.def.*;
import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap;
@ -27,6 +27,7 @@ public class GameData {
private static final Map<String, AbilityModifierEntry> abilityModifiers = new HashMap<>();
private static final Map<String, OpenConfigEntry> openConfigEntries = new HashMap<>();
private static final Map<String, ScenePointEntry> scenePointEntries = new HashMap<>();
private static final Int2ObjectMap<MainQuestData> mainQuestData = new Int2ObjectOpenHashMap<>();
// ExcelConfigs
private static final Int2ObjectMap<PlayerLevelData> playerLevelDataMap = new Int2ObjectOpenHashMap<>();
@ -63,11 +64,14 @@ public class GameData {
private static final Int2ObjectMap<SceneData> sceneDataMap = new Int2ObjectLinkedOpenHashMap<>();
private static final Int2ObjectMap<FetterData> fetterDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<CodexQuest> codexQuestMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<CodexQuest> codexQuestIdMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<FetterCharacterCardData> fetterCharacterCardDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<RewardData> rewardDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<WorldLevelData> worldLevelDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<DailyDungeonData> dailyDungeonDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<DungeonData> dungeonDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<QuestData> questDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<ShopGoodsData> shopGoodsDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<CombineData> combineDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<RewardPreviewData> rewardPreviewDataMap = new Int2ObjectOpenHashMap<>();
@ -122,6 +126,10 @@ public class GameData {
return getScenePointEntries().get(sceneId + "_" + pointId);
}
public static Int2ObjectMap<MainQuestData> getMainQuestDataMap() {
return mainQuestData;
}
public static Int2ObjectMap<AvatarData> getAvatarDataMap() {
return avatarDataMap;
}
@ -286,6 +294,10 @@ public class GameData {
return fetters;
}
public static Int2ObjectMap<CodexQuest> getCodexQuestMap(){return codexQuestMap;}
public static Int2ObjectMap<CodexQuest> getCodexQuestIdMap(){return codexQuestIdMap;}
public static Int2ObjectMap<WorldLevelData> getWorldLevelDataMap() {
return worldLevelDataMap;
}
@ -331,4 +343,8 @@ public class GameData {
public static Int2ObjectMap<TowerScheduleData> getTowerScheduleDataMap(){
return towerScheduleDataMap;
}
public static Int2ObjectMap<QuestData> getQuestDataMap() {
return questDataMap;
}
}

View File

@ -24,6 +24,7 @@ import emu.grasscutter.data.custom.AbilityModifier.AbilityModifierAction;
import emu.grasscutter.data.custom.AbilityModifier.AbilityModifierActionType;
import emu.grasscutter.data.custom.AbilityModifierEntry;
import emu.grasscutter.data.custom.OpenConfigEntry;
import emu.grasscutter.data.custom.MainQuestData;
import emu.grasscutter.data.custom.ScenePointEntry;
import emu.grasscutter.game.world.SpawnDataEntry.*;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
@ -58,8 +59,9 @@ public class ResourceLoader {
loadResources();
// Process into depots
GameDepot.load();
// Load spawn data
// Load spawn data and quests
loadSpawnData();
loadQuests();
// Load scene points - must be done AFTER resources are loaded
loadScenePoints();
// Custom - TODO move this somewhere else
@ -395,6 +397,29 @@ public class ResourceLoader {
}
}
private static void loadQuests() {
File folder = new File(RESOURCE("BinOutput/Quest/"));
if (!folder.exists()) {
return;
}
for (File file : folder.listFiles()) {
MainQuestData mainQuest = null;
try (FileReader fileReader = new FileReader(file)) {
mainQuest = Grasscutter.getGsonFactory().fromJson(fileReader, MainQuestData.class);
} catch (Exception e) {
e.printStackTrace();
continue;
}
GameData.getMainQuestDataMap().put(mainQuest.getId(), mainQuest);
}
Grasscutter.getLogger().info("Loaded " + GameData.getMainQuestDataMap().size() + " MainQuestDatas.");
}
// BinOutput configs
private static class AvatarConfig {

View File

@ -0,0 +1,53 @@
package emu.grasscutter.data.custom;
import emu.grasscutter.game.quest.enums.LogicType;
import emu.grasscutter.game.quest.enums.QuestTrigger;
import emu.grasscutter.game.quest.enums.QuestType;
public class MainQuestData {
private int id;
private int series;
private QuestType type;
private long titleTextMapHash;
private int[] suggestTrackMainQuestList;
private int[] rewardIdList;
private SubQuestData[] subQuests;
public int getId() {
return id;
}
public int getSeries() {
return series;
}
public QuestType getType() {
return type;
}
public long getTitleTextMapHash() {
return titleTextMapHash;
}
public int[] getSuggestTrackMainQuestList() {
return suggestTrackMainQuestList;
}
public int[] getRewardIdList() {
return rewardIdList;
}
public SubQuestData[] getSubQuests() {
return subQuests;
}
public static class SubQuestData {
private int subId;
public int getSubId() {
return subId;
}
}
}

Some files were not shown because too many files have changed in this diff Show More